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 ma...@apache.org on 2018/06/13 14:58:17 UTC

[1/5] james-project git commit: JAMES-2422 Test support for env variables in configuration files

Repository: james-project
Updated Branches:
  refs/heads/master 175f1f29c -> 946c68bea


JAMES-2422 Test support for env variables in configuration files


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/1694051f
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/1694051f
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/1694051f

Branch: refs/heads/master
Commit: 1694051f6c40801cdb09d26bb6cb0b10a3e22e58
Parents: 175f1f2
Author: Raphael Ouazana <ra...@linagora.com>
Authored: Tue Jun 12 10:04:05 2018 +0200
Committer: Matthieu Baechler <ma...@apache.org>
Committed: Wed Jun 13 16:56:17 2018 +0200

----------------------------------------------------------------------
 server/container/guice/guice-common/pom.xml     |  6 +++++
 .../utils/FileConfigurationProviderTest.java    | 25 +++++++++++++++++++-
 .../guice-common/src/test/resources/test.xml    |  4 +++-
 3 files changed, 33 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/1694051f/server/container/guice/guice-common/pom.xml
----------------------------------------------------------------------
diff --git a/server/container/guice/guice-common/pom.xml b/server/container/guice/guice-common/pom.xml
index 2c0be52..9f03660 100644
--- a/server/container/guice/guice-common/pom.xml
+++ b/server/container/guice/guice-common/pom.xml
@@ -153,6 +153,12 @@
             <artifactId>metrics-logger</artifactId>
         </dependency>
         <dependency>
+            <groupId>com.github.stefanbirkner</groupId>
+            <artifactId>system-rules</artifactId>
+            <version>1.18.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>com.github.steveash.guavate</groupId>
             <artifactId>guavate</artifactId>
         </dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/1694051f/server/container/guice/guice-common/src/test/java/org/apache/james/utils/FileConfigurationProviderTest.java
----------------------------------------------------------------------
diff --git a/server/container/guice/guice-common/src/test/java/org/apache/james/utils/FileConfigurationProviderTest.java b/server/container/guice/guice-common/src/test/java/org/apache/james/utils/FileConfigurationProviderTest.java
index 6120ff7..74678ec 100644
--- a/server/container/guice/guice-common/src/test/java/org/apache/james/utils/FileConfigurationProviderTest.java
+++ b/server/container/guice/guice-common/src/test/java/org/apache/james/utils/FileConfigurationProviderTest.java
@@ -26,7 +26,9 @@ import org.apache.james.server.core.configuration.Configuration;
 import org.apache.james.server.core.configuration.FileConfigurationProvider;
 import org.apache.james.server.core.filesystem.FileSystemImpl;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.contrib.java.lang.system.EnvironmentVariables;
 
 public class FileConfigurationProviderTest {
 
@@ -34,17 +36,26 @@ public class FileConfigurationProviderTest {
     private static final String CONFIG_KEY_2 = "property";
     private static final String CONFIG_KEY_4 = "james";
     private static final String CONFIG_KEY_5 = "internal";
+    private static final String CONFIG_KEY_ENV = "env";
+    private static final String CONFIG_KEY_NOT_ENV = "notEnv";
     private static final String VALUE_1 = "0";
     private static final String VALUE_2 = "awesome";
     private static final String VALUE_3 = "james";
+    private static final String VALUE_NOT_ENV = "${env:MY_NOT_IN_ENV_VAR}";
+    private static final String ENVIRONMENT_SET_VALUE = "testvalue";
     private static final String FAKE_CONFIG_KEY = "fake";
     private static final String ROOT_CONFIG_KEY = "test";
     private static final String CONFIG_SEPARATOR = ".";
 
     private FileConfigurationProvider configurationProvider;
 
+    @Rule
+    public final EnvironmentVariables environmentVariables = new EnvironmentVariables();
+
     @Before
     public void setUp() {
+        environmentVariables.set("MY_ENV_VAR", ENVIRONMENT_SET_VALUE);
+        environmentVariables.clear("MY_NOT_IN_ENV_VAR");
         Configuration configuration = Configuration.builder()
             .workingDirectory("../")
             .configurationFromClasspath()
@@ -78,7 +89,8 @@ public class FileConfigurationProviderTest {
         HierarchicalConfiguration hierarchicalConfiguration = configurationProvider.getConfiguration(ROOT_CONFIG_KEY);
         assertThat(hierarchicalConfiguration.getKeys()).containsOnly(CONFIG_KEY_1,
                 String.join(CONFIG_SEPARATOR, CONFIG_KEY_4, CONFIG_KEY_2),
-                String.join(CONFIG_SEPARATOR, CONFIG_KEY_4, CONFIG_KEY_5, CONFIG_KEY_2));
+                String.join(CONFIG_SEPARATOR, CONFIG_KEY_4, CONFIG_KEY_5, CONFIG_KEY_2),
+                CONFIG_KEY_ENV, CONFIG_KEY_NOT_ENV);
         assertThat(hierarchicalConfiguration.getProperty(CONFIG_KEY_1)).isEqualTo(VALUE_1);
     }
 
@@ -118,4 +130,15 @@ public class FileConfigurationProviderTest {
         configurationProvider.getConfiguration(String.join(CONFIG_SEPARATOR, ROOT_CONFIG_KEY, FAKE_CONFIG_KEY));
     }
 
+    @Test
+    public void getConfigurationShouldNotReplaceEnvironmentVariableWhenNotSet() throws Exception {
+        HierarchicalConfiguration hierarchicalConfiguration = configurationProvider.getConfiguration(ROOT_CONFIG_KEY);
+        assertThat(hierarchicalConfiguration.getString(CONFIG_KEY_NOT_ENV)).isEqualTo(VALUE_NOT_ENV);
+    }
+
+    @Test
+    public void getConfigurationShouldReplaceEnvironmentVariableWhenSet() throws Exception {
+        HierarchicalConfiguration hierarchicalConfiguration = configurationProvider.getConfiguration(ROOT_CONFIG_KEY);
+        assertThat(hierarchicalConfiguration.getString(CONFIG_KEY_ENV)).isEqualTo(ENVIRONMENT_SET_VALUE);
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/1694051f/server/container/guice/guice-common/src/test/resources/test.xml
----------------------------------------------------------------------
diff --git a/server/container/guice/guice-common/src/test/resources/test.xml b/server/container/guice/guice-common/src/test/resources/test.xml
index 8d89ecc..d34c3c4 100644
--- a/server/container/guice/guice-common/src/test/resources/test.xml
+++ b/server/container/guice/guice-common/src/test/resources/test.xml
@@ -25,4 +25,6 @@
           <property>james</property>
         </internal>
     </james>
-</test>
\ No newline at end of file
+    <env>${env:MY_ENV_VAR}</env>
+    <notEnv>${env:MY_NOT_IN_ENV_VAR}</notEnv>
+</test>


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


[5/5] james-project git commit: JAMES-2418 Strong typing for repository URLs and keys

Posted by ma...@apache.org.
JAMES-2418 Strong typing for repository URLs and keys


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/946c68be
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/946c68be
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/946c68be

Branch: refs/heads/master
Commit: 946c68bea7d8c6f29ea51566436bf4190fa6480f
Parents: 7dfda86
Author: benwa <bt...@linagora.com>
Authored: Fri Jun 8 11:03:43 2018 +0700
Committer: Matthieu Baechler <ma...@apache.org>
Committed: Wed Jun 13 16:57:07 2018 +0200

----------------------------------------------------------------------
 .../data/CassandraMailRepositoryModule.java     |   3 +-
 .../CassandraMailRepositoryIntegrationTest.java |   5 +-
 .../server/MailStoreRepositoryModule.java       |   3 +-
 .../utils/InMemoryMailRepositoryStore.java      |  72 ++++------
 .../james/utils/MailRepositoryProbeImpl.java    |  13 +-
 .../james/utils/MailRepositoryProvider.java     |   3 +-
 .../utils/InMemoryMailRepositoryStoreTest.java  |  51 +++----
 .../MailRepositoryStoreBeanFactory.java         |  47 +++----
 .../container/spring/tool/James23Importer.java  |   6 +-
 .../mailrepository/api/MailRepositoryStore.java |   8 +-
 .../mailrepository/file/FileMailRepository.java |  25 ++--
 .../mailrepository/file/MBoxMailRepository.java |  27 ++--
 .../mailrepository/MBoxMailRepositoryTest.java  |   3 +-
 .../mailrepository/jcr/JCRMailRepository.java   |  15 +-
 .../mailrepository/jdbc/JDBCMailRepository.java |  23 +--
 .../lib/AbstractMailRepository.java             |  21 +--
 .../mock/MockMailRepositoryStore.java           |  11 +-
 .../apache/james/mailets/MailetErrorsTest.java  |  35 ++---
 .../mailets/NetworkMatcherIntegrationTest.java  |   5 +-
 .../james/mailets/SmtpAuthIntegrationTest.java  |   5 +-
 .../mailets/configuration/CommonProcessors.java |   5 +-
 .../transport/mailets/GroupMappingTest.java     |   5 +-
 .../transport/mailets/ToRepositoryTest.java     |  17 ++-
 .../mailets/ToSenderDomainRepositoryTest.java   |  32 +++--
 .../james/transport/mailets/FromRepository.java |  14 +-
 .../james/transport/mailets/ToRepository.java   |   5 +-
 .../mailets/ToSenderDomainRepository.java       |   7 +-
 .../transport/mailets/ToRepositoryTest.java     |  37 +++--
 .../mailets/ToSenderDomainRepositoryTest.java   |   7 +-
 .../mailrepository/mailrepository-api/pom.xml   |  11 ++
 .../james/mailrepository/api/MailKey.java       |  66 +++++++++
 .../mailrepository/api/MailRepository.java      |  12 +-
 .../mailrepository/api/MailRepositoryUrl.java   |  86 ++++++++++++
 .../james/mailrepository/api/Protocol.java      |  60 ++++++++
 .../mailrepository/MailRepositoryContract.java  | 140 +++++++++----------
 .../james/mailrepository/api/MailKeyTest.java   |  54 +++++++
 .../api/MailRepositoryUrlTest.java              |  71 ++++++++++
 .../james/mailrepository/api/ProtocolTest.java  |  42 ++++++
 .../cassandra/CassandraMailRepository.java      |  28 ++--
 .../CassandraMailRepositoryCountDAO.java        |  13 +-
 .../CassandraMailRepositoryKeysDAO.java         |  20 +--
 .../CassandraMailRepositoryMailDAO.java         |  18 +--
 .../CassandraMailRepositoryCountDAOTest.java    |   5 +-
 .../CassandraMailRepositoryKeysDAOTest.java     |  13 +-
 .../CassandraMailRepositoryMailDAOTest.java     |  16 ++-
 .../cassandra/CassandraMailRepositoryTest.java  |   3 +-
 .../memory/MemoryMailRepository.java            |  23 +--
 .../dto/ExtendedMailRepositoryResponse.java     |   4 +-
 .../org/apache/james/webadmin/dto/MailKey.java  |  50 -------
 .../apache/james/webadmin/dto/MailKeyDTO.java   |  52 +++++++
 .../webadmin/dto/MailRepositoryResponse.java    |  12 +-
 .../webadmin/routes/MailRepositoriesRoutes.java |  46 +++---
 .../service/ClearMailRepositoryTask.java        |   9 +-
 .../service/MailRepositoryStoreService.java     |  26 ++--
 .../service/ReprocessingAllMailsTask.java       |  14 +-
 .../service/ReprocessingOneMailTask.java        |  18 +--
 .../webadmin/service/ReprocessingService.java   |   8 +-
 .../routes/MailRepositoriesRoutesTest.java      |  32 +++--
 .../service/MailRepositoryStoreServiceTest.java |  32 +++--
 59 files changed, 966 insertions(+), 528 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/data/CassandraMailRepositoryModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/data/CassandraMailRepositoryModule.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/data/CassandraMailRepositoryModule.java
index f307440..dc4a7c5 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/data/CassandraMailRepositoryModule.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/data/CassandraMailRepositoryModule.java
@@ -22,6 +22,7 @@ package org.apache.james.modules.data;
 import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.blob.api.ObjectStore;
 import org.apache.james.mailrepository.api.MailRepository;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.mailrepository.cassandra.CassandraMailRepository;
 import org.apache.james.mailrepository.cassandra.CassandraMailRepositoryCountDAO;
 import org.apache.james.mailrepository.cassandra.CassandraMailRepositoryKeysDAO;
@@ -67,7 +68,7 @@ public class CassandraMailRepositoryModule extends AbstractModule {
         }
 
         @Override
-        public MailRepository provide(String url) {
+        public MailRepository provide(MailRepositoryUrl url) {
             return new CassandraMailRepository(url, keysDAO, countDAO, mailDAO, objectStore);
         }
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/container/guice/cassandra-guice/src/test/java/org/apache/james/CassandraMailRepositoryIntegrationTest.java
----------------------------------------------------------------------
diff --git a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/CassandraMailRepositoryIntegrationTest.java b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/CassandraMailRepositoryIntegrationTest.java
index e694ec9..cf2cbdb 100644
--- a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/CassandraMailRepositoryIntegrationTest.java
+++ b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/CassandraMailRepositoryIntegrationTest.java
@@ -21,6 +21,7 @@ package org.apache.james;
 
 import static com.jayway.awaitility.Duration.FIVE_HUNDRED_MILLISECONDS;
 
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.utils.DataProbeImpl;
 import org.apache.james.utils.MailRepositoryProbeImpl;
 import org.apache.james.utils.SMTPMessageSender;
@@ -35,6 +36,8 @@ import com.jayway.awaitility.core.ConditionFactory;
 
 public class CassandraMailRepositoryIntegrationTest {
 
+    private static final MailRepositoryUrl SENDER_DENIED_URL = MailRepositoryUrl.from("cassandra://var/mail/sender-denied/");
+
     @ClassRule
     public static DockerCassandraRule cassandra = new DockerCassandraRule();
     
@@ -74,7 +77,7 @@ public class CassandraMailRepositoryIntegrationTest {
             .sendMessage("denied@other.com", "user@domain.com");
 
         MailRepositoryProbeImpl repositoryProbe = server.getProbe(MailRepositoryProbeImpl.class);
-        await.until(() -> repositoryProbe.getRepositoryMailCount("cassandra://var/mail/sender-denied/") == 1);
+        await.until(() -> repositoryProbe.getRepositoryMailCount(SENDER_DENIED_URL) == 1);
     }
 
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/container/guice/guice-common/src/main/java/org/apache/james/modules/server/MailStoreRepositoryModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/modules/server/MailStoreRepositoryModule.java b/server/container/guice/guice-common/src/main/java/org/apache/james/modules/server/MailStoreRepositoryModule.java
index a7e9eda..0365beb 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/modules/server/MailStoreRepositoryModule.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/modules/server/MailStoreRepositoryModule.java
@@ -25,6 +25,7 @@ import org.apache.james.filesystem.api.FileSystem;
 import org.apache.james.lifecycle.api.Configurable;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.mailrepository.file.FileMailRepository;
 import org.apache.james.server.core.configuration.ConfigurationProvider;
 import org.apache.james.utils.ConfigurationPerformer;
@@ -69,7 +70,7 @@ public class MailStoreRepositoryModule extends AbstractModule {
         }
 
         @Override
-        public MailRepository provide(String url) {
+        public MailRepository provide(MailRepositoryUrl url) {
             FileMailRepository fileMailRepository = new FileMailRepository();
             fileMailRepository.setFileSystem(fileSystem);
             return fileMailRepository;

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/container/guice/guice-common/src/main/java/org/apache/james/utils/InMemoryMailRepositoryStore.java
----------------------------------------------------------------------
diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/utils/InMemoryMailRepositoryStore.java b/server/container/guice/guice-common/src/main/java/org/apache/james/utils/InMemoryMailRepositoryStore.java
index 2c4fab7..0d87a8d 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/utils/InMemoryMailRepositoryStore.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/utils/InMemoryMailRepositoryStore.java
@@ -34,6 +34,8 @@ import org.apache.commons.configuration.HierarchicalConfiguration;
 import org.apache.james.lifecycle.api.Configurable;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
+import org.apache.james.mailrepository.api.Protocol;
 import org.apache.james.repository.api.Initializable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,36 +45,12 @@ import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 public class InMemoryMailRepositoryStore implements MailRepositoryStore, Configurable {
-
-    public static class Destination {
-
-        public static Destination fromUrl(String destinationUrl) throws MailRepositoryStoreException {
-            return new Destination(destinationUrl, retrieveProtocol(destinationUrl));
-        }
-
-        private static String retrieveProtocol(String destination) throws MailRepositoryStoreException {
-            int protocolSeparatorPosition = destination.indexOf(':');
-            if (protocolSeparatorPosition == -1) {
-                throw new MailRepositoryStoreException("Destination is malformed. Must be a valid URL: " + destination);
-            }
-            return destination.substring(0, protocolSeparatorPosition);
-        }
-
-        private final String url;
-        private final String protocol;
-
-        public Destination(String url, String protocol) {
-            this.url = url;
-            this.protocol = protocol;
-        }
-    }
-
     private static final Logger LOGGER = LoggerFactory.getLogger(InMemoryMailRepositoryStore.class);
 
     private final Set<MailRepositoryProvider> mailRepositories;
-    private final ConcurrentMap<String, MailRepository> destinationToRepositoryAssociations;
-    private final Map<String, MailRepositoryProvider> protocolToRepositoryProvider;
-    private final Map<String, HierarchicalConfiguration> perProtocolMailRepositoryDefaultConfiguration;
+    private final ConcurrentMap<MailRepositoryUrl, MailRepository> destinationToRepositoryAssociations;
+    private final Map<Protocol, MailRepositoryProvider> protocolToRepositoryProvider;
+    private final Map<Protocol, HierarchicalConfiguration> perProtocolMailRepositoryDefaultConfiguration;
     private HierarchicalConfiguration configuration;
 
     @Inject
@@ -84,7 +62,7 @@ public class InMemoryMailRepositoryStore implements MailRepositoryStore, Configu
     }
 
     @Override
-    public List<String> getUrls() {
+    public List<MailRepositoryUrl> getUrls() {
         return ImmutableList.copyOf(destinationToRepositoryAssociations.keySet());
     }
 
@@ -111,23 +89,22 @@ public class InMemoryMailRepositoryStore implements MailRepositoryStore, Configu
     }
 
     @Override
-    public Optional<MailRepository> get(String url) {
+    public Optional<MailRepository> get(MailRepositoryUrl url) {
         return Optional.ofNullable(destinationToRepositoryAssociations.get(url));
     }
 
     @Override
-    public MailRepository select(String destinationUrl) throws MailRepositoryStoreException {
-        Destination destination = Destination.fromUrl(destinationUrl);
-        return Optional.ofNullable(destinationToRepositoryAssociations.get(destination.url))
+    public MailRepository select(MailRepositoryUrl mailRepositoryUrl) {
+        return Optional.ofNullable(destinationToRepositoryAssociations.get(mailRepositoryUrl))
             .orElseGet(Throwing.supplier(
-                () -> createNewMailRepository(destination))
+                () -> createNewMailRepository(mailRepositoryUrl))
                 .sneakyThrow());
     }
 
-    private MailRepository createNewMailRepository(Destination destination) throws MailRepositoryStoreException {
-        MailRepository newMailRepository = retrieveMailRepository(destination);
-        newMailRepository = initializeNewRepository(newMailRepository, createRepositoryCombinedConfig(destination));
-        MailRepository previousRepository = destinationToRepositoryAssociations.putIfAbsent(destination.url, newMailRepository);
+    private MailRepository createNewMailRepository(MailRepositoryUrl mailRepositoryUrl) throws MailRepositoryStoreException {
+        MailRepository newMailRepository = retrieveMailRepository(mailRepositoryUrl);
+        newMailRepository = initializeNewRepository(newMailRepository, createRepositoryCombinedConfig(mailRepositoryUrl));
+        MailRepository previousRepository = destinationToRepositoryAssociations.putIfAbsent(mailRepositoryUrl, newMailRepository);
         return Optional.ofNullable(previousRepository)
             .orElse(newMailRepository);
     }
@@ -139,12 +116,12 @@ public class InMemoryMailRepositoryStore implements MailRepositoryStore, Configu
             .findAny()
             .orElseThrow(() -> new ConfigurationException("MailRepository " + className + " has not been registered"));
         for (String protocol : repositoryConfiguration.getStringArray("protocols.protocol")) {
-            protocolToRepositoryProvider.put(protocol, usedMailRepository);
-            registerRepositoryDefaultConfiguration(repositoryConfiguration, protocol);
+            protocolToRepositoryProvider.put(new Protocol(protocol), usedMailRepository);
+            registerRepositoryDefaultConfiguration(repositoryConfiguration, new Protocol(protocol));
         }
     }
 
-    private void registerRepositoryDefaultConfiguration(HierarchicalConfiguration repositoryConfiguration, String protocol) {
+    private void registerRepositoryDefaultConfiguration(HierarchicalConfiguration repositoryConfiguration, Protocol protocol) {
         HierarchicalConfiguration defConf = null;
         if (repositoryConfiguration.getKeys("config").hasNext()) {
             defConf = repositoryConfiguration.configurationAt("config");
@@ -154,14 +131,14 @@ public class InMemoryMailRepositoryStore implements MailRepositoryStore, Configu
         }
     }
 
-    private CombinedConfiguration createRepositoryCombinedConfig(Destination destination) {
+    private CombinedConfiguration createRepositoryCombinedConfig(MailRepositoryUrl mailRepositoryUrl) {
         CombinedConfiguration config = new CombinedConfiguration();
-        HierarchicalConfiguration defaultProtocolConfig = perProtocolMailRepositoryDefaultConfiguration.get(destination.protocol);
+        HierarchicalConfiguration defaultProtocolConfig = perProtocolMailRepositoryDefaultConfiguration.get(mailRepositoryUrl.getProtocol());
         if (defaultProtocolConfig != null) {
             config.addConfiguration(defaultProtocolConfig);
         }
         DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
-        builder.addProperty("[@destinationURL]", destination.url);
+        builder.addProperty("[@destinationURL]", mailRepositoryUrl.asString());
         config.addConfiguration(builder);
         return config;
     }
@@ -180,10 +157,11 @@ public class InMemoryMailRepositoryStore implements MailRepositoryStore, Configu
         }
     }
 
-    private MailRepository retrieveMailRepository(Destination destination) throws MailRepositoryStoreException {
-        return Optional.ofNullable(protocolToRepositoryProvider.get(destination.protocol))
-            .orElseThrow(() -> new MailRepositoryStoreException("No Mail Repository associated with " + destination.protocol))
-            .provide(destination.url);
+    private MailRepository retrieveMailRepository(MailRepositoryUrl mailRepositoryUrl) throws MailRepositoryStoreException {
+        Protocol protocol = mailRepositoryUrl.getProtocol();
+        return Optional.ofNullable(protocolToRepositoryProvider.get(protocol))
+            .orElseThrow(() -> new MailRepositoryStoreException("No Mail Repository associated with " + protocol.getValue()))
+            .provide(mailRepositoryUrl);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/container/guice/guice-common/src/main/java/org/apache/james/utils/MailRepositoryProbeImpl.java
----------------------------------------------------------------------
diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/utils/MailRepositoryProbeImpl.java b/server/container/guice/guice-common/src/main/java/org/apache/james/utils/MailRepositoryProbeImpl.java
index f49fedd..71f7dec 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/utils/MailRepositoryProbeImpl.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/utils/MailRepositoryProbeImpl.java
@@ -23,7 +23,9 @@ import java.util.List;
 
 import javax.inject.Inject;
 
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 
 import com.google.common.collect.ImmutableList;
 
@@ -39,23 +41,22 @@ public class MailRepositoryProbeImpl implements GuiceProbe {
     /**
      * Get the count of email currently stored in a given repository
      */
-    public long getRepositoryMailCount(String url) throws Exception {
+    public long getRepositoryMailCount(MailRepositoryUrl url) throws Exception {
         return repositoryStore.select(url).size();
     }
 
-    public void createRepository(String url) throws Exception {
+    public void createRepository(MailRepositoryUrl url) throws Exception {
         repositoryStore.select(url);
     }
 
-    public List<String> listMailKeys(String url) throws Exception {
+    public List<MailKey> listMailKeys(MailRepositoryUrl url) throws Exception {
         return ImmutableList.copyOf(
             repositoryStore.select(url)
                 .list());
     }
 
-    public List<String> listRepositoryUrls() {
-        return ImmutableList.copyOf(
-            repositoryStore.getUrls());
+    public List<MailRepositoryUrl> listRepositoryUrls() {
+        return ImmutableList.copyOf(repositoryStore.getUrls());
     }
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/container/guice/guice-common/src/main/java/org/apache/james/utils/MailRepositoryProvider.java
----------------------------------------------------------------------
diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/utils/MailRepositoryProvider.java b/server/container/guice/guice-common/src/main/java/org/apache/james/utils/MailRepositoryProvider.java
index e14bb5e..2338616 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/utils/MailRepositoryProvider.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/utils/MailRepositoryProvider.java
@@ -20,10 +20,11 @@
 package org.apache.james.utils;
 
 import org.apache.james.mailrepository.api.MailRepository;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 
 public interface MailRepositoryProvider {
 
     String canonicalName();
 
-    MailRepository provide(String url);
+    MailRepository provide(MailRepositoryUrl url);
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/container/guice/guice-common/src/test/java/org/apache/james/utils/InMemoryMailRepositoryStoreTest.java
----------------------------------------------------------------------
diff --git a/server/container/guice/guice-common/src/test/java/org/apache/james/utils/InMemoryMailRepositoryStoreTest.java b/server/container/guice/guice-common/src/test/java/org/apache/james/utils/InMemoryMailRepositoryStoreTest.java
index 3321933..cb1e18a 100644
--- a/server/container/guice/guice-common/src/test/java/org/apache/james/utils/InMemoryMailRepositoryStoreTest.java
+++ b/server/container/guice/guice-common/src/test/java/org/apache/james/utils/InMemoryMailRepositoryStoreTest.java
@@ -29,6 +29,7 @@ import org.apache.commons.configuration.HierarchicalConfiguration;
 import org.apache.james.core.builder.MimeMessageBuilder;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.mailrepository.file.FileMailRepository;
 import org.apache.james.mailrepository.memory.MemoryMailRepository;
 import org.apache.james.modules.server.MailStoreRepositoryModule;
@@ -43,6 +44,7 @@ import org.junit.Test;
 import com.google.common.collect.Sets;
 
 public class InMemoryMailRepositoryStoreTest {
+    private static final MailRepositoryUrl FILE_REPO = MailRepositoryUrl.from("file://repo");
 
     private static class MemoryMailRepositoryProvider implements MailRepositoryProvider {
         @Override
@@ -51,7 +53,7 @@ public class InMemoryMailRepositoryStoreTest {
         }
 
         @Override
-        public MailRepository provide(String url) {
+        public MailRepository provide(MailRepositoryUrl url) {
             return new MemoryMailRepository();
         }
     }
@@ -77,23 +79,26 @@ public class InMemoryMailRepositoryStoreTest {
     }
 
     @Test(expected = MailRepositoryStore.MailRepositoryStoreException.class)
-    public void selectingANonRegisteredProtocolShouldFail() throws Exception {
-        repositoryStore.select("proto://repo");
+    public void selectingANonRegisteredProtocolShouldFail() {
+        repositoryStore.select(MailRepositoryUrl.from("proto://repo"));
     }
 
     @Test
-    public void selectingARegisteredProtocolShouldWork() throws Exception {
-        assertThat(repositoryStore.select("file://repo")).isInstanceOf(FileMailRepository.class);
+    public void selectingARegisteredProtocolShouldWork() {
+        assertThat(repositoryStore.select(FILE_REPO))
+            .isInstanceOf(FileMailRepository.class);
     }
 
     @Test
-    public void selectingTwiceARegisteredProtocolWithSameDestinationShouldReturnTheSameResult() throws Exception {
-        assertThat(repositoryStore.select("file://repo")).isEqualTo(repositoryStore.select("file://repo"));
+    public void selectingTwiceARegisteredProtocolWithSameDestinationShouldReturnTheSameResult() {
+        assertThat(repositoryStore.select(FILE_REPO))
+            .isEqualTo(repositoryStore.select(FILE_REPO));
     }
 
     @Test
-    public void selectingTwiceARegisteredProtocolWithDifferentDestinationShouldReturnDifferentResults() throws Exception {
-        assertThat(repositoryStore.select("file://repo")).isNotEqualTo(repositoryStore.select("file://repo1"));
+    public void selectingTwiceARegisteredProtocolWithDifferentDestinationShouldReturnDifferentResults() {
+        assertThat(repositoryStore.select(FILE_REPO))
+            .isNotEqualTo(repositoryStore.select(MailRepositoryUrl.from("file://repo1")));
     }
 
     @Test
@@ -123,10 +128,10 @@ public class InMemoryMailRepositoryStoreTest {
     }
 
     @Test
-    public void getUrlsShouldReturnUsedUrls() throws Exception {
-        String url1 = "file://repo1";
-        String url2 = "file://repo2";
-        String url3 = "file://repo3";
+    public void getUrlsShouldReturnUsedUrls() {
+        MailRepositoryUrl url1 = MailRepositoryUrl.from("file://repo1");
+        MailRepositoryUrl url2 = MailRepositoryUrl.from("file://repo2");
+        MailRepositoryUrl url3 = MailRepositoryUrl.from("file://repo3");
         repositoryStore.select(url1);
         repositoryStore.select(url2);
         repositoryStore.select(url3);
@@ -134,31 +139,29 @@ public class InMemoryMailRepositoryStoreTest {
     }
 
     @Test
-    public void getUrlsResultsShouldNotBeDuplicated() throws Exception {
-        String url1 = "file://repo1";
-        repositoryStore.select(url1);
-        repositoryStore.select(url1);
-        assertThat(repositoryStore.getUrls()).containsExactly(url1);
+    public void getUrlsResultsShouldNotBeDuplicated() {
+        repositoryStore.select(FILE_REPO);
+        repositoryStore.select(FILE_REPO);
+        assertThat(repositoryStore.getUrls()).containsExactly(FILE_REPO);
     }
 
     @Test
     public void getShouldReturnEmptyWhenUrlNotInUse() {
-        assertThat(repositoryStore.get("file://repo"))
+        assertThat(repositoryStore.get(FILE_REPO))
             .isEmpty();
     }
 
     @Test
-    public void getShouldReturnPreviouslyCreatedMailRepository() throws Exception {
-        String url = "file://repo";
-        MailRepository mailRepository = repositoryStore.select(url);
+    public void getShouldReturnPreviouslyCreatedMailRepository() {
+        MailRepository mailRepository = repositoryStore.select(FILE_REPO);
 
-        assertThat(repositoryStore.get(url))
+        assertThat(repositoryStore.get(FILE_REPO))
             .contains(mailRepository);
     }
 
     @Test
     public void selectShouldNotReturnDifferentResultsWhenUsedInAConcurrentEnvironment() throws Exception {
-        String url = "memory://repo";
+        MailRepositoryUrl url = MailRepositoryUrl.from("memory://repo");
         int threadCount = 10;
         int operationCount = 1;
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/container/spring/src/main/java/org/apache/james/container/spring/bean/factory/mailrepositorystore/MailRepositoryStoreBeanFactory.java
----------------------------------------------------------------------
diff --git a/server/container/spring/src/main/java/org/apache/james/container/spring/bean/factory/mailrepositorystore/MailRepositoryStoreBeanFactory.java b/server/container/spring/src/main/java/org/apache/james/container/spring/bean/factory/mailrepositorystore/MailRepositoryStoreBeanFactory.java
index 2004442..b83af66 100644
--- a/server/container/spring/src/main/java/org/apache/james/container/spring/bean/factory/mailrepositorystore/MailRepositoryStoreBeanFactory.java
+++ b/server/container/spring/src/main/java/org/apache/james/container/spring/bean/factory/mailrepositorystore/MailRepositoryStoreBeanFactory.java
@@ -35,6 +35,8 @@ import org.apache.james.container.spring.bean.factory.AbstractBeanFactory;
 import org.apache.james.lifecycle.api.Configurable;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
+import org.apache.james.mailrepository.api.Protocol;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -49,17 +51,17 @@ public class MailRepositoryStoreBeanFactory extends AbstractBeanFactory implemen
     /**
      * Map of [destinationURL + type]->Repository
      */
-    private Map<String, MailRepository> repositories;
+    private Map<MailRepositoryUrl, MailRepository> repositories;
 
     /**
      * Map of [protocol(destinationURL) + type ]->classname of repository;
      */
-    private Map<String, String> classes;
+    private Map<Protocol, String> classes;
 
     /**
      * Map of [protocol(destinationURL) + type ]->default config for repository.
      */
-    private Map<String, HierarchicalConfiguration> defaultConfigs;
+    private Map<Protocol, HierarchicalConfiguration> defaultConfigs;
 
     /**
      * The configuration used by the instance
@@ -67,7 +69,7 @@ public class MailRepositoryStoreBeanFactory extends AbstractBeanFactory implemen
     private HierarchicalConfiguration configuration;
 
     @Override
-    public void configure(HierarchicalConfiguration configuration) throws ConfigurationException {
+    public void configure(HierarchicalConfiguration configuration) {
         this.configuration = configuration;
     }
 
@@ -88,7 +90,7 @@ public class MailRepositoryStoreBeanFactory extends AbstractBeanFactory implemen
     }
 
     @Override
-    public Optional<MailRepository> get(String url) throws MailRepositoryStoreException {
+    public Optional<MailRepository> get(MailRepositoryUrl url) {
         return Optional.ofNullable(repositories.get(url));
     }
 
@@ -121,14 +123,14 @@ public class MailRepositoryStoreBeanFactory extends AbstractBeanFactory implemen
 
             LOGGER.info("Registering Repository instance of class {} to handle {} protocol requests", className, protocol);
 
-            if (classes.get(protocol) != null) {
+            if (classes.get(new Protocol(protocol)) != null) {
                 throw new ConfigurationException("The combination of protocol and type comprise a unique key for repositories.  This constraint has been violated.  Please check your repository configuration.");
             }
 
-            classes.put(protocol, className);
+            classes.put(new Protocol(protocol), className);
 
             if (defConf != null) {
-                defaultConfigs.put(protocol, defConf);
+                defaultConfigs.put(new Protocol(protocol), defConf);
             }
         }
 
@@ -153,33 +155,26 @@ public class MailRepositoryStoreBeanFactory extends AbstractBeanFactory implemen
      */
     @Override
     @SuppressWarnings("deprecation")
-    public synchronized MailRepository select(String destination) throws MailRepositoryStoreException {
-        int idx = destination.indexOf(':');
-        if (idx == -1) {
-            throw new MailRepositoryStoreException("Destination is malformed. Must be a valid URL: " + destination);
-        }
-        String protocol = destination.substring(0, idx);
-
-        String repID = destination;
-        MailRepository reply = repositories.get(repID);
+    public synchronized MailRepository select(MailRepositoryUrl destination) throws MailRepositoryStoreException {
+        MailRepository reply = repositories.get(destination);
         if (reply != null) {
-            LOGGER.debug("obtained repository: {},{}", repID, reply.getClass());
+            LOGGER.debug("obtained repository: {},{}", destination, reply.getClass());
             return reply;
         } else {
-            String repClass = classes.get(protocol);
-            LOGGER.debug("obtained repository: {} to handle: {}", repClass, protocol);
+            String repClass = classes.get(destination.getProtocol());
+            LOGGER.debug("obtained repository: {} to handle: {}", repClass, destination.getProtocol().getValue());
 
             // If default values have been set, create a new repository
             // configuration element using the default values
             // and the values in the selector.
             // If no default values, just use the selector.
             final CombinedConfiguration config = new CombinedConfiguration();
-            HierarchicalConfiguration defConf = defaultConfigs.get(protocol);
+            HierarchicalConfiguration defConf = defaultConfigs.get(destination.getProtocol());
             if (defConf != null) {
                 config.addConfiguration(defConf);
             }
             DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
-            builder.addProperty("[@destinationURL]", destination);
+            builder.addProperty("[@destinationURL]", destination.asString());
             config.addConfiguration(builder);
 
             try {
@@ -192,10 +187,10 @@ public class MailRepositoryStoreBeanFactory extends AbstractBeanFactory implemen
                     ((Configurable) reply).configure(config);
                 }
 
-                reply = (MailRepository) getBeanFactory().initializeBean(reply, protocol);
+                reply = (MailRepository) getBeanFactory().initializeBean(reply, destination.getProtocol().getValue());
 
-                repositories.put(repID, reply);
-                LOGGER.info("added repository: {}->{}", repID, repClass);
+                repositories.put(destination, reply);
+                LOGGER.info("added repository: {}->{}", defConf, repClass);
                 return reply;
             } catch (Exception e) {
                 LOGGER.warn("Exception while creating repository: {}", e.getMessage(), e);
@@ -206,7 +201,7 @@ public class MailRepositoryStoreBeanFactory extends AbstractBeanFactory implemen
     }
 
     @Override
-    public synchronized List<String> getUrls() {
+    public synchronized List<MailRepositoryUrl> getUrls() {
         return new ArrayList<>(repositories.keySet());
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/container/spring/src/main/java/org/apache/james/container/spring/tool/James23Importer.java
----------------------------------------------------------------------
diff --git a/server/container/spring/src/main/java/org/apache/james/container/spring/tool/James23Importer.java b/server/container/spring/src/main/java/org/apache/james/container/spring/tool/James23Importer.java
index 42eeded..c3bccbe 100644
--- a/server/container/spring/src/main/java/org/apache/james/container/spring/tool/James23Importer.java
+++ b/server/container/spring/src/main/java/org/apache/james/container/spring/tool/James23Importer.java
@@ -33,9 +33,11 @@ import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.exception.MailboxExistsException;
 import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
 import org.apache.james.mailrepository.api.MailRepositoryStore.MailRepositoryStoreException;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.server.core.MimeMessageInputStream;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.james.user.api.UsersRepositoryException;
@@ -135,8 +137,8 @@ public class James23Importer {
         while (james23userRepositoryIterator.hasNext()) {
 
             String userName23 = james23userRepositoryIterator.next();
-            MailRepository mailRepository = mailRepositoryStore.select(james23MailRepositoryPath + "/" + userName23);
-            Iterator<String> mailRepositoryIterator = mailRepository.list();
+            MailRepository mailRepository = mailRepositoryStore.select(MailRepositoryUrl.from(james23MailRepositoryPath + "/" + userName23));
+            Iterator<MailKey> mailRepositoryIterator = mailRepository.list();
 
             String userName30 = convert23UserTo30(userName23);
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/data/data-api/src/main/java/org/apache/james/mailrepository/api/MailRepositoryStore.java
----------------------------------------------------------------------
diff --git a/server/data/data-api/src/main/java/org/apache/james/mailrepository/api/MailRepositoryStore.java b/server/data/data-api/src/main/java/org/apache/james/mailrepository/api/MailRepositoryStore.java
index 37f1902..e13c31c 100644
--- a/server/data/data-api/src/main/java/org/apache/james/mailrepository/api/MailRepositoryStore.java
+++ b/server/data/data-api/src/main/java/org/apache/james/mailrepository/api/MailRepositoryStore.java
@@ -31,13 +31,13 @@ public interface MailRepositoryStore {
      * @return repository
      * @throws MailRepositoryStoreException
      */
-    MailRepository select(String url) throws MailRepositoryStoreException;
+    MailRepository select(MailRepositoryUrl url) throws MailRepositoryStoreException;
 
     /**
      * Create the {@link MailRepository} for the given url and return it. If the repository already exists,
      * then no new repository is created, the old one will be returned.
      */
-    default MailRepository create(String url) throws MailRepositoryStoreException {
+    default MailRepository create(MailRepositoryUrl url) throws MailRepositoryStoreException {
         return select(url);
     }
 
@@ -45,7 +45,7 @@ public interface MailRepositoryStore {
      * Returns the {@link MailRepository} for the given url.
      * This mail repository will not be created if it does not exist.
      */
-    Optional<MailRepository> get(String url) throws MailRepositoryStoreException;
+    Optional<MailRepository> get(MailRepositoryUrl url) throws MailRepositoryStoreException;
 
     /**
      * Return a {@link List} which contains all urls of the selected
@@ -53,7 +53,7 @@ public interface MailRepositoryStore {
      * 
      * @return urls
      */
-    List<String> getUrls();
+    List<MailRepositoryUrl> getUrls();
 
     class MailRepositoryStoreException extends Exception {
         public MailRepositoryStoreException(String msg, Throwable t) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/data/data-file/src/main/java/org/apache/james/mailrepository/file/FileMailRepository.java
----------------------------------------------------------------------
diff --git a/server/data/data-file/src/main/java/org/apache/james/mailrepository/file/FileMailRepository.java b/server/data/data-file/src/main/java/org/apache/james/mailrepository/file/FileMailRepository.java
index 2123562..1f6b1d1 100644
--- a/server/data/data-file/src/main/java/org/apache/james/mailrepository/file/FileMailRepository.java
+++ b/server/data/data-file/src/main/java/org/apache/james/mailrepository/file/FileMailRepository.java
@@ -36,6 +36,7 @@ import javax.mail.internet.MimeMessage;
 import org.apache.commons.configuration.DefaultConfigurationBuilder;
 import org.apache.commons.configuration.HierarchicalConfiguration;
 import org.apache.james.filesystem.api.FileSystem;
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.lib.AbstractMailRepository;
 import org.apache.james.repository.file.FilePersistentObjectRepository;
 import org.apache.james.repository.file.FilePersistentStreamRepository;
@@ -124,7 +125,7 @@ public class FileMailRepository extends AbstractMailRepository {
             Collection<String> strandedStreams = (Collection<String>) streamKeys.clone();
             strandedStreams.removeAll(objectKeys);
             for (Object strandedStream : strandedStreams) {
-                String key = (String) strandedStream;
+                MailKey key = new MailKey((String) strandedStream);
                 remove(key);
             }
 
@@ -132,7 +133,7 @@ public class FileMailRepository extends AbstractMailRepository {
             Collection<String> strandedObjects = (Collection<String>) objectKeys.clone();
             strandedObjects.removeAll(streamKeys);
             for (Object strandedObject : strandedObjects) {
-                String key = (String) strandedObject;
+                MailKey key = new MailKey((String) strandedObject);
                 remove(key);
             }
 
@@ -218,11 +219,11 @@ public class FileMailRepository extends AbstractMailRepository {
     }
 
     @Override
-    public Mail retrieve(String key) throws MessagingException {
+    public Mail retrieve(MailKey key) throws MessagingException {
         try {
             Mail mc;
             try {
-                mc = (Mail) objectRepository.get(key);
+                mc = (Mail) objectRepository.get(key.asString());
             } catch (RuntimeException re) {
                 if (re.getCause() instanceof Error) {
                     LOGGER.warn("Error when retrieving mail, not deleting: {}", re, re);
@@ -232,7 +233,7 @@ public class FileMailRepository extends AbstractMailRepository {
                 }
                 return null;
             }
-            MimeMessageStreamRepositorySource source = new MimeMessageStreamRepositorySource(streamRepository, destination, key);
+            MimeMessageStreamRepositorySource source = new MimeMessageStreamRepositorySource(streamRepository, destination, key.asString());
             mc.setMessage(new MimeMessageCopyOnWriteProxy(source));
 
             return mc;
@@ -243,16 +244,16 @@ public class FileMailRepository extends AbstractMailRepository {
     }
 
     @Override
-    protected void internalRemove(String key) throws MessagingException {
+    protected void internalRemove(MailKey key) throws MessagingException {
         if (keys != null) {
-            keys.remove(key);
+            keys.remove(key.asString());
         }
-        streamRepository.remove(key);
-        objectRepository.remove(key);
+        streamRepository.remove(key.asString());
+        objectRepository.remove(key.asString());
     }
 
     @Override
-    public Iterator<String> list() {
+    public Iterator<MailKey> list() {
         // Fix ConcurrentModificationException by cloning
         // the keyset before getting an iterator
         final ArrayList<String> clone;
@@ -270,6 +271,8 @@ public class FileMailRepository extends AbstractMailRepository {
             Collections.sort(clone); // Keys is a HashSet; impose FIFO for apps
         }
         // that need it
-        return clone.iterator();
+        return clone.stream()
+            .map(MailKey::new)
+            .iterator();
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/data/data-file/src/main/java/org/apache/james/mailrepository/file/MBoxMailRepository.java
----------------------------------------------------------------------
diff --git a/server/data/data-file/src/main/java/org/apache/james/mailrepository/file/MBoxMailRepository.java b/server/data/data-file/src/main/java/org/apache/james/mailrepository/file/MBoxMailRepository.java
index bcc69bb..e4f8350 100755
--- a/server/data/data-file/src/main/java/org/apache/james/mailrepository/file/MBoxMailRepository.java
+++ b/server/data/data-file/src/main/java/org/apache/james/mailrepository/file/MBoxMailRepository.java
@@ -71,6 +71,7 @@ import org.apache.commons.configuration.ConfigurationException;
 import org.apache.commons.configuration.HierarchicalConfiguration;
 import org.apache.commons.io.FileUtils;
 import org.apache.james.lifecycle.api.Configurable;
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.server.core.MailImpl;
 import org.apache.mailet.Mail;
@@ -467,7 +468,7 @@ public class MBoxMailRepository implements MailRepository, Configurable {
     }
 
     @Override
-    public void store(Mail mc) {
+    public MailKey store(Mail mc) {
         LOGGER.debug("Will store message to file {}", mboxFile);
 
         this.mList = null;
@@ -494,16 +495,16 @@ public class MBoxMailRepository implements MailRepository, Configurable {
             saveFile.writeBytes((fromHeader + "\n"));
             saveFile.writeBytes((message + "\n"));
             saveFile.close();
-
         } catch (FileNotFoundException e) {
             LOGGER.error("Unable to save(open) file (File not found) {}", mboxFile, e);
         } catch (IOException e) {
             LOGGER.error("Unable to write file (General I/O problem) {}", mboxFile, e);
         }
+        return MailKey.forMail(mc);
     }
 
     @Override
-    public Iterator<String> list() {
+    public Iterator<MailKey> list() {
         ArrayList<String> keys = loadKeysAsArray();
 
         if (!keys.isEmpty()) {
@@ -517,7 +518,9 @@ public class MBoxMailRepository implements MailRepository, Configurable {
         if (fifo) {
             Collections.sort(keys); // Keys is a HashSet; impose FIFO for apps that need it
         }
-        return keys.iterator();
+        return keys.stream()
+            .map(MailKey::new)
+            .iterator();
     }
 
     private ArrayList<String> loadKeysAsArray() {
@@ -526,19 +529,19 @@ public class MBoxMailRepository implements MailRepository, Configurable {
     }
 
     @Override
-    public Mail retrieve(String key) throws MessagingException {
+    public Mail retrieve(MailKey key) throws MessagingException {
 
         loadKeys();
         MailImpl res;
 
-        MimeMessage foundMessage = findMessage(key);
+        MimeMessage foundMessage = findMessage(key.asString());
         if (foundMessage == null) {
             LOGGER.error("found message is null!");
             return null;
         }
         res = new MailImpl();
         res.setMessage(foundMessage);
-        res.setName(key);
+        res.setName(key.asString());
         LOGGER.debug("Retrieving entry for key {}", key);
         return res;
     }
@@ -669,7 +672,7 @@ public class MBoxMailRepository implements MailRepository, Configurable {
     }
 
     @Override
-    public void remove(String key) throws MessagingException {
+    public void remove(MailKey key) throws MessagingException {
         loadKeys();
         try {
             lockMBox();
@@ -685,23 +688,23 @@ public class MBoxMailRepository implements MailRepository, Configurable {
     }
 
     @Override
-    public boolean lock(String key) {
+    public boolean lock(MailKey key) {
         return false;
     }
 
     @Override
-    public boolean unlock(String key) {
+    public boolean unlock(MailKey key) {
         return false;
     }
 
     @Override
-    public long size() throws MessagingException {
+    public long size() {
         return loadKeysAsArray().size();
     }
 
     @Override
     public void removeAll() throws MessagingException {
         ImmutableList.copyOf(list())
-            .forEach(Throwing.<String>consumer(this::remove).sneakyThrow());
+            .forEach(Throwing.<MailKey>consumer(this::remove).sneakyThrow());
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/data/data-file/src/test/java/org/apache/james/mailrepository/MBoxMailRepositoryTest.java
----------------------------------------------------------------------
diff --git a/server/data/data-file/src/test/java/org/apache/james/mailrepository/MBoxMailRepositoryTest.java b/server/data/data-file/src/test/java/org/apache/james/mailrepository/MBoxMailRepositoryTest.java
index b874cd9..30ff7b0 100644
--- a/server/data/data-file/src/test/java/org/apache/james/mailrepository/MBoxMailRepositoryTest.java
+++ b/server/data/data-file/src/test/java/org/apache/james/mailrepository/MBoxMailRepositoryTest.java
@@ -26,6 +26,7 @@ import java.util.Iterator;
 
 import org.apache.commons.configuration.DefaultConfigurationBuilder;
 import org.apache.james.filesystem.api.mock.MockFileSystem;
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.file.MBoxMailRepository;
 import org.junit.Test;
@@ -67,7 +68,7 @@ public class MBoxMailRepositoryTest {
     public void testReadMboxrdFile() throws Exception {
         MailRepository mr = getMailRepository();
 
-        Iterator<String> keys = mr.list();
+        Iterator<MailKey> keys = mr.list();
 
         assertTrue("Two messages in list", keys.hasNext());
         keys.next();

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/data/data-jcr/src/main/java/org/apache/james/mailrepository/jcr/JCRMailRepository.java
----------------------------------------------------------------------
diff --git a/server/data/data-jcr/src/main/java/org/apache/james/mailrepository/jcr/JCRMailRepository.java b/server/data/data-jcr/src/main/java/org/apache/james/mailrepository/jcr/JCRMailRepository.java
index 1f9328c..0f57114 100644
--- a/server/data/data-jcr/src/main/java/org/apache/james/mailrepository/jcr/JCRMailRepository.java
+++ b/server/data/data-jcr/src/main/java/org/apache/james/mailrepository/jcr/JCRMailRepository.java
@@ -59,6 +59,7 @@ import org.apache.jackrabbit.commons.cnd.CndImporter;
 import org.apache.jackrabbit.util.ISO9075;
 import org.apache.jackrabbit.util.Text;
 import org.apache.james.core.MailAddress;
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.lib.AbstractMailRepository;
 import org.apache.james.server.core.MailImpl;
@@ -118,7 +119,7 @@ public class JCRMailRepository extends AbstractMailRepository implements MailRep
     }
 
     @Override
-    public Iterator<String> list() throws MessagingException {
+    public Iterator<MailKey> list() throws MessagingException {
         try {
             Session session = login();
             try {
@@ -130,7 +131,9 @@ public class JCRMailRepository extends AbstractMailRepository implements MailRep
                     String name = iterator.nextNode().getName();
                     keys.add(Text.unescapeIllegalJcrChars(name));
                 }
-                return keys.iterator();
+                return keys.stream()
+                    .map(MailKey::new)
+                    .iterator();
             } finally {
                 session.logout();
             }
@@ -140,11 +143,11 @@ public class JCRMailRepository extends AbstractMailRepository implements MailRep
     }
 
     @Override
-    public Mail retrieve(String key) throws MessagingException {
+    public Mail retrieve(MailKey key) throws MessagingException {
         try {
             Session session = login();
             try {
-                String name = toSafeName(key);
+                String name = toSafeName(key.asString());
                 QueryManager manager = session.getWorkspace().getQueryManager();
                 Query query = manager.createQuery("/jcr:root/" + MAIL_PATH + "//element(" + name + ",james:mail)", Query.XPATH);
                 NodeIterator iterator = query.execute().getNodes();
@@ -591,11 +594,11 @@ public class JCRMailRepository extends AbstractMailRepository implements MailRep
     }
 
     @Override
-    protected void internalRemove(String key) throws MessagingException {
+    protected void internalRemove(MailKey key) throws MessagingException {
         try {
             Session session = login();
             try {
-                String name = ISO9075.encode(Text.escapeIllegalJcrChars(key));
+                String name = ISO9075.encode(Text.escapeIllegalJcrChars(key.asString()));
                 QueryManager manager = session.getWorkspace().getQueryManager();
                 Query query = manager.createQuery("/jcr:root/" + MAIL_PATH + "//element(" + name + ",james:mail)", Query.XPATH);
                 NodeIterator nodes = query.execute().getNodes();

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/JDBCMailRepository.java
----------------------------------------------------------------------
diff --git a/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/JDBCMailRepository.java b/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/JDBCMailRepository.java
index dae0b7b..31a728e 100644
--- a/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/JDBCMailRepository.java
+++ b/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/JDBCMailRepository.java
@@ -53,6 +53,7 @@ import org.apache.commons.configuration.DefaultConfigurationBuilder;
 import org.apache.commons.configuration.HierarchicalConfiguration;
 import org.apache.james.core.MailAddress;
 import org.apache.james.filesystem.api.FileSystem;
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.lib.AbstractMailRepository;
 import org.apache.james.repository.file.FilePersistentStreamRepository;
 import org.apache.james.server.core.MailImpl;
@@ -592,7 +593,7 @@ public class JDBCMailRepository extends AbstractMailRepository {
 
     @Override
     @SuppressWarnings("unchecked")
-    public Mail retrieve(String key) throws MessagingException {
+    public Mail retrieve(MailKey key) throws MessagingException {
         if (DEEP_DEBUG) {
             System.err.println("retrieving " + key);
         }
@@ -606,7 +607,7 @@ public class JDBCMailRepository extends AbstractMailRepository {
             }
 
             retrieveMessage = conn.prepareStatement(sqlQueries.getSqlString("retrieveMessageSQL", true));
-            retrieveMessage.setString(1, key);
+            retrieveMessage.setString(1, key.asString());
             retrieveMessage.setString(2, repositoryName);
             rsMessage = retrieveMessage.executeQuery();
             if (DEEP_DEBUG) {
@@ -625,7 +626,7 @@ public class JDBCMailRepository extends AbstractMailRepository {
                 try {
                     retrieveMessageAttr = conn.prepareStatement(retrieveMessageAttrSql);
 
-                    retrieveMessageAttr.setString(1, key);
+                    retrieveMessageAttr.setString(1, key.asString());
                     retrieveMessageAttr.setString(2, repositoryName);
                     rsMessageAttr = retrieveMessageAttr.executeQuery();
 
@@ -662,7 +663,7 @@ public class JDBCMailRepository extends AbstractMailRepository {
 
             MailImpl mc = new MailImpl();
             mc.setAttributesRaw(attributes);
-            mc.setName(key);
+            mc.setName(key.asString());
             mc.setState(rsMessage.getString(1));
             mc.setErrorMessage(rsMessage.getString(2));
             String sender = rsMessage.getString(3);
@@ -681,7 +682,7 @@ public class JDBCMailRepository extends AbstractMailRepository {
             mc.setRemoteAddr(rsMessage.getString(6));
             mc.setLastUpdated(rsMessage.getTimestamp(7));
 
-            MimeMessageJDBCSource source = new MimeMessageJDBCSource(this, key, sr);
+            MimeMessageJDBCSource source = new MimeMessageJDBCSource(this, key.asString(), sr);
             MimeMessageCopyOnWriteProxy message = new MimeMessageCopyOnWriteProxy(source);
             mc.setMessage(message);
             return mc;
@@ -699,18 +700,18 @@ public class JDBCMailRepository extends AbstractMailRepository {
     }
 
     @Override
-    protected void internalRemove(String key) throws MessagingException {
+    protected void internalRemove(MailKey key) throws MessagingException {
         Connection conn = null;
         PreparedStatement removeMessage = null;
         try {
             conn = datasource.getConnection();
             removeMessage = conn.prepareStatement(sqlQueries.getSqlString("removeMessageSQL", true));
-            removeMessage.setString(1, key);
+            removeMessage.setString(1, key.asString());
             removeMessage.setString(2, repositoryName);
             removeMessage.execute();
 
             if (sr != null) {
-                sr.remove(key);
+                sr.remove(key.asString());
             }
         } catch (Exception me) {
             throw new MessagingException("Exception while removing mail: " + me.getMessage(), me);
@@ -721,7 +722,7 @@ public class JDBCMailRepository extends AbstractMailRepository {
     }
 
     @Override
-    public Iterator<String> list() throws MessagingException {
+    public Iterator<MailKey> list() throws MessagingException {
         // System.err.println("listing messages");
         Connection conn = null;
         PreparedStatement listMessages = null;
@@ -736,7 +737,9 @@ public class JDBCMailRepository extends AbstractMailRepository {
             while (rsListMessages.next() && !Thread.currentThread().isInterrupted()) {
                 messageList.add(rsListMessages.getString(1));
             }
-            return messageList.iterator();
+            return messageList.stream()
+                .map(MailKey::new)
+                .iterator();
         } catch (Exception me) {
             throw new MessagingException("Exception while listing mail: " + me.getMessage(), me);
         } finally {

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/data/data-library/src/main/java/org/apache/james/mailrepository/lib/AbstractMailRepository.java
----------------------------------------------------------------------
diff --git a/server/data/data-library/src/main/java/org/apache/james/mailrepository/lib/AbstractMailRepository.java b/server/data/data-library/src/main/java/org/apache/james/mailrepository/lib/AbstractMailRepository.java
index bc9b813..4e988e5 100644
--- a/server/data/data-library/src/main/java/org/apache/james/mailrepository/lib/AbstractMailRepository.java
+++ b/server/data/data-library/src/main/java/org/apache/james/mailrepository/lib/AbstractMailRepository.java
@@ -27,6 +27,7 @@ import javax.mail.MessagingException;
 import org.apache.commons.configuration.ConfigurationException;
 import org.apache.commons.configuration.HierarchicalConfiguration;
 import org.apache.james.lifecycle.api.Configurable;
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.repository.api.Initializable;
 import org.apache.mailet.Mail;
@@ -73,7 +74,7 @@ public abstract class AbstractMailRepository implements MailRepository, Configur
      * @return true if successfully released the lock, false otherwise
      */
     @Override
-    public boolean unlock(String key) {
+    public boolean unlock(MailKey key) {
         return lock.unlock(key);
     }
 
@@ -86,14 +87,14 @@ public abstract class AbstractMailRepository implements MailRepository, Configur
      * @return true if successfully obtained the lock, false otherwise
      */
     @Override
-    public boolean lock(String key) {
+    public boolean lock(MailKey key) {
         return lock.lock(key);
     }
 
     @Override
-    public void store(Mail mc) throws MessagingException {
+    public MailKey store(Mail mc) throws MessagingException {
         boolean wasLocked = true;
-        String key = mc.getName();
+        MailKey key = MailKey.forMail(mc);
         try {
             synchronized (this) {
                 wasLocked = lock.isLocked(key);
@@ -103,6 +104,7 @@ public abstract class AbstractMailRepository implements MailRepository, Configur
                 }
             }
             internalStore(mc);
+            return key;
         } catch (MessagingException e) {
             LOGGER.error("Exception caught while storing mail {}", key, e);
             throw e;
@@ -124,7 +126,7 @@ public abstract class AbstractMailRepository implements MailRepository, Configur
 
     @Override
     public void remove(Mail mail) throws MessagingException {
-        remove(mail.getName());
+        remove(MailKey.forMail(mail));
     }
 
     @Override
@@ -135,7 +137,7 @@ public abstract class AbstractMailRepository implements MailRepository, Configur
     }
 
     @Override
-    public void remove(String key) throws MessagingException {
+    public void remove(MailKey key) throws MessagingException {
         if (lock(key)) {
             try {
                 internalRemove(key);
@@ -143,12 +145,11 @@ public abstract class AbstractMailRepository implements MailRepository, Configur
                 unlock(key);
             }
         } else {
-            String exceptionBuffer = "Cannot lock " + key + " to remove it";
-            throw new MessagingException(exceptionBuffer.toString());
+            throw new MessagingException("Cannot lock " + key + " to remove it");
         }
     }
 
-    protected abstract void internalRemove(String key) throws MessagingException;
+    protected abstract void internalRemove(MailKey key) throws MessagingException;
 
     @Override
     public long size() throws MessagingException {
@@ -158,6 +159,6 @@ public abstract class AbstractMailRepository implements MailRepository, Configur
     @Override
     public void removeAll() throws MessagingException {
         ImmutableList.copyOf(list())
-            .forEach(Throwing.<String>consumer(this::remove).sneakyThrow());
+            .forEach(Throwing.<MailKey>consumer(this::remove).sneakyThrow());
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/data/data-library/src/test/java/org/apache/james/mailrepository/mock/MockMailRepositoryStore.java
----------------------------------------------------------------------
diff --git a/server/data/data-library/src/test/java/org/apache/james/mailrepository/mock/MockMailRepositoryStore.java b/server/data/data-library/src/test/java/org/apache/james/mailrepository/mock/MockMailRepositoryStore.java
index ef9e085..f232e43 100644
--- a/server/data/data-library/src/test/java/org/apache/james/mailrepository/mock/MockMailRepositoryStore.java
+++ b/server/data/data-library/src/test/java/org/apache/james/mailrepository/mock/MockMailRepositoryStore.java
@@ -27,27 +27,28 @@ import java.util.Optional;
 
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 
 public class MockMailRepositoryStore implements MailRepositoryStore {
 
-    final Map<String, MailRepository> storedObjectMap = new HashMap<>();
+    private final Map<MailRepositoryUrl, MailRepository> storedObjectMap = new HashMap<>();
 
-    public void add(String url, MailRepository obj) {
+    public void add(MailRepositoryUrl url, MailRepository obj) {
         storedObjectMap.put(url, obj);
     }
 
     @Override
-    public MailRepository select(String url) throws MailRepositoryStoreException {
+    public MailRepository select(MailRepositoryUrl url) {
         return storedObjectMap.get(url);
     }
 
     @Override
-    public Optional<MailRepository> get(String url) throws MailRepositoryStoreException {
+    public Optional<MailRepository> get(MailRepositoryUrl url) {
         return Optional.ofNullable(storedObjectMap.get(url));
     }
 
     @Override
-    public List<String> getUrls() {
+    public List<MailRepositoryUrl> getUrls() {
         return new ArrayList<>(storedObjectMap.keySet());
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/MailetErrorsTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/MailetErrorsTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/MailetErrorsTest.java
index ffd8013..0dc9ed0 100644
--- a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/MailetErrorsTest.java
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/MailetErrorsTest.java
@@ -31,6 +31,7 @@ import org.apache.james.mailets.configuration.CommonProcessors;
 import org.apache.james.mailets.configuration.MailetConfiguration;
 import org.apache.james.mailets.configuration.MailetContainer;
 import org.apache.james.mailets.configuration.ProcessorConfiguration;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.transport.mailets.ErrorMailet;
 import org.apache.james.transport.mailets.ErrorMatcher;
 import org.apache.james.transport.mailets.NoopMailet;
@@ -49,7 +50,7 @@ import org.junit.rules.TemporaryFolder;
 
 public class MailetErrorsTest {
     public static final String CUSTOM_PROCESSOR = "custom";
-    public static final String CUSTOM_REPOSITORY = "file://var/mail/custom/";
+    public static final MailRepositoryUrl CUSTOM_REPOSITORY = MailRepositoryUrl.from("file://var/mail/custom/");
 
     @Rule
     public TemporaryFolder temporaryFolder = new TemporaryFolder();
@@ -162,7 +163,7 @@ public class MailetErrorsTest {
                     .addMailet(MailetConfiguration.builder()
                         .matcher(All.class)
                         .mailet(ToRepository.class)
-                        .addProperty("repositoryPath", CUSTOM_REPOSITORY))))
+                        .addProperty("repositoryPath", CUSTOM_REPOSITORY.asString()))))
             .build(temporaryFolder);
         MailRepositoryProbeImpl probe = jamesServer.getProbe(MailRepositoryProbeImpl.class);
 
@@ -187,7 +188,7 @@ public class MailetErrorsTest {
                     .addMailet(MailetConfiguration.builder()
                         .matcher(All.class)
                         .mailet(ToRepository.class)
-                        .addProperty("repositoryPath", CUSTOM_REPOSITORY))))
+                        .addProperty("repositoryPath", CUSTOM_REPOSITORY.asString()))))
             .build(temporaryFolder);
         MailRepositoryProbeImpl probe = jamesServer.getProbe(MailRepositoryProbeImpl.class);
 
@@ -292,7 +293,7 @@ public class MailetErrorsTest {
                     .addMailet(MailetConfiguration.builder()
                         .matcher(All.class)
                         .mailet(ToRepository.class)
-                        .addProperty("repositoryPath", CUSTOM_REPOSITORY))))
+                        .addProperty("repositoryPath", CUSTOM_REPOSITORY.asString()))))
             .build(temporaryFolder);
         MailRepositoryProbeImpl probe = jamesServer.getProbe(MailRepositoryProbeImpl.class);
 
@@ -317,7 +318,7 @@ public class MailetErrorsTest {
                     .addMailet(MailetConfiguration.builder()
                         .matcher(All.class)
                         .mailet(ToRepository.class)
-                        .addProperty("repositoryPath", CUSTOM_REPOSITORY))))
+                        .addProperty("repositoryPath", CUSTOM_REPOSITORY.asString()))))
             .build(temporaryFolder);
         MailRepositoryProbeImpl probe = jamesServer.getProbe(MailRepositoryProbeImpl.class);
 
@@ -338,7 +339,7 @@ public class MailetErrorsTest {
                     .addMailet(MailetConfiguration.builder()
                         .matcher(RuntimeErrorMatcher.class)
                         .mailet(ToRepository.class)
-                        .addProperty("repositoryPath", CUSTOM_REPOSITORY)
+                        .addProperty("repositoryPath", CUSTOM_REPOSITORY.asString())
                         .addProperty("onMatchException", "matchall"))
                     .addMailet(MailetConfiguration.builder()
                         .matcher(All.class)
@@ -363,7 +364,7 @@ public class MailetErrorsTest {
                     .addMailet(MailetConfiguration.builder()
                         .matcher(ErrorMatcher.class)
                         .mailet(ToRepository.class)
-                        .addProperty("repositoryPath", CUSTOM_REPOSITORY)
+                        .addProperty("repositoryPath", CUSTOM_REPOSITORY.asString())
                         .addProperty("onMatchException", "matchall"))
                     .addMailet(MailetConfiguration.builder()
                         .matcher(All.class)
@@ -387,11 +388,11 @@ public class MailetErrorsTest {
                             .matcher(HasException.class)
                             .matcherCondition("javax.mail.MessagingException")
                             .mailet(ToRepository.class)
-                            .addProperty("repositoryPath", CUSTOM_REPOSITORY))
+                            .addProperty("repositoryPath", CUSTOM_REPOSITORY.asString()))
                     .addMailet(MailetConfiguration.builder()
                             .matcher(All.class)
                             .mailet(ToRepository.class)
-                            .addProperty("repositoryPath", ERROR_REPOSITORY)))
+                            .addProperty("repositoryPath", ERROR_REPOSITORY.asString())))
                 .putProcessor(customProcessor())
                 .putProcessor(ProcessorConfiguration.root()
                     .addMailet(MailetConfiguration.builder()
@@ -416,11 +417,11 @@ public class MailetErrorsTest {
                             .matcher(HasException.class)
                             .matcherCondition("javax.mail.MessagingException")
                             .mailet(ToRepository.class)
-                            .addProperty("repositoryPath", CUSTOM_REPOSITORY))
+                            .addProperty("repositoryPath", CUSTOM_REPOSITORY.asString()))
                     .addMailet(MailetConfiguration.builder()
                             .matcher(All.class)
                             .mailet(ToRepository.class)
-                            .addProperty("repositoryPath", ERROR_REPOSITORY)))
+                            .addProperty("repositoryPath", ERROR_REPOSITORY.asString())))
                 .putProcessor(customProcessor())
                 .putProcessor(ProcessorConfiguration.root()
                     .addMailet(MailetConfiguration.builder()
@@ -445,11 +446,11 @@ public class MailetErrorsTest {
                             .matcher(HasException.class)
                             .matcherCondition("javax.mail.MessagingException")
                             .mailet(ToRepository.class)
-                            .addProperty("repositoryPath", CUSTOM_REPOSITORY))
+                            .addProperty("repositoryPath", CUSTOM_REPOSITORY.asString()))
                     .addMailet(MailetConfiguration.builder()
                             .matcher(All.class)
                             .mailet(ToRepository.class)
-                            .addProperty("repositoryPath", ERROR_REPOSITORY)))
+                            .addProperty("repositoryPath", ERROR_REPOSITORY.asString())))
                 .putProcessor(customProcessor())
                 .putProcessor(ProcessorConfiguration.root()
                     .addMailet(MailetConfiguration.builder()
@@ -474,11 +475,11 @@ public class MailetErrorsTest {
                             .matcher(HasException.class)
                             .matcherCondition("javax.mail.MessagingException")
                             .mailet(ToRepository.class)
-                            .addProperty("repositoryPath", CUSTOM_REPOSITORY))
+                            .addProperty("repositoryPath", CUSTOM_REPOSITORY.asString()))
                     .addMailet(MailetConfiguration.builder()
                             .matcher(All.class)
                             .mailet(ToRepository.class)
-                            .addProperty("repositoryPath", ERROR_REPOSITORY)))
+                            .addProperty("repositoryPath", ERROR_REPOSITORY.asString())))
                 .putProcessor(customProcessor())
                 .putProcessor(ProcessorConfiguration.root()
                     .addMailet(MailetConfiguration.builder()
@@ -497,7 +498,7 @@ public class MailetErrorsTest {
             .addMailet(MailetConfiguration.builder()
                 .matcher(All.class)
                 .mailet(ToRepository.class)
-                .addProperty("repositoryPath", ERROR_REPOSITORY));
+                .addProperty("repositoryPath", ERROR_REPOSITORY.asString()));
     }
 
     private ProcessorConfiguration.Builder customProcessor() {
@@ -506,6 +507,6 @@ public class MailetErrorsTest {
             .addMailet(MailetConfiguration.builder()
                 .matcher(All.class)
                 .mailet(ToRepository.class)
-                .addProperty("repositoryPath", CUSTOM_REPOSITORY));
+                .addProperty("repositoryPath", CUSTOM_REPOSITORY.asString()));
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/NetworkMatcherIntegrationTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/NetworkMatcherIntegrationTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/NetworkMatcherIntegrationTest.java
index 7fcc013..7132860 100644
--- a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/NetworkMatcherIntegrationTest.java
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/NetworkMatcherIntegrationTest.java
@@ -31,6 +31,7 @@ import org.apache.james.MemoryJamesServerMain;
 import org.apache.james.mailets.configuration.CommonProcessors;
 import org.apache.james.mailets.configuration.MailetConfiguration;
 import org.apache.james.mailets.configuration.ProcessorConfiguration;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.probe.DataProbe;
 import org.apache.james.transport.mailets.ToProcessor;
 import org.apache.james.transport.mailets.ToRepository;
@@ -48,7 +49,7 @@ import org.junit.rules.TemporaryFolder;
 
 public class NetworkMatcherIntegrationTest {
     private static final String FROM = "fromuser@" + DEFAULT_DOMAIN;
-    private static final String DROPPED_MAILS = "file://var/mail/dropped-mails/";
+    private static final MailRepositoryUrl DROPPED_MAILS = MailRepositoryUrl.from("file://var/mail/dropped-mails/");
 
     @Rule
     public TemporaryFolder temporaryFolder = new TemporaryFolder();
@@ -77,7 +78,7 @@ public class NetworkMatcherIntegrationTest {
         return MailetConfiguration.builder()
             .matcher(All.class)
             .mailet(ToRepository.class)
-            .addProperty("repositoryPath", DROPPED_MAILS);
+            .addProperty("repositoryPath", DROPPED_MAILS.asString());
     }
 
     @After

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SmtpAuthIntegrationTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SmtpAuthIntegrationTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SmtpAuthIntegrationTest.java
index 0796b77..4f5085e 100644
--- a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SmtpAuthIntegrationTest.java
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SmtpAuthIntegrationTest.java
@@ -32,6 +32,7 @@ import org.apache.james.mailets.configuration.CommonProcessors;
 import org.apache.james.mailets.configuration.MailetConfiguration;
 import org.apache.james.mailets.configuration.MailetContainer;
 import org.apache.james.mailets.configuration.ProcessorConfiguration;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.probe.DataProbe;
 import org.apache.james.transport.mailets.ToProcessor;
 import org.apache.james.transport.mailets.ToRepository;
@@ -49,7 +50,7 @@ import org.junit.rules.TemporaryFolder;
 
 public class SmtpAuthIntegrationTest {
     private static final String FROM = "fromuser@" + DEFAULT_DOMAIN;
-    private static final String DROPPED_MAILS = "file://var/mail/dropped-mails/";
+    private static final MailRepositoryUrl DROPPED_MAILS = MailRepositoryUrl.from("file://var/mail/dropped-mails/");
 
     @Rule
     public IMAPMessageReader imapMessageReader = new IMAPMessageReader();
@@ -91,7 +92,7 @@ public class SmtpAuthIntegrationTest {
             .addMailet(MailetConfiguration.builder()
                 .matcher(All.class)
                 .mailet(ToRepository.class)
-                .addProperty("repositoryPath", DROPPED_MAILS));
+                .addProperty("repositoryPath", DROPPED_MAILS.asString()));
     }
 
     @After

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/configuration/CommonProcessors.java
----------------------------------------------------------------------
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/configuration/CommonProcessors.java b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/configuration/CommonProcessors.java
index 73daf8b..d465620 100644
--- a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/configuration/CommonProcessors.java
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/configuration/CommonProcessors.java
@@ -21,6 +21,7 @@
 package org.apache.james.mailets.configuration;
 
 import org.apache.james.jmap.mailet.VacationMailet;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.transport.mailets.AddDeliveredToHeader;
 import org.apache.james.transport.mailets.Bounce;
 import org.apache.james.transport.mailets.DSNBounce;
@@ -46,7 +47,7 @@ import org.apache.mailet.Mail;
 
 public class CommonProcessors {
 
-    public static final String ERROR_REPOSITORY = "file://var/mail/error/";
+    public static final MailRepositoryUrl ERROR_REPOSITORY = MailRepositoryUrl.from("file://var/mail/error/");
 
     public static ProcessorConfiguration root() {
         return ProcessorConfiguration.root()
@@ -106,7 +107,7 @@ public class CommonProcessors {
                 .addMailet(MailetConfiguration.builder()
                         .matcher(All.class)
                         .mailet(ToRepository.class)
-                        .addProperty("repositoryPath", ERROR_REPOSITORY))
+                        .addProperty("repositoryPath", ERROR_REPOSITORY.asString()))
                 .build();
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/GroupMappingTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/GroupMappingTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/GroupMappingTest.java
index f0f6ba6..45bbcd8 100644
--- a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/GroupMappingTest.java
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/GroupMappingTest.java
@@ -39,6 +39,7 @@ import org.apache.james.mailets.configuration.CommonProcessors;
 import org.apache.james.mailets.configuration.MailetConfiguration;
 import org.apache.james.mailets.configuration.MailetContainer;
 import org.apache.james.mailets.configuration.ProcessorConfiguration;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.modules.MailboxProbeImpl;
 import org.apache.james.probe.DataProbe;
 import org.apache.james.transport.matchers.All;
@@ -74,7 +75,7 @@ public class GroupMappingTest {
     private static final String USER_DOMAIN2 = "user@" + DOMAIN2;
     private static final String MESSAGE_CONTENT = "any text";
     public static final String RRT_ERROR = "rrt-error";
-    public static final String RRT_ERROR_REPOSITORY = "file://var/mail/rrt-error/";
+    public static final MailRepositoryUrl RRT_ERROR_REPOSITORY = MailRepositoryUrl.from("file://var/mail/rrt-error/");
 
     private TemporaryJamesServer jamesServer;
     private MimeMessage message;
@@ -111,7 +112,7 @@ public class GroupMappingTest {
                     .matcher(All.class)
                     .mailet(ToRepository.class)
                     .addProperty("passThrough", "true")
-                    .addProperty("repositoryPath", RRT_ERROR_REPOSITORY))
+                    .addProperty("repositoryPath", RRT_ERROR_REPOSITORY.asString()))
                 .addMailet(MailetConfiguration.builder()
                     .matcher(IsSenderInRRTLoop.class)
                     .mailet(Null.class))

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/ToRepositoryTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/ToRepositoryTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/ToRepositoryTest.java
index 04c4abd..0fecd96 100644
--- a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/ToRepositoryTest.java
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/ToRepositoryTest.java
@@ -27,13 +27,12 @@ import static org.apache.james.mailets.configuration.Constants.PASSWORD;
 import static org.apache.james.mailets.configuration.Constants.SMTP_PORT;
 import static org.apache.james.mailets.configuration.Constants.awaitAtMostOneMinute;
 
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-
 import org.apache.james.mailets.TemporaryJamesServer;
 import org.apache.james.mailets.configuration.MailetConfiguration;
 import org.apache.james.mailets.configuration.MailetContainer;
 import org.apache.james.mailets.configuration.ProcessorConfiguration;
+import org.apache.james.mailrepository.api.MailKey;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.probe.DataProbe;
 import org.apache.james.transport.matchers.All;
 import org.apache.james.utils.DataProbeImpl;
@@ -54,7 +53,7 @@ import com.jayway.restassured.specification.RequestSpecification;
 
 public class ToRepositoryTest {
     private static final String RECIPIENT = "touser@" + DEFAULT_DOMAIN;
-    public static final String CUSTOM_REPOSITORY = "file://var/mail/custom/";
+    public static final MailRepositoryUrl CUSTOM_REPOSITORY = MailRepositoryUrl.from("file://var/mail/custom/");
 
     @Rule
     public TemporaryFolder temporaryFolder = new TemporaryFolder();
@@ -74,7 +73,7 @@ public class ToRepositoryTest {
                 .addMailet(MailetConfiguration.builder()
                     .matcher(All.class)
                     .mailet(ToRepository.class)
-                    .addProperty("repositoryPath", CUSTOM_REPOSITORY)));
+                    .addProperty("repositoryPath", CUSTOM_REPOSITORY.asString())));
 
         jamesServer = TemporaryJamesServer.builder()
             .withMailetContainer(mailetContainer)
@@ -118,7 +117,7 @@ public class ToRepositoryTest {
             .queryParam("processor", ProcessorConfiguration.STATE_TRANSPORT)
             .queryParam("action", "reprocess")
         .patch(MailRepositoriesRoutes.MAIL_REPOSITORIES
-                + "/" + URLEncoder.encode(CUSTOM_REPOSITORY, StandardCharsets.UTF_8.displayName())
+                + "/" + CUSTOM_REPOSITORY.urlEncoded()
                 + "/mails")
             .jsonPath()
             .getString("taskId");
@@ -142,15 +141,15 @@ public class ToRepositoryTest {
             .sendMessage(RECIPIENT, RECIPIENT);
 
         awaitAtMostOneMinute.until(() -> probe.getRepositoryMailCount(CUSTOM_REPOSITORY) == 2);
-        String key = probe.listMailKeys(CUSTOM_REPOSITORY).get(0);
+        MailKey key = probe.listMailKeys(CUSTOM_REPOSITORY).get(0);
 
         with()
             .spec(webAdminAPI)
             .queryParam("processor", ProcessorConfiguration.STATE_TRANSPORT)
             .queryParam("action", "reprocess")
             .patch(MailRepositoriesRoutes.MAIL_REPOSITORIES
-                + "/" + URLEncoder.encode(CUSTOM_REPOSITORY, StandardCharsets.UTF_8.displayName())
-                + "/mails/" + key)
+                + "/" + CUSTOM_REPOSITORY.urlEncoded()
+                + "/mails/" + key.asString())
             .jsonPath()
             .get("taskId");
 


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


[2/5] james-project git commit: JAMES-2422 Update documentation about environment variables

Posted by ma...@apache.org.
JAMES-2422 Update documentation about environment variables


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/7dfda860
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/7dfda860
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/7dfda860

Branch: refs/heads/master
Commit: 7dfda860f7e0c29fd50cc2e3f4183be2dc9642bb
Parents: 1694051
Author: Raphael Ouazana <ra...@linagora.com>
Authored: Tue Jun 12 11:38:15 2018 +0200
Committer: Matthieu Baechler <ma...@apache.org>
Committed: Wed Jun 13 16:56:24 2018 +0200

----------------------------------------------------------------------
 src/site/xdoc/server/config.xml | 4 ++++
 1 file changed, 4 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/7dfda860/src/site/xdoc/server/config.xml
----------------------------------------------------------------------
diff --git a/src/site/xdoc/server/config.xml b/src/site/xdoc/server/config.xml
index 00f9dd8..807b22d 100644
--- a/src/site/xdoc/server/config.xml
+++ b/src/site/xdoc/server/config.xml
@@ -39,6 +39,10 @@
     some back-ends, to some protocols. Please also note that some configuration files are not required for Guice. In this
     case, it will be specified.</p>
        
+    <p>In every configuration files, you can use the following notation to use an environment variable: <code>${env:MY_VAR}</code>.
+    If <code>MY_VAR</code> exists, it will be replaced by the matching environment variable.
+    If it does not, <code>${env:MY_VAR}</code> will be kept as is.</p>
+    
     <p>You can/must configure James for the following:</p>
     
     <table>


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


[3/5] james-project git commit: JAMES-2418 Strong typing for repository URLs and keys

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailKeyDTO.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailKeyDTO.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailKeyDTO.java
new file mode 100644
index 0000000..64d4504
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailKeyDTO.java
@@ -0,0 +1,52 @@
+/****************************************************************
+ * 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.webadmin.dto;
+
+import java.util.Objects;
+
+import org.apache.james.mailrepository.api.MailKey;
+
+public class MailKeyDTO {
+
+    private final MailKey mailKey;
+
+    public MailKeyDTO(MailKey mailKey) {
+        this.mailKey = mailKey;
+    }
+
+    public String getMailKey() {
+        return mailKey.asString();
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof MailKeyDTO) {
+            MailKeyDTO that = (MailKeyDTO) o;
+
+            return Objects.equals(this.mailKey, that.mailKey);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(mailKey);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailRepositoryResponse.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailRepositoryResponse.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailRepositoryResponse.java
index 6e58052..962ffa0 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailRepositoryResponse.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailRepositoryResponse.java
@@ -20,22 +20,22 @@
 package org.apache.james.webadmin.dto;
 
 import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
+
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 
 public class MailRepositoryResponse {
 
-    private final String repository;
+    private final MailRepositoryUrl repository;
 
-    public MailRepositoryResponse(String repository) {
+    public MailRepositoryResponse(MailRepositoryUrl repository) {
         this.repository = repository;
     }
 
     public String getRepository() {
-        return repository;
+        return repository.asString();
     }
 
     public String getId() throws UnsupportedEncodingException {
-        return URLEncoder.encode(repository, StandardCharsets.UTF_8.displayName());
+        return repository.urlEncoded();
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
index b60bb6f..29ea380 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
@@ -22,8 +22,6 @@ package org.apache.james.webadmin.routes;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.Optional;
 import java.util.function.Supplier;
@@ -38,7 +36,9 @@ import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.queue.api.MailQueueFactory;
 import org.apache.james.task.Task;
 import org.apache.james.task.TaskId;
@@ -124,7 +124,7 @@ public class MailRepositoriesRoutes implements Routes {
     })
     public void definePutMailRepository() {
         service.put(MAIL_REPOSITORIES + "/:encodedUrl", (request, response) -> {
-            String url = decodedRepositoryUrl(request);
+            MailRepositoryUrl url = decodedRepositoryUrl(request);
             try {
                 repositoryStoreService.createMailRepository(url);
                 response.status(HttpStatus.NO_CONTENT_204);
@@ -134,7 +134,7 @@ public class MailRepositoriesRoutes implements Routes {
                     .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500)
                     .type(ErrorResponder.ErrorType.SERVER_ERROR)
                     .cause(e)
-                    .message(String.format("Error while creating a mail repository with url '%s'", url))
+                    .message(String.format("Error while creating a mail repository with url '%s'", url.asString()))
                     .haltError();
             }
         }, jsonTransformer);
@@ -172,7 +172,7 @@ public class MailRepositoriesRoutes implements Routes {
             Offset offset = ParametersExtractor.extractOffset(request);
             Limit limit = ParametersExtractor.extractLimit(request);
             String encodedUrl = request.params("encodedUrl");
-            String url = decodedRepositoryUrl(request);
+            MailRepositoryUrl url = MailRepositoryUrl.fromEncoded(encodedUrl);
             try {
                 return repositoryStoreService.listMails(url, offset, limit)
                     .orElseThrow(() -> ErrorResponder.builder()
@@ -214,13 +214,17 @@ public class MailRepositoriesRoutes implements Routes {
         @ApiResponse(code = HttpStatus.NOT_FOUND_404, message = "Not found - Could not retrieve the given mail.")
     })
     public void defineGetMail() {
-
         service.get(MAIL_REPOSITORIES + "/:encodedUrl/mails/:mailKey", Constants.JSON_CONTENT_TYPE,
-            (request, response) -> getMailAsJson(decodedRepositoryUrl(request), request.params("mailKey")), jsonTransformer);
+            (request, response) -> getMailAsJson(
+                decodedRepositoryUrl(request),
+                new MailKey(request.params("mailKey"))),
+            jsonTransformer);
 
         service.get(MAIL_REPOSITORIES + "/:encodedUrl/mails/:mailKey", Constants.RFC822_CONTENT_TYPE,
             (request, response) -> writeMimeMessage(
-                getMailAsMimeMessage(decodedRepositoryUrl(request), request.params("mailKey")),
+                getMailAsMimeMessage(
+                    decodedRepositoryUrl(request),
+                    new MailKey(request.params("mailKey"))),
                 response.raw()));
     }
 
@@ -237,7 +241,7 @@ public class MailRepositoriesRoutes implements Routes {
         return byteArrayOutputStream.size();
     }
 
-    private MimeMessage getMailAsMimeMessage(String url, String mailKey) {
+    private MimeMessage getMailAsMimeMessage(MailRepositoryUrl url, MailKey mailKey) {
         try {
             return repositoryStoreService.retrieveMessage(url, mailKey)
                 .orElseThrow(mailNotFoundError(mailKey));
@@ -246,7 +250,7 @@ public class MailRepositoriesRoutes implements Routes {
         }
     }
 
-    private MailDto getMailAsJson(String url, String mailKey) {
+    private MailDto getMailAsJson(MailRepositoryUrl url, MailKey mailKey) {
         try {
             return repositoryStoreService.retrieveMail(url, mailKey)
                 .orElseThrow(mailNotFoundError(mailKey));
@@ -255,11 +259,11 @@ public class MailRepositoriesRoutes implements Routes {
         }
     }
 
-    private Supplier<HaltException> mailNotFoundError(String mailKey) {
+    private Supplier<HaltException> mailNotFoundError(MailKey mailKey) {
         return () -> ErrorResponder.builder()
             .statusCode(HttpStatus.NOT_FOUND_404)
             .type(ErrorResponder.ErrorType.NOT_FOUND)
-            .message("Could not retrieve " + mailKey)
+            .message("Could not retrieve " + mailKey.asString())
             .haltError();
     }
 
@@ -283,7 +287,7 @@ public class MailRepositoriesRoutes implements Routes {
     public void defineGetMailRepository() {
         service.get(MAIL_REPOSITORIES + "/:encodedUrl", (request, response) -> {
             String encodedUrl = request.params("encodedUrl");
-            String url = URLDecoder.decode(encodedUrl, StandardCharsets.UTF_8.displayName());
+            MailRepositoryUrl url = MailRepositoryUrl.fromEncoded(encodedUrl);
             try {
                 long size = repositoryStoreService.size(url)
                     .orElseThrow(() -> ErrorResponder.builder()
@@ -312,8 +316,8 @@ public class MailRepositoriesRoutes implements Routes {
     })
     public void defineDeleteMail() {
         service.delete(MAIL_REPOSITORIES + "/:encodedUrl/mails/:mailKey", (request, response) -> {
-            String url = decodedRepositoryUrl(request);
-            String mailKey = request.params("mailKey");
+            MailRepositoryUrl url = decodedRepositoryUrl(request);
+            MailKey mailKey = new MailKey(request.params("mailKey"));
             try {
                 response.status(HttpStatus.NO_CONTENT_204);
                 repositoryStoreService.deleteMail(url, mailKey);
@@ -339,7 +343,7 @@ public class MailRepositoriesRoutes implements Routes {
     })
     public void defineDeleteAll() {
         service.delete(MAIL_REPOSITORIES + "/:encodedUrl/mails", (request, response) -> {
-            String url = decodedRepositoryUrl(request);
+            MailRepositoryUrl url = decodedRepositoryUrl(request);
             try {
                 Task task = repositoryStoreService.createClearMailRepositoryTask(url);
                 TaskId taskId = taskManager.submit(task);
@@ -398,7 +402,7 @@ public class MailRepositoriesRoutes implements Routes {
     }
 
     private Task toAllMailReprocessingTask(Request request) throws UnsupportedEncodingException, MailRepositoryStore.MailRepositoryStoreException, MessagingException {
-        String url = decodedRepositoryUrl(request);
+        MailRepositoryUrl url = decodedRepositoryUrl(request);
         enforceActionParameter(request);
         Optional<String> targetProcessor = Optional.ofNullable(request.queryParams("processor"));
         String targetQueue = Optional.ofNullable(request.queryParams("queue")).orElse(MailQueueFactory.SPOOL);
@@ -450,8 +454,8 @@ public class MailRepositoriesRoutes implements Routes {
     }
 
     private Task toOneMailReprocessingTask(Request request) throws UnsupportedEncodingException {
-        String url = decodedRepositoryUrl(request);
-        String key = request.params("key");
+        MailRepositoryUrl url = decodedRepositoryUrl(request);
+        MailKey key = new MailKey(request.params("key"));
         enforceActionParameter(request);
         Optional<String> targetProcessor = Optional.ofNullable(request.queryParams("processor"));
         String targetQueue = Optional.ofNullable(request.queryParams("queue")).orElse(MailQueueFactory.SPOOL);
@@ -470,7 +474,7 @@ public class MailRepositoriesRoutes implements Routes {
         }
     }
 
-    private String decodedRepositoryUrl(Request request) throws UnsupportedEncodingException {
-        return URLDecoder.decode(request.params("encodedUrl"), StandardCharsets.UTF_8.displayName());
+    private MailRepositoryUrl decodedRepositoryUrl(Request request) throws UnsupportedEncodingException {
+        return MailRepositoryUrl.fromEncoded(request.params("encodedUrl"));
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ClearMailRepositoryTask.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ClearMailRepositoryTask.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ClearMailRepositoryTask.java
index 4dfc43e..853e530 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ClearMailRepositoryTask.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ClearMailRepositoryTask.java
@@ -25,6 +25,7 @@ import java.util.function.Supplier;
 import javax.mail.MessagingException;
 
 import org.apache.james.mailrepository.api.MailRepository;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.task.Task;
 import org.apache.james.task.TaskExecutionDetails;
 
@@ -33,18 +34,18 @@ public class ClearMailRepositoryTask implements Task {
     public static final String TYPE = "clearMailRepository";
 
     public static class AdditionalInformation implements TaskExecutionDetails.AdditionalInformation {
-        private final String repositoryUrl;
+        private final MailRepositoryUrl repositoryUrl;
         private final Supplier<Long> countSupplier;
         private final long initialCount;
 
-        public AdditionalInformation(String repositoryUrl, Supplier<Long> countSupplier) {
+        public AdditionalInformation(MailRepositoryUrl repositoryUrl, Supplier<Long> countSupplier) {
             this.repositoryUrl = repositoryUrl;
             this.initialCount = countSupplier.get();
             this.countSupplier = countSupplier;
         }
 
         public String getRepositoryUrl() {
-            return repositoryUrl;
+            return repositoryUrl.asString();
         }
 
         public long getRemainingCount() {
@@ -59,7 +60,7 @@ public class ClearMailRepositoryTask implements Task {
     private final MailRepository mailRepository;
     private final AdditionalInformation additionalInformation;
 
-    public ClearMailRepositoryTask(MailRepository mailRepository, String url) {
+    public ClearMailRepositoryTask(MailRepository mailRepository, MailRepositoryUrl url) {
         this.mailRepository = mailRepository;
         this.additionalInformation = new AdditionalInformation(url, this::getRemainingSize);
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
index de33041..7a0d691 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
@@ -26,14 +26,16 @@ import javax.inject.Inject;
 import javax.mail.MessagingException;
 import javax.mail.internet.MimeMessage;
 
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.task.Task;
 import org.apache.james.util.streams.Iterators;
 import org.apache.james.util.streams.Limit;
 import org.apache.james.util.streams.Offset;
 import org.apache.james.webadmin.dto.MailDto;
-import org.apache.james.webadmin.dto.MailKey;
+import org.apache.james.webadmin.dto.MailKeyDTO;
 import org.apache.james.webadmin.dto.MailRepositoryResponse;
 import org.apache.james.webadmin.utils.ErrorResponder;
 import org.apache.mailet.Mail;
@@ -58,53 +60,53 @@ public class MailRepositoryStoreService {
             .collect(Guavate.toImmutableList());
     }
 
-    public MailRepository createMailRepository(String repositoryUrl) throws MailRepositoryStore.MailRepositoryStoreException {
+    public MailRepository createMailRepository(MailRepositoryUrl repositoryUrl) throws MailRepositoryStore.MailRepositoryStoreException {
         return mailRepositoryStore.create(repositoryUrl);
     }
 
-    public Optional<List<MailKey>> listMails(String url, Offset offset, Limit limit) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
+    public Optional<List<MailKeyDTO>> listMails(MailRepositoryUrl url, Offset offset, Limit limit) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
         Optional<MailRepository> mailRepository = Optional.ofNullable(getRepository(url));
-        ThrowingFunction<MailRepository, List<MailKey>> list = repository -> list(repository, offset, limit);
+        ThrowingFunction<MailRepository, List<MailKeyDTO>> list = repository -> list(repository, offset, limit);
         return mailRepository.map(Throwing.function(list).sneakyThrow());
     }
 
-    private List<MailKey> list(MailRepository mailRepository, Offset offset, Limit limit) throws MessagingException {
+    private List<MailKeyDTO> list(MailRepository mailRepository, Offset offset, Limit limit) throws MessagingException {
         return limit.applyOnStream(
                 Iterators.toStream(mailRepository.list())
                     .skip(offset.getOffset()))
-                .map(MailKey::new)
+                .map(MailKeyDTO::new)
                 .collect(Guavate.toImmutableList());
     }
 
-    public Optional<Long> size(String url) throws MailRepositoryStore.MailRepositoryStoreException {
+    public Optional<Long> size(MailRepositoryUrl url) throws MailRepositoryStore.MailRepositoryStoreException {
         Optional<MailRepository> mailRepository = Optional.ofNullable(getRepository(url));
         return mailRepository.map(Throwing.function(MailRepository::size).sneakyThrow());
     }
 
-    public Optional<MailDto> retrieveMail(String url, String mailKey) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
+    public Optional<MailDto> retrieveMail(MailRepositoryUrl url, MailKey mailKey) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
         MailRepository mailRepository = getRepository(url);
 
         return Optional.ofNullable(mailRepository.retrieve(mailKey))
             .map(Throwing.function(MailDto::fromMail).sneakyThrow());
     }
 
-    public Optional<MimeMessage> retrieveMessage(String url, String mailKey) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
+    public Optional<MimeMessage> retrieveMessage(MailRepositoryUrl url, MailKey mailKey) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
         MailRepository mailRepository = getRepository(url);
 
         return Optional.ofNullable(mailRepository.retrieve(mailKey))
             .map(Throwing.function(Mail::getMessage).sneakyThrow());
     }
 
-    public void deleteMail(String url, String mailKey) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
+    public void deleteMail(MailRepositoryUrl url, MailKey mailKey) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
         getRepository(url)
             .remove(mailKey);
     }
 
-    public Task createClearMailRepositoryTask(String url) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
+    public Task createClearMailRepositoryTask(MailRepositoryUrl url) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
         return new ClearMailRepositoryTask(getRepository(url), url);
     }
 
-    public MailRepository getRepository(String url) throws MailRepositoryStore.MailRepositoryStoreException {
+    public MailRepository getRepository(MailRepositoryUrl url) throws MailRepositoryStore.MailRepositoryStoreException {
         return mailRepositoryStore.get(url)
             .orElseThrow(() -> ErrorResponder.builder()
                 .statusCode(HttpStatus.NOT_FOUND_404)

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ReprocessingAllMailsTask.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ReprocessingAllMailsTask.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ReprocessingAllMailsTask.java
index 6f75e2e..26ad50d 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ReprocessingAllMailsTask.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ReprocessingAllMailsTask.java
@@ -24,7 +24,9 @@ import java.util.concurrent.atomic.AtomicLong;
 
 import javax.mail.MessagingException;
 
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.task.Task;
 import org.apache.james.task.TaskExecutionDetails;
 
@@ -35,13 +37,13 @@ public class ReprocessingAllMailsTask implements Task {
     public static final String TYPE = "reprocessingAllTask";
 
     public static class AdditionalInformation implements TaskExecutionDetails.AdditionalInformation {
-        private final String repositoryUrl;
+        private final MailRepositoryUrl repositoryUrl;
         private final String targetQueue;
         private final Optional<String> targetProcessor;
         private final long initialCount;
         private final AtomicLong processedCount;
 
-        public AdditionalInformation(String repositoryUrl, String targetQueue, Optional<String> targetProcessor, long initialCount) {
+        public AdditionalInformation(MailRepositoryUrl repositoryUrl, String targetQueue, Optional<String> targetProcessor, long initialCount) {
             this.repositoryUrl = repositoryUrl;
             this.targetQueue = targetQueue;
             this.targetProcessor = targetProcessor;
@@ -58,7 +60,7 @@ public class ReprocessingAllMailsTask implements Task {
         }
 
         public String getRepositoryUrl() {
-            return repositoryUrl;
+            return repositoryUrl.asString();
         }
 
         public long getRemainingCount() {
@@ -70,19 +72,19 @@ public class ReprocessingAllMailsTask implements Task {
         }
 
         @JsonIgnore
-        public void notifyProgress(String key) {
+        public void notifyProgress(MailKey key) {
             processedCount.incrementAndGet();
         }
     }
 
     private final ReprocessingService reprocessingService;
-    private final String repositoryUrl;
+    private final MailRepositoryUrl repositoryUrl;
     private final String targetQueue;
     private final Optional<String> targetProcessor;
     private final AdditionalInformation additionalInformation;
 
     public ReprocessingAllMailsTask(ReprocessingService reprocessingService, long repositorySize,
-                                    String repositoryUrl, String targetQueue, Optional<String> targetProcessor) {
+                                    MailRepositoryUrl repositoryUrl, String targetQueue, Optional<String> targetProcessor) {
         this.reprocessingService = reprocessingService;
         this.repositoryUrl = repositoryUrl;
         this.targetQueue = targetQueue;

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ReprocessingOneMailTask.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ReprocessingOneMailTask.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ReprocessingOneMailTask.java
index d476bfc..453ff8b 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ReprocessingOneMailTask.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ReprocessingOneMailTask.java
@@ -23,7 +23,9 @@ import java.util.Optional;
 
 import javax.mail.MessagingException;
 
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.task.Task;
 import org.apache.james.task.TaskExecutionDetails;
 
@@ -32,12 +34,12 @@ public class ReprocessingOneMailTask implements Task {
     public static final String TYPE = "reprocessingOneTask";
 
     public static class AdditionalInformation implements TaskExecutionDetails.AdditionalInformation {
-        private final String repositoryUrl;
+        private final MailRepositoryUrl repositoryUrl;
         private final String targetQueue;
-        private final String mailKey;
+        private final MailKey mailKey;
         private final Optional<String> targetProcessor;
 
-        public AdditionalInformation(String repositoryUrl, String targetQueue, String mailKey, Optional<String> targetProcessor) {
+        public AdditionalInformation(MailRepositoryUrl repositoryUrl, String targetQueue, MailKey mailKey, Optional<String> targetProcessor) {
             this.repositoryUrl = repositoryUrl;
             this.targetQueue = targetQueue;
             this.mailKey = mailKey;
@@ -45,7 +47,7 @@ public class ReprocessingOneMailTask implements Task {
         }
 
         public String getMailKey() {
-            return mailKey;
+            return mailKey.asString();
         }
 
         public String getTargetQueue() {
@@ -57,19 +59,19 @@ public class ReprocessingOneMailTask implements Task {
         }
 
         public String getRepositoryUrl() {
-            return repositoryUrl;
+            return repositoryUrl.asString();
         }
     }
 
     private final ReprocessingService reprocessingService;
-    private final String repositoryUrl;
+    private final MailRepositoryUrl repositoryUrl;
     private final String targetQueue;
-    private final String mailKey;
+    private final MailKey mailKey;
     private final Optional<String> targetProcessor;
     private final AdditionalInformation additionalInformation;
 
     public ReprocessingOneMailTask(ReprocessingService reprocessingService,
-                                   String repositoryUrl, String targetQueue, String mailKey, Optional<String> targetProcessor) {
+                                   MailRepositoryUrl repositoryUrl, String targetQueue, MailKey mailKey, Optional<String> targetProcessor) {
         this.reprocessingService = reprocessingService;
         this.repositoryUrl = repositoryUrl;
         this.targetQueue = targetQueue;

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ReprocessingService.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ReprocessingService.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ReprocessingService.java
index 8eee51b..fbe68a4 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ReprocessingService.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/ReprocessingService.java
@@ -25,8 +25,10 @@ import java.util.function.Consumer;
 import javax.inject.Inject;
 import javax.mail.MessagingException;
 
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.queue.api.MailQueue;
 import org.apache.james.queue.api.MailQueueFactory;
 import org.apache.james.util.streams.Iterators;
@@ -46,7 +48,7 @@ public class ReprocessingService {
         this.mailRepositoryStoreService = mailRepositoryStoreService;
     }
 
-    public void reprocessAll(String url, Optional<String> targetProcessor, String targetQueue, Consumer<String> keyListener) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
+    public void reprocessAll(MailRepositoryUrl url, Optional<String> targetProcessor, String targetQueue, Consumer<MailKey> keyListener) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
         MailRepository repository = mailRepositoryStoreService.getRepository(url);
         MailQueue mailQueue = getMailQueue(targetQueue);
 
@@ -55,14 +57,14 @@ public class ReprocessingService {
             .forEach(Throwing.consumer(key -> reprocess(repository, mailQueue, key, targetProcessor)));
     }
 
-    public void reprocess(String url, String key, Optional<String> targetProcessor, String targetQueue) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
+    public void reprocess(MailRepositoryUrl url, MailKey key, Optional<String> targetProcessor, String targetQueue) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
         MailRepository repository = mailRepositoryStoreService.getRepository(url);
         MailQueue mailQueue = getMailQueue(targetQueue);
 
         reprocess(repository, mailQueue, key, targetProcessor);
     }
 
-    private void reprocess(MailRepository repository, MailQueue mailQueue, String key, Optional<String> targetProcessor) throws MessagingException {
+    private void reprocess(MailRepository repository, MailQueue mailQueue, MailKey key, Optional<String> targetProcessor) throws MessagingException {
         Mail mail = repository.retrieve(key);
         targetProcessor.ifPresent(mail::setState);
         mailQueue.enQueue(mail);

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
index 7370e3c..ae90244 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
@@ -32,7 +32,7 @@ import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.isEmptyOrNullString;
 import static org.hamcrest.Matchers.notNullValue;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -43,7 +43,9 @@ import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.Optional;
 
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.mailrepository.memory.MemoryMailRepository;
 import org.apache.james.metrics.api.NoopMetricFactory;
 import org.apache.james.queue.api.MailQueueFactory;
@@ -69,6 +71,7 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import com.github.steveash.guavate.Guavate;
 import com.google.common.collect.ImmutableList;
 import com.jayway.restassured.RestAssured;
 import com.jayway.restassured.builder.RequestSpecBuilder;
@@ -77,7 +80,7 @@ import com.jayway.restassured.parsing.Parser;
 
 public class MailRepositoriesRoutesTest {
 
-    private static final String URL_MY_REPO = "url://myRepo";
+    private static final MailRepositoryUrl URL_MY_REPO = MailRepositoryUrl.from("url://myRepo");
     private static final String URL_ESCAPED_MY_REPO = "url%3A%2F%2FmyRepo";
     private static final String MY_REPO_MAILS = "url%3A%2F%2FmyRepo/mails";
     private static final String CUSTOM_QUEUE = "customQueue";
@@ -152,7 +155,7 @@ public class MailRepositoriesRoutesTest {
 
     @Test
     public void putMailRepositoryShouldReturnServerErrorWhenCannotCreateRepository() throws Exception {
-        when(mailRepositoryStore.create(anyString()))
+        when(mailRepositoryStore.create(any()))
             .thenThrow(new MailRepositoryStore.MailRepositoryStoreException("Error while selecting repository url://myRepo"));
 
         when()
@@ -191,13 +194,13 @@ public class MailRepositoriesRoutesTest {
         .then()
             .statusCode(HttpStatus.OK_200)
             .body("", hasSize(1))
-            .body("[0].repository", is(URL_MY_REPO))
+            .body("[0].repository", is(URL_MY_REPO.asString()))
             .body("[0].id", is(URL_ESCAPED_MY_REPO));
     }
 
     @Test
     public void getMailRepositoriesShouldReturnTwoRepositoriesWhenTwo() {
-        ImmutableList<String> myRepositories = ImmutableList.of(URL_MY_REPO, "url://mySecondRepo");
+        ImmutableList<MailRepositoryUrl> myRepositories = ImmutableList.of(URL_MY_REPO, MailRepositoryUrl.from("url://mySecondRepo"));
         when(mailRepositoryStore.getUrls())
             .thenReturn(myRepositories);
 
@@ -212,7 +215,10 @@ public class MailRepositoriesRoutesTest {
                 .jsonPath()
                 .getList("repository");
 
-        assertThat(mailRepositories).containsOnlyElementsOf(myRepositories);
+        assertThat(mailRepositories)
+            .containsOnlyElementsOf(myRepositories.stream()
+                .map(MailRepositoryUrl::asString)
+                .collect(Guavate.toImmutableList()));
     }
 
     @Test
@@ -423,7 +429,7 @@ public class MailRepositoriesRoutesTest {
         .then()
             .statusCode(HttpStatus.OK_200)
             .contentType(ContentType.JSON)
-            .body("repository", is(URL_MY_REPO))
+            .body("repository", is(URL_MY_REPO.asString()))
             .body("id", is(URL_ESCAPED_MY_REPO));
     }
 
@@ -653,7 +659,7 @@ public class MailRepositoriesRoutesTest {
             .body("status", is("completed"))
             .body("taskId", is(notNullValue()))
             .body("type", is(ClearMailRepositoryTask.TYPE))
-            .body("additionalInformation.repositoryUrl", is(URL_MY_REPO))
+            .body("additionalInformation.repositoryUrl", is(URL_MY_REPO.asString()))
             .body("additionalInformation.initialCount", is(2))
             .body("additionalInformation.remainingCount", is(0))
             .body("startedDate", is(notNullValue()))
@@ -780,7 +786,7 @@ public class MailRepositoriesRoutesTest {
             .body("status", is("completed"))
             .body("taskId", is(notNullValue()))
             .body("type", is(ReprocessingAllMailsTask.TYPE))
-            .body("additionalInformation.repositoryUrl", is(URL_MY_REPO))
+            .body("additionalInformation.repositoryUrl", is(URL_MY_REPO.asString()))
             .body("additionalInformation.initialCount", is(2))
             .body("additionalInformation.remainingCount", is(0))
             .body("additionalInformation.targetProcessor", isEmptyOrNullString())
@@ -819,7 +825,7 @@ public class MailRepositoriesRoutesTest {
             .body("status", is("completed"))
             .body("taskId", is(notNullValue()))
             .body("type", is(ReprocessingAllMailsTask.TYPE))
-            .body("additionalInformation.repositoryUrl", is(URL_MY_REPO))
+            .body("additionalInformation.repositoryUrl", is(URL_MY_REPO.asString()))
             .body("additionalInformation.initialCount", is(2))
             .body("additionalInformation.remainingCount", is(0))
             .body("additionalInformation.targetProcessor", is(transport))
@@ -1052,7 +1058,7 @@ public class MailRepositoriesRoutesTest {
             .body("status", is("completed"))
             .body("taskId", is(notNullValue()))
             .body("type", is(ReprocessingOneMailTask.TYPE))
-            .body("additionalInformation.repositoryUrl", is(URL_MY_REPO))
+            .body("additionalInformation.repositoryUrl", is(URL_MY_REPO.asString()))
             .body("additionalInformation.mailKey", is(NAME_1))
             .body("additionalInformation.targetProcessor", isEmptyOrNullString())
             .body("additionalInformation.targetQueue", is(MailQueueFactory.SPOOL))
@@ -1090,7 +1096,7 @@ public class MailRepositoriesRoutesTest {
             .body("status", is("completed"))
             .body("taskId", is(notNullValue()))
             .body("type", is(ReprocessingOneMailTask.TYPE))
-            .body("additionalInformation.repositoryUrl", is(URL_MY_REPO))
+            .body("additionalInformation.repositoryUrl", is(URL_MY_REPO.asString()))
             .body("additionalInformation.mailKey", is(NAME_1))
             .body("additionalInformation.targetProcessor", is(transport))
             .body("additionalInformation.targetQueue", is(CUSTOM_QUEUE))
@@ -1125,7 +1131,7 @@ public class MailRepositoriesRoutesTest {
             .get(taskId + "/await");
 
         assertThat(mailRepository.list())
-            .containsOnly(NAME_2);
+            .containsOnly(new MailKey(NAME_2));
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/MailRepositoryStoreServiceTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/MailRepositoryStoreServiceTest.java b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/MailRepositoryStoreServiceTest.java
index 100959a..927c5136 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/MailRepositoryStoreServiceTest.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/MailRepositoryStoreServiceTest.java
@@ -29,13 +29,15 @@ import java.util.Optional;
 import javax.mail.internet.MimeMessage;
 
 import org.apache.commons.io.IOUtils;
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.mailrepository.memory.MemoryMailRepository;
 import org.apache.james.server.core.MimeMessageInputStream;
 import org.apache.james.util.ClassLoaderUtils;
 import org.apache.james.util.streams.Limit;
 import org.apache.james.util.streams.Offset;
-import org.apache.james.webadmin.dto.MailKey;
+import org.apache.james.webadmin.dto.MailKeyDTO;
 import org.apache.james.webadmin.dto.MailRepositoryResponse;
 import org.apache.mailet.base.test.FakeMail;
 import org.junit.Before;
@@ -44,10 +46,10 @@ import org.junit.Test;
 import com.google.common.collect.ImmutableList;
 
 public class MailRepositoryStoreServiceTest {
-    private static final String FIRST_REPOSITORY = "url://repository";
-    private static final String SECOND_REPOSITORY = "url://repository2";
-    private static final String NAME_1 = "name1";
-    private static final String NAME_2 = "name2";
+    private static final MailRepositoryUrl FIRST_REPOSITORY = MailRepositoryUrl.from("url://repository");
+    private static final MailRepositoryUrl SECOND_REPOSITORY = MailRepositoryUrl.from("url://repository2");
+    private static final MailKey NAME_1 = new MailKey("name1");
+    private static final MailKey NAME_2 = new MailKey("name2");
 
     private MailRepositoryStore mailRepositoryStore;
     private MailRepositoryStoreService testee;
@@ -71,7 +73,7 @@ public class MailRepositoryStoreServiceTest {
             .thenReturn(ImmutableList.of(FIRST_REPOSITORY));
         assertThat(testee.listMailRepositories())
             .extracting(MailRepositoryResponse::getRepository)
-            .containsOnly(FIRST_REPOSITORY);
+            .containsOnly(FIRST_REPOSITORY.asString());
     }
 
     @Test
@@ -80,7 +82,7 @@ public class MailRepositoryStoreServiceTest {
             .thenReturn(ImmutableList.of(FIRST_REPOSITORY, SECOND_REPOSITORY));
         assertThat(testee.listMailRepositories())
             .extracting(MailRepositoryResponse::getRepository)
-            .containsOnly(FIRST_REPOSITORY, SECOND_REPOSITORY);
+            .containsOnly(FIRST_REPOSITORY.asString(), SECOND_REPOSITORY.asString());
     }
 
     @Test
@@ -105,14 +107,14 @@ public class MailRepositoryStoreServiceTest {
         when(mailRepositoryStore.get(FIRST_REPOSITORY)).thenReturn(Optional.of(repository));
 
         repository.store(FakeMail.builder()
-            .name(NAME_1)
+            .name(NAME_1.asString())
             .build());
         repository.store(FakeMail.builder()
-            .name(NAME_2)
+            .name(NAME_2.asString())
             .build());
 
         assertThat(testee.listMails(FIRST_REPOSITORY, Offset.none(), Limit.unlimited()).get())
-            .containsOnly(new MailKey(NAME_1), new MailKey(NAME_2));
+            .containsOnly(new MailKeyDTO(NAME_1), new MailKeyDTO(NAME_2));
     }
 
     @Test
@@ -120,22 +122,22 @@ public class MailRepositoryStoreServiceTest {
         when(mailRepositoryStore.get(FIRST_REPOSITORY)).thenReturn(Optional.of(repository));
 
         repository.store(FakeMail.builder()
-            .name(NAME_1)
+            .name(NAME_1.asString())
             .build());
         repository.store(FakeMail.builder()
-            .name(NAME_2)
+            .name(NAME_2.asString())
             .build());
         repository.store(FakeMail.builder()
             .name("name3")
             .build());
 
         assertThat(testee.listMails(FIRST_REPOSITORY, Offset.from(1), Limit.from(1)).get())
-            .containsOnly(new MailKey(NAME_2));
+            .containsOnly(new MailKeyDTO(NAME_2));
     }
 
     @Test
     public void retrieveMessageShouldThrownWhenUnknownRepository() throws Exception {
-        when(mailRepositoryStore.get("unkown")).thenReturn(Optional.empty());
+        when(mailRepositoryStore.get(MailRepositoryUrl.from("proto://unkown"))).thenReturn(Optional.empty());
 
         assertThatThrownBy(() -> testee.retrieveMessage(FIRST_REPOSITORY, NAME_1))
             .isInstanceOf(NullPointerException.class);
@@ -163,7 +165,7 @@ public class MailRepositoryStoreServiceTest {
         when(mailRepositoryStore.get(FIRST_REPOSITORY)).thenReturn(Optional.of(repository));
 
         FakeMail mail = FakeMail.builder()
-            .name(NAME_1)
+            .name(NAME_1.asString())
             .fileName("mail.eml")
             .build();
         repository.store(mail);


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


[4/5] james-project git commit: JAMES-2418 Strong typing for repository URLs and keys

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/ToSenderDomainRepositoryTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/ToSenderDomainRepositoryTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/ToSenderDomainRepositoryTest.java
index 467f3d3..79ff8bc 100644
--- a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/ToSenderDomainRepositoryTest.java
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/ToSenderDomainRepositoryTest.java
@@ -30,6 +30,7 @@ import org.apache.james.mailets.TemporaryJamesServer;
 import org.apache.james.mailets.configuration.MailetConfiguration;
 import org.apache.james.mailets.configuration.MailetContainer;
 import org.apache.james.mailets.configuration.ProcessorConfiguration;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.transport.matchers.All;
 import org.apache.james.utils.DataProbeImpl;
 import org.apache.james.utils.IMAPMessageReader;
@@ -44,7 +45,8 @@ public class ToSenderDomainRepositoryTest {
 
     private static final String RECIPIENT = "touser@" + DEFAULT_DOMAIN;
     private static final String CUSTOM_REPOSITORY_PREFIX = "file://var/mail/custom/";
-    public static final String AWAIT_REPOSITORY_PATH = "file://var/mail/await/";
+    public static final MailRepositoryUrl DOMAIN_URL = MailRepositoryUrl.from(CUSTOM_REPOSITORY_PREFIX + DEFAULT_DOMAIN);
+    public static final MailRepositoryUrl AWAIT_REPOSITORY_PATH = MailRepositoryUrl.from("file://var/mail/await/");
 
     @Rule
     public TemporaryFolder temporaryFolder = new TemporaryFolder();
@@ -76,9 +78,9 @@ public class ToSenderDomainRepositoryTest {
             .sendMessage(RECIPIENT, RECIPIENT);
 
         awaitAtMostOneMinute.until(
-            () -> probe.getRepositoryMailCount(CUSTOM_REPOSITORY_PREFIX + DEFAULT_DOMAIN) == 1);
+            () -> probe.getRepositoryMailCount(DOMAIN_URL) == 1);
 
-        assertThat(probe.getRepositoryMailCount(CUSTOM_REPOSITORY_PREFIX + DEFAULT_DOMAIN))
+        assertThat(probe.getRepositoryMailCount(DOMAIN_URL))
             .isEqualTo(1);
     }
 
@@ -97,9 +99,9 @@ public class ToSenderDomainRepositoryTest {
             .sendMessage(RECIPIENT, RECIPIENT);
 
         awaitAtMostOneMinute.until(
-            () -> probe.getRepositoryMailCount(CUSTOM_REPOSITORY_PREFIX + DEFAULT_DOMAIN) == 1);
+            () -> probe.getRepositoryMailCount(DOMAIN_URL) == 1);
 
-        assertThat(probe.getRepositoryMailCount(CUSTOM_REPOSITORY_PREFIX + DEFAULT_DOMAIN))
+        assertThat(probe.getRepositoryMailCount(DOMAIN_URL))
             .isEqualTo(1);
     }
 
@@ -114,15 +116,15 @@ public class ToSenderDomainRepositoryTest {
                     .addProperty("allowRepositoryCreation", "true"))));
         MailRepositoryProbeImpl probe = jamesServer.getProbe(MailRepositoryProbeImpl.class);
 
-        probe.createRepository(CUSTOM_REPOSITORY_PREFIX + DEFAULT_DOMAIN);
+        probe.createRepository(DOMAIN_URL);
 
         messageSender.connect(LOCALHOST_IP, SMTP_PORT)
             .sendMessage(RECIPIENT, RECIPIENT);
 
         awaitAtMostOneMinute.until(
-            () -> probe.getRepositoryMailCount(CUSTOM_REPOSITORY_PREFIX + DEFAULT_DOMAIN) == 1);
+            () -> probe.getRepositoryMailCount(DOMAIN_URL) == 1);
 
-        assertThat(probe.getRepositoryMailCount(CUSTOM_REPOSITORY_PREFIX + DEFAULT_DOMAIN))
+        assertThat(probe.getRepositoryMailCount(DOMAIN_URL))
             .isEqualTo(1);
     }
 
@@ -139,7 +141,7 @@ public class ToSenderDomainRepositoryTest {
                 .addMailet(MailetConfiguration.builder()
                     .matcher(All.class)
                     .mailet(ToRepository.class)
-                    .addProperty("repositoryPath", AWAIT_REPOSITORY_PATH))));
+                    .addProperty("repositoryPath", AWAIT_REPOSITORY_PATH.asString()))));
         MailRepositoryProbeImpl mailRepositoryProbe = jamesServer.getProbe(MailRepositoryProbeImpl.class);
 
         messageSender.connect(LOCALHOST_IP, SMTP_PORT)
@@ -149,7 +151,7 @@ public class ToSenderDomainRepositoryTest {
             () -> mailRepositoryProbe.getRepositoryMailCount(AWAIT_REPOSITORY_PATH) == 1);
 
         assertThat(mailRepositoryProbe.listRepositoryUrls())
-            .doesNotContain(CUSTOM_REPOSITORY_PREFIX + DEFAULT_DOMAIN);
+            .doesNotContain(DOMAIN_URL);
     }
 
     @Test
@@ -163,15 +165,15 @@ public class ToSenderDomainRepositoryTest {
                     .addProperty("allowRepositoryCreation", "false"))));
         MailRepositoryProbeImpl probe = jamesServer.getProbe(MailRepositoryProbeImpl.class);
 
-        probe.createRepository(CUSTOM_REPOSITORY_PREFIX + DEFAULT_DOMAIN);
+        probe.createRepository(DOMAIN_URL);
 
         messageSender.connect(LOCALHOST_IP, SMTP_PORT)
             .sendMessage(RECIPIENT, RECIPIENT);
 
         awaitAtMostOneMinute.until(
-            () -> probe.getRepositoryMailCount(CUSTOM_REPOSITORY_PREFIX + DEFAULT_DOMAIN) == 1);
+            () -> probe.getRepositoryMailCount(DOMAIN_URL) == 1);
 
-        assertThat(probe.getRepositoryMailCount(CUSTOM_REPOSITORY_PREFIX + DEFAULT_DOMAIN))
+        assertThat(probe.getRepositoryMailCount(DOMAIN_URL))
             .isEqualTo(1);
     }
 
@@ -190,9 +192,9 @@ public class ToSenderDomainRepositoryTest {
             .sendMessage(RECIPIENT, RECIPIENT);
 
         awaitAtMostOneMinute.until(
-            () -> probe.getRepositoryMailCount(CUSTOM_REPOSITORY_PREFIX + DEFAULT_DOMAIN) == 2);
+            () -> probe.getRepositoryMailCount(DOMAIN_URL) == 2);
 
-        assertThat(probe.getRepositoryMailCount(CUSTOM_REPOSITORY_PREFIX + DEFAULT_DOMAIN))
+        assertThat(probe.getRepositoryMailCount(DOMAIN_URL))
             .isEqualTo(2);
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/FromRepository.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/FromRepository.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/FromRepository.java
index b8ef707..5a3d58e 100755
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/FromRepository.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/FromRepository.java
@@ -27,8 +27,10 @@ import javax.inject.Inject;
 import javax.mail.MessagingException;
 
 import org.apache.james.lifecycle.api.LifecycleUtil;
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.transport.mailets.managesieve.ManageSieveMailet;
 import org.apache.mailet.Experimental;
 import org.apache.mailet.Mail;
@@ -58,7 +60,7 @@ public class FromRepository extends GenericMailet {
     private boolean delete = false;
 
     /** The path to the repository */
-    private String repositoryPath;
+    private MailRepositoryUrl repositoryPath;
 
     /** The processor that will handle the re-spooled message(s) */
     private String processor;
@@ -72,7 +74,7 @@ public class FromRepository extends GenericMailet {
 
     @Override
     public void init() throws MessagingException {
-        repositoryPath = getInitParameter("repositoryPath");
+        repositoryPath = MailRepositoryUrl.from(getInitParameter("repositoryPath"));
         processor = (getInitParameter("processor") == null) ? Mail.DEFAULT : getInitParameter("processor");
 
         try {
@@ -98,10 +100,10 @@ public class FromRepository extends GenericMailet {
     @Override
     public void service(Mail trigger) throws MessagingException {
         trigger.setState(Mail.GHOST);
-        Collection<String> processed = new ArrayList<>();
-        Iterator<String> list = repository.list();
+        Collection<MailKey> processed = new ArrayList<>();
+        Iterator<MailKey> list = repository.list();
         while (list.hasNext()) {
-            String key = (String) list.next();
+            MailKey key = list.next();
             try {
                 Mail mail = repository.retrieve(key);
                 if (mail != null && mail.getRecipients() != null) {
@@ -122,7 +124,7 @@ public class FromRepository extends GenericMailet {
 
         if (delete) {
             for (Object aProcessed : processed) {
-                repository.remove((String) aProcessed);
+                repository.remove((MailKey) aProcessed);
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToRepository.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToRepository.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToRepository.java
index 4424721..8f480bc 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToRepository.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToRepository.java
@@ -24,6 +24,7 @@ import javax.mail.MessagingException;
 
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.mailet.Mail;
 import org.apache.mailet.base.GenericMailet;
 import org.slf4j.Logger;
@@ -41,7 +42,7 @@ public class ToRepository extends GenericMailet {
 
     private boolean passThrough = false;
 
-    private String repositoryPath;
+    private MailRepositoryUrl repositoryPath;
 
     private MailRepositoryStore mailStore;
 
@@ -52,7 +53,7 @@ public class ToRepository extends GenericMailet {
 
     @Override
     public void init() throws MessagingException {
-        repositoryPath = getInitParameter("repositoryPath");
+        repositoryPath = MailRepositoryUrl.from(getInitParameter("repositoryPath"));
         passThrough = getPassThroughParameter();
         repository = selectRepository();
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderDomainRepository.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderDomainRepository.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderDomainRepository.java
index f86becc..5992f85 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderDomainRepository.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderDomainRepository.java
@@ -26,6 +26,7 @@ import javax.mail.MessagingException;
 
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.mailet.Mail;
 import org.apache.mailet.base.GenericMailet;
 import org.slf4j.Logger;
@@ -85,14 +86,14 @@ public class ToSenderDomainRepository extends GenericMailet {
 
     @Override
     public void service(Mail mail) throws MessagingException {
-        String repositoryUrl = urlPrefix + mail.getSender().getDomain().asString();
+        MailRepositoryUrl repositoryUrl = MailRepositoryUrl.from(urlPrefix + mail.getSender().getDomain().asString());
         store(mail, repositoryUrl);
         if (!passThrough) {
             mail.setState(Mail.GHOST);
         }
     }
 
-    private void store(Mail mail, String url) throws MessagingException {
+    private void store(Mail mail, MailRepositoryUrl url) throws MessagingException {
         try {
             Optional<MailRepository> mailRepository = retrieveRepository(url);
             if (!mailRepository.isPresent()) {
@@ -106,7 +107,7 @@ public class ToSenderDomainRepository extends GenericMailet {
         }
     }
 
-    private Optional<MailRepository> retrieveRepository(String url) throws MailRepositoryStore.MailRepositoryStoreException {
+    private Optional<MailRepository> retrieveRepository(MailRepositoryUrl url) throws MailRepositoryStore.MailRepositoryStoreException {
         if (allowRepositoryCreation) {
             return Optional.of(mailRepositoryStore.select(url));
         } else {

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/ToRepositoryTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/ToRepositoryTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/ToRepositoryTest.java
index cd14097..a876f77 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/ToRepositoryTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/ToRepositoryTest.java
@@ -39,6 +39,7 @@ import org.junit.Test;
 import org.junit.rules.ExpectedException;
 
 public class ToRepositoryTest {
+    public static final String REPOSITORY_PATH = "file://var/mail/any";
     @Rule public ExpectedException expectedException = ExpectedException.none();
     
     private ToRepository mailet;
@@ -67,29 +68,32 @@ public class ToRepositoryTest {
     public void initShouldNotThrowWhenInvalidPassThrough() throws Exception {
         MailetConfig mockedMailetConfig = mock(MailetConfig.class);
         when(mockedMailetConfig.getInitParameter("passThrough")).thenThrow(new RuntimeException());
+        when(mockedMailetConfig.getInitParameter("repositoryPath")).thenReturn(REPOSITORY_PATH);
 
         mailet.init(mockedMailetConfig);
     }
 
     @Test
     public void initShouldThrowWhenMailStoreThrows() throws Exception {
-        when(mailRepositoryStore.select(any(String.class))).thenThrow(new RuntimeException());
+        when(mailRepositoryStore.select(any())).thenThrow(new RuntimeException());
         expectedException.expect(MessagingException.class);
 
         FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
-                .mailetName("Test")
-                .build();
+            .mailetName("Test")
+            .setProperty("repositoryPath", REPOSITORY_PATH)
+            .build();
         mailet.init(mailetConfig);
     }
 
     @Test
     public void serviceShouldStoreMailIntoRepository() throws Exception {
         MailRepository mailRepository = mock(MailRepository.class);
-        when(mailRepositoryStore.select(any(String.class))).thenReturn(mailRepository);
+        when(mailRepositoryStore.select(any())).thenReturn(mailRepository);
 
         FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
-                .mailetName("Test")
-                .build();
+            .mailetName("Test")
+            .setProperty("repositoryPath", REPOSITORY_PATH)
+            .build();
         mailet.init(mailetConfig);
 
         mailet.service(message);
@@ -100,10 +104,11 @@ public class ToRepositoryTest {
     @Test
     public void serviceShouldGhostMailIfPassThroughNotSet() throws Exception {
         MailRepository mailRepository = mock(MailRepository.class);
-        when(mailRepositoryStore.select(any(String.class))).thenReturn(mailRepository);
+        when(mailRepositoryStore.select(any())).thenReturn(mailRepository);
 
         FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
                 .mailetName("Test")
+                .setProperty("repositoryPath", REPOSITORY_PATH)
                 .build();
         mailet.init(mailetConfig);
 
@@ -115,11 +120,12 @@ public class ToRepositoryTest {
     @Test
     public void serviceShouldGhostMailIfPassThroughSetToFalse() throws Exception {
         FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
-                .mailetName("Test")
-                .setProperty("passThrough", "false")
-                .build();
+            .mailetName("Test")
+            .setProperty("passThrough", "false")
+            .setProperty("repositoryPath", REPOSITORY_PATH)
+            .build();
         MailRepository mailRepository = mock(MailRepository.class);
-        when(mailRepositoryStore.select(any(String.class))).thenReturn(mailRepository);
+        when(mailRepositoryStore.select(any())).thenReturn(mailRepository);
         mailet.init(mailetConfig);
 
         mailet.service(message);
@@ -130,11 +136,12 @@ public class ToRepositoryTest {
     @Test
     public void serviceShouldNotGhostMailIfPassThroughSetToTrue() throws Exception {
         FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
-                .mailetName("Test")
-                .setProperty("passThrough", "true")
-                .build();
+            .mailetName("Test")
+            .setProperty("passThrough", "true")
+            .setProperty("repositoryPath", REPOSITORY_PATH)
+            .build();
         MailRepository mailRepository = mock(MailRepository.class);
-        when(mailRepositoryStore.select(any(String.class))).thenReturn(mailRepository);
+        when(mailRepositoryStore.select(any())).thenReturn(mailRepository);
         mailet.init(mailetConfig);
 
         mailet.service(message);

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/ToSenderDomainRepositoryTest.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/ToSenderDomainRepositoryTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/ToSenderDomainRepositoryTest.java
index b69807a..73ddca9 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/ToSenderDomainRepositoryTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/ToSenderDomainRepositoryTest.java
@@ -31,6 +31,7 @@ import javax.mail.MessagingException;
 
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.mailrepository.memory.MemoryMailRepository;
 import org.apache.james.mailrepository.mock.MockMailRepositoryStore;
 import org.apache.mailet.Mail;
@@ -43,17 +44,19 @@ import org.junit.jupiter.api.Test;
 class ToSenderDomainRepositoryTest {
 
     private static final String MEMORY_URL_PREFIX = "memory://var/mail/dlp/";
+    private static final MailRepositoryUrl JAMES_LOCAL_REPOSITORY_URL = MailRepositoryUrl.from("memory://var/mail/dlp/" + JAMES_LOCAL);
     private static final FakeMailetConfig DEFAULT_MAILET_CONFIG = FakeMailetConfig.builder()
         .mailetName("TestConfig")
         .setProperty("urlPrefix", MEMORY_URL_PREFIX)
         .build();
+
     private ToSenderDomainRepository mailet;
     private MockMailRepositoryStore mailRepositoryStore;
 
     @BeforeEach
     void setup() {
         mailRepositoryStore = new MockMailRepositoryStore();
-        mailRepositoryStore.add(MEMORY_URL_PREFIX + JAMES_LOCAL, new MemoryMailRepository());
+        mailRepositoryStore.add(JAMES_LOCAL_REPOSITORY_URL, new MemoryMailRepository());
         mailet = new ToSenderDomainRepository(mailRepositoryStore);
     }
 
@@ -84,7 +87,7 @@ class ToSenderDomainRepositoryTest {
             .sender(MailAddressFixture.SENDER)
             .build());
 
-        MailRepository mailRepository = mailRepositoryStore.select(MEMORY_URL_PREFIX + JAMES_LOCAL);
+        MailRepository mailRepository = mailRepositoryStore.select(JAMES_LOCAL_REPOSITORY_URL);
 
         assertThat(mailRepository.list())
             .extracting(mailRepository::retrieve)

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-api/pom.xml
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-api/pom.xml b/server/mailrepository/mailrepository-api/pom.xml
index 3a3e00f..cd972a7 100644
--- a/server/mailrepository/mailrepository-api/pom.xml
+++ b/server/mailrepository/mailrepository-api/pom.xml
@@ -39,6 +39,12 @@
         </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
+            <artifactId>apache-mailet-base</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
             <artifactId>james-server-core</artifactId>
             <scope>test</scope>
         </dependency>
@@ -48,6 +54,11 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>nl.jqno.equalsverifier</groupId>
+            <artifactId>equalsverifier</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.assertj</groupId>
             <artifactId>assertj-core</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/MailKey.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/MailKey.java b/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/MailKey.java
new file mode 100644
index 0000000..4f6f563
--- /dev/null
+++ b/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/MailKey.java
@@ -0,0 +1,66 @@
+/****************************************************************
+ * 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.mailrepository.api;
+
+import java.util.Objects;
+
+import org.apache.mailet.Mail;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+
+public class MailKey {
+    public static MailKey forMail(Mail mail) {
+        return new MailKey(mail.getName());
+    }
+
+    private final String value;
+
+    public MailKey(String value) {
+        Preconditions.checkNotNull(value);
+        this.value = value;
+    }
+
+    public String asString() {
+        return value;
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof MailKey) {
+            MailKey mailKey = (MailKey) o;
+
+            return Objects.equals(this.value, mailKey.value);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(value);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("value", value)
+            .toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/MailRepository.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/MailRepository.java b/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/MailRepository.java
index db5e267..b20d8b0 100644
--- a/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/MailRepository.java
+++ b/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/MailRepository.java
@@ -44,7 +44,7 @@ public interface MailRepository {
      * @param mc
      *            the mail message to store
      */
-    void store(Mail mc) throws MessagingException;
+    MailKey store(Mail mc) throws MessagingException;
 
     /**
      * List string keys of messages in repository.
@@ -52,7 +52,7 @@ public interface MailRepository {
      * @return an <code>Iterator</code> over the list of keys in the repository
      * 
      */
-    Iterator<String> list() throws MessagingException;
+    Iterator<MailKey> list() throws MessagingException;
 
     /**
      * Retrieves a message given a key. At the moment, keys can be obtained from
@@ -62,7 +62,7 @@ public interface MailRepository {
      *            the key of the message to retrieve
      * @return the mail corresponding to this key, null if none exists
      */
-    Mail retrieve(String key) throws MessagingException;
+    Mail retrieve(MailKey key) throws MessagingException;
 
     /**
      * Removes a specified message
@@ -87,7 +87,7 @@ public interface MailRepository {
      * @param key
      *            the key of the message to be removed from the repository
      */
-    void remove(String key) throws MessagingException;
+    void remove(MailKey key) throws MessagingException;
 
     /**
      * Removes all mails from this repository
@@ -100,11 +100,11 @@ public interface MailRepository {
      * @deprecated This method is implementation dependent, it has been moved to org.apache.james.mailrepository.lib.AbstractMailRepository
      */
     @Deprecated
-    boolean lock(String key) throws MessagingException;
+    boolean lock(MailKey key) throws MessagingException;
 
     /**
      * @deprecated This method is implementation dependent, it has been moved to org.apache.james.mailrepository.lib.AbstractMailRepository
      */
     @Deprecated
-    boolean unlock(String key) throws MessagingException;
+    boolean unlock(MailKey key) throws MessagingException;
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/MailRepositoryUrl.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/MailRepositoryUrl.java b/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/MailRepositoryUrl.java
new file mode 100644
index 0000000..c6e69ec
--- /dev/null
+++ b/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/MailRepositoryUrl.java
@@ -0,0 +1,86 @@
+/****************************************************************
+ * 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.mailrepository.api;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+
+public class MailRepositoryUrl {
+    public static final MailRepositoryUrl fromEncoded(String encodedUrl) throws UnsupportedEncodingException {
+        return new MailRepositoryUrl(URLDecoder.decode(encodedUrl, StandardCharsets.UTF_8.displayName()));
+    }
+
+    public static final MailRepositoryUrl from(String url) {
+        return new MailRepositoryUrl(url);
+    }
+
+    private final String value;
+    private final Protocol protocol;
+
+    private MailRepositoryUrl(String value) {
+        Preconditions.checkNotNull(value);
+        Preconditions.checkArgument(value.contains(":"), "':' is mandatory to delimit protocol");
+        this.value = value;
+        this.protocol = new Protocol(Splitter.on(':')
+            .splitToList(value)
+            .get(0));
+    }
+
+    public String asString() {
+        return value;
+    }
+
+    public String urlEncoded() throws UnsupportedEncodingException {
+        return URLEncoder.encode(value, StandardCharsets.UTF_8.displayName());
+    }
+
+    public Protocol getProtocol() {
+       return protocol;
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof MailRepositoryUrl) {
+            MailRepositoryUrl that = (MailRepositoryUrl) o;
+
+            return Objects.equals(this.value, that.value);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(value);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("value", value)
+            .toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/Protocol.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/Protocol.java b/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/Protocol.java
new file mode 100644
index 0000000..5cacf2a
--- /dev/null
+++ b/server/mailrepository/mailrepository-api/src/main/java/org/apache/james/mailrepository/api/Protocol.java
@@ -0,0 +1,60 @@
+/****************************************************************
+ * 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.mailrepository.api;
+
+import java.util.Objects;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+
+public class Protocol {
+    private final String value;
+
+    public Protocol(String value) {
+        Preconditions.checkNotNull(value);
+        this.value = value;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof Protocol) {
+            Protocol protocol = (Protocol) o;
+
+            return Objects.equals(this.value, protocol.value);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(value);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("value", value)
+            .toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/MailRepositoryContract.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/MailRepositoryContract.java b/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/MailRepositoryContract.java
index e6c9fc1..d56dee9 100644
--- a/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/MailRepositoryContract.java
+++ b/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/MailRepositoryContract.java
@@ -35,6 +35,7 @@ import javax.mail.internet.MimeMessage;
 
 import org.apache.james.core.MailAddress;
 import org.apache.james.core.builder.MimeMessageBuilder;
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.server.core.MailImpl;
 import org.apache.james.util.concurrency.ConcurrentTestRunner;
@@ -54,18 +55,21 @@ import com.google.common.hash.Hashing;
 public interface MailRepositoryContract {
 
     String TEST_ATTRIBUTE = "testAttribute";
+    MailKey MAIL_1 = new MailKey("mail1");
+    MailKey MAIL_2 = new MailKey("mail2");
+    MailKey UNKNOWN_KEY = new MailKey("random");
 
-    default MailImpl createMail(String name) throws MessagingException {
-        return createMail(name, "original body");
+    default MailImpl createMail(MailKey key) throws MessagingException {
+        return createMail(key, "original body");
     }
 
-    default MailImpl createMail(String name, String body) throws MessagingException {
+    default MailImpl createMail(MailKey key, String body) throws MessagingException {
         MimeMessage mailContent = generateMailContent(body);
         List<MailAddress> recipients = ImmutableList
             .of(new MailAddress("rec1@domain.com"),
                 new MailAddress("rec2@domain.com"));
         MailAddress sender = new MailAddress("sender@domain.com");
-        MailImpl mail = new MailImpl(name, sender, recipients, mailContent);
+        MailImpl mail = new MailImpl(key.asString(), sender, recipients, mailContent);
         mail.setAttribute(TEST_ATTRIBUTE, "testValue");
         return mail;
     }
@@ -105,8 +109,8 @@ public interface MailRepositoryContract {
     default void sizeShouldReturnMailCount() throws Exception {
         MailRepository testee = retrieveRepository();
 
-        testee.store(createMail("mail1"));
-        testee.store(createMail("mail2"));
+        testee.store(createMail(MAIL_1));
+        testee.store(createMail(MAIL_2));
 
         assertThat(testee.size()).isEqualTo(2L);
     }
@@ -115,9 +119,8 @@ public interface MailRepositoryContract {
     default void sizeShouldBeIncrementedByOneWhenDuplicates() throws Exception {
         MailRepository testee = retrieveRepository();
 
-        String key = "mail1";
-        testee.store(createMail(key));
-        testee.store(createMail(key));
+        testee.store(createMail(MAIL_1));
+        testee.store(createMail(MAIL_1));
 
         assertThat(testee.size()).isEqualTo(1L);
     }
@@ -126,9 +129,8 @@ public interface MailRepositoryContract {
     default void sizeShouldBeDecrementedByRemove() throws Exception {
         MailRepository testee = retrieveRepository();
 
-        String key = "mail1";
-        testee.store(createMail(key));
-        testee.remove(key);
+        testee.store(createMail(MAIL_1));
+        testee.remove(MAIL_1);
 
         assertThat(testee.size()).isEqualTo(0L);
     }
@@ -136,7 +138,7 @@ public interface MailRepositoryContract {
     @Test
     default void storeRegularMailShouldNotFail() throws Exception {
         MailRepository testee = retrieveRepository();
-        Mail mail = createMail("mail1");
+        Mail mail = createMail(MAIL_1);
 
         testee.store(mail);
     }
@@ -145,7 +147,7 @@ public interface MailRepositoryContract {
     default void storeBigMailShouldNotFail() throws Exception {
         MailRepository testee = retrieveRepository();
         String bigString = Strings.repeat("my mail is big 🐋", 1_000_000);
-        Mail mail = createMail("mail1", bigString);
+        Mail mail = createMail(MAIL_1, bigString);
 
         testee.store(mail);
     }
@@ -153,18 +155,17 @@ public interface MailRepositoryContract {
     @Test
     default void retrieveShouldGetStoredMail() throws Exception {
         MailRepository testee = retrieveRepository();
-        String key1 = "mail1";
-        Mail mail = createMail(key1);
+        Mail mail = createMail(MAIL_1);
 
         testee.store(mail);
 
-        assertThat(testee.retrieve(key1)).satisfies(actual -> checkMailEquality(actual, mail));
+        assertThat(testee.retrieve(MAIL_1)).satisfies(actual -> checkMailEquality(actual, mail));
     }
 
     @Test
     default void removeAllShouldRemoveStoredMails() throws Exception {
         MailRepository testee = retrieveRepository();
-        testee.store(createMail("name"));
+        testee.store(createMail(MAIL_1));
 
         testee.removeAll();
 
@@ -174,18 +175,17 @@ public interface MailRepositoryContract {
     @Test
     default void retrieveShouldReturnNullAfterRemoveAll() throws Exception {
         MailRepository testee = retrieveRepository();
-        String name = "name";
-        testee.store(createMail(name));
+        testee.store(createMail(MAIL_1));
 
         testee.removeAll();
 
-        assertThat(testee.retrieve(name)).isNull();
+        assertThat(testee.retrieve(MAIL_1)).isNull();
     }
 
     @Test
     default void removeAllShouldBeIdempotent() throws Exception {
         MailRepository testee = retrieveRepository();
-        testee.store(createMail("name"));
+        testee.store(createMail(MAIL_1));
 
         testee.removeAll();
         testee.removeAll();
@@ -196,7 +196,6 @@ public interface MailRepositoryContract {
     @Test
     default void removeAllShouldNotFailWhenEmpty() throws Exception {
         MailRepository testee = retrieveRepository();
-        testee.store(createMail("name"));
 
         testee.removeAll();
     }
@@ -204,22 +203,21 @@ public interface MailRepositoryContract {
     @Test
     default void retrieveShouldGetStoredEmojiMail() throws Exception {
         MailRepository testee = retrieveRepository();
-        String key1 = "mail1";
-        Mail mail = createMail(key1, "my content contains 🐋");
+        Mail mail = createMail(MAIL_1, "my content contains 🐋");
 
         testee.store(mail);
 
-        assertThat(testee.retrieve(key1).getMessage().getContent()).isEqualTo("my content contains 🐋");
+        assertThat(testee.retrieve(MAIL_1).getMessage().getContent()).isEqualTo("my content contains 🐋");
     }
 
     @Test
     default void retrieveBigMailShouldHaveSameHash() throws Exception {
         MailRepository testee = retrieveRepository();
         String bigString = Strings.repeat("my mail is big 🐋", 1_000_000);
-        Mail mail = createMail("mail1", bigString);
+        Mail mail = createMail(MAIL_1, bigString);
         testee.store(mail);
 
-        Mail actual = testee.retrieve("mail1");
+        Mail actual = testee.retrieve(MAIL_1);
 
         assertThat(Hashing.sha256().hashString((String)actual.getMessage().getContent(), StandardCharsets.UTF_8))
             .isEqualTo(Hashing.sha256().hashString(bigString, StandardCharsets.UTF_8));
@@ -229,8 +227,7 @@ public interface MailRepositoryContract {
     @Test
     default void retrieveShouldReturnAllMailProperties() throws Exception {
         MailRepository testee = retrieveRepository();
-        String key1 = "mail1";
-        MailImpl mail = createMail(key1);
+        MailImpl mail = createMail(MAIL_1);
         mail.setErrorMessage("Error message");
         mail.setRemoteAddr("172.5.2.3");
         mail.setRemoteHost("smtp@domain.com");
@@ -243,7 +240,7 @@ public interface MailRepositoryContract {
 
         testee.store(mail);
 
-        assertThat(testee.retrieve(key1)).satisfies(actual -> checkMailEquality(actual, mail));
+        assertThat(testee.retrieve(MAIL_1)).satisfies(actual -> checkMailEquality(actual, mail));
     }
 
     @Test
@@ -257,50 +254,45 @@ public interface MailRepositoryContract {
     default void retrievingUnknownMailShouldReturnNull() throws Exception {
         MailRepository testee = retrieveRepository();
 
-        assertThat(testee.retrieve("random")).isNull();
+        assertThat(testee.retrieve(UNKNOWN_KEY)).isNull();
     }
 
     @Test
     default void removingUnknownMailShouldHaveNoEffect() throws Exception {
         MailRepository testee = retrieveRepository();
 
-        testee.remove("random");
+        testee.remove(UNKNOWN_KEY);
     }
 
     @Test
     default void retrieveShouldReturnNullWhenKeyWasRemoved() throws Exception {
         MailRepository testee = retrieveRepository();
-        String key = "mail1";
-        testee.store(createMail(key));
+        testee.store(createMail(MAIL_1));
 
-        testee.remove(key);
+        testee.remove(MAIL_1);
 
-        assertThat(retrieveRepository().list()).doesNotContain(key);
-        assertThat(retrieveRepository().retrieve(key)).isNull();
+        assertThat(retrieveRepository().list()).doesNotContain(MAIL_1);
+        assertThat(retrieveRepository().retrieve(MAIL_1)).isNull();
     }
 
     @Test
     default void removeShouldnotAffectUnrelatedMails() throws Exception {
         MailRepository testee = retrieveRepository();
-        String key1 = "mail1";
-        testee.store(createMail(key1));
-        String key2 = "mail2";
-        testee.store(createMail(key2));
+        testee.store(createMail(MAIL_1));
+        testee.store(createMail(MAIL_2));
 
-        testee.remove(key1);
+        testee.remove(MAIL_1);
 
-        assertThat(retrieveRepository().list()).contains(key2);
+        assertThat(retrieveRepository().list()).contains(MAIL_2);
     }
 
     @Test
     default void removedMailsShouldNotBeListed() throws Exception {
         MailRepository testee = retrieveRepository();
 
-        String key1 = "mail1";
-        String key2 = "mail2";
-        String key3 = "mail3";
-        Mail mail1 = createMail(key1);
-        Mail mail2 = createMail(key2);
+        MailKey key3 = new MailKey("mail3");
+        Mail mail1 = createMail(MAIL_1);
+        Mail mail2 = createMail(MAIL_2);
         Mail mail3 = createMail(key3);
         retrieveRepository().store(mail1);
         retrieveRepository().store(mail2);
@@ -309,19 +301,17 @@ public interface MailRepositoryContract {
         testee.remove(ImmutableList.of(mail1, mail3));
 
         assertThat(retrieveRepository().list())
-            .contains(key2)
-            .doesNotContain(key1, key3);
+            .contains(MAIL_2)
+            .doesNotContain(MAIL_1, key3);
     }
 
     @Test
     default void removedMailShouldNotBeListed() throws Exception {
         MailRepository testee = retrieveRepository();
 
-        String key1 = "mail1";
-        String key2 = "mail2";
-        String key3 = "mail3";
-        Mail mail1 = createMail(key1);
-        Mail mail2 = createMail(key2);
+        MailKey key3 = new MailKey("mail3");
+        Mail mail1 = createMail(MAIL_1);
+        Mail mail2 = createMail(MAIL_2);
         Mail mail3 = createMail(key3);
         retrieveRepository().store(mail1);
         retrieveRepository().store(mail2);
@@ -330,15 +320,15 @@ public interface MailRepositoryContract {
         testee.remove(mail2);
 
         assertThat(retrieveRepository().list())
-            .contains(key1, key3)
-            .doesNotContain(key2);
+            .contains(MAIL_1, key3)
+            .doesNotContain(MAIL_2);
     }
 
     @Test
     default void removeShouldHaveNoEffectForUnknownMails() throws Exception {
         MailRepository testee = retrieveRepository();
 
-        testee.remove(ImmutableList.of(createMail("unknown")));
+        testee.remove(ImmutableList.of(createMail(UNKNOWN_KEY)));
 
         assertThat(retrieveRepository().list()).isEmpty();
     }
@@ -347,7 +337,7 @@ public interface MailRepositoryContract {
     default void removeShouldHaveNoEffectForUnknownMail() throws Exception {
         MailRepository testee = retrieveRepository();
 
-        testee.remove(createMail("unknown"));
+        testee.remove(createMail(UNKNOWN_KEY));
 
         assertThat(retrieveRepository().list()).isEmpty();
     }
@@ -355,40 +345,36 @@ public interface MailRepositoryContract {
     @Test
     default void listShouldReturnStoredMailsKeys() throws Exception {
         MailRepository testee = retrieveRepository();
-        String key1 = "mail1";
-        String key2 = "mail2";
-        testee.store(createMail(key1));
+        testee.store(createMail(MAIL_1));
 
-        testee.store(createMail(key2));
+        testee.store(createMail(MAIL_2));
 
-        assertThat(testee.list()).containsOnly(key1, key2);
+        assertThat(testee.list()).containsOnly(MAIL_1, MAIL_2);
     }
 
     @Test
     default void storingMessageWithSameKeyTwiceShouldUpdateMessageContent() throws Exception {
         MailRepository testee = retrieveRepository();
-        String key = "mail1";
-        testee.store(createMail(key));
+        testee.store(createMail(MAIL_1));
 
-        Mail updatedMail = createMail(key, "modified content");
+        Mail updatedMail = createMail(MAIL_1, "modified content");
         testee.store(updatedMail);
 
         assertThat(testee.list()).hasSize(1);
-        assertThat(testee.retrieve(key)).satisfies(actual -> checkMailEquality(actual, updatedMail));
+        assertThat(testee.retrieve(MAIL_1)).satisfies(actual -> checkMailEquality(actual, updatedMail));
     }
 
     @Test
     default void storingMessageWithSameKeyTwiceShouldUpdateMessageAttributes() throws Exception {
         MailRepository testee = retrieveRepository();
-        String key = "mail1";
-        Mail mail = createMail(key);
+        Mail mail = createMail(MAIL_1);
         testee.store(mail);
 
         mail.setAttribute(TEST_ATTRIBUTE, "newValue");
         testee.store(mail);
 
         assertThat(testee.list()).hasSize(1);
-        assertThat(testee.retrieve(key)).satisfies(actual -> checkMailEquality(actual, mail));
+        assertThat(testee.retrieve(MAIL_1)).satisfies(actual -> checkMailEquality(actual, mail));
     }
 
     @RepeatedTest(100)
@@ -397,7 +383,7 @@ public interface MailRepositoryContract {
         int nbKeys = 20;
         int nbIterations = 10;
         int threadCount = 10;
-        ConcurrentHashMap.KeySetView<String, Boolean> expectedResult = ConcurrentHashMap.newKeySet();
+        ConcurrentHashMap.KeySetView<MailKey, Boolean> expectedResult = ConcurrentHashMap.newKeySet();
         List<Object> locks = IntStream.range(0, 10)
             .boxed()
             .collect(Guavate.toImmutableList());
@@ -405,7 +391,7 @@ public interface MailRepositoryContract {
         Random random = new Random();
         ThrowingRunnable add = () -> {
             int keyIndex = computeKeyIndex(nbKeys, random.nextInt());
-            String key =  computeKey(keyIndex);
+            MailKey key =  computeKey(keyIndex);
             synchronized (locks.get(keyIndex)) {
                 testee.store(createMail(key));
                 expectedResult.add(key);
@@ -414,7 +400,7 @@ public interface MailRepositoryContract {
 
         ThrowingRunnable remove = () -> {
             int keyIndex = computeKeyIndex(nbKeys, random.nextInt());
-            String key =  computeKey(keyIndex);
+            MailKey key =  computeKey(keyIndex);
             synchronized (locks.get(keyIndex)) {
                 testee.remove(key);
                 expectedResult.remove(key);
@@ -434,8 +420,8 @@ public interface MailRepositoryContract {
         assertThat(testee.list()).containsOnlyElementsOf(expectedResult);
     }
 
-    default String computeKey(int keyIndex) {
-        return "mail" + keyIndex;
+    default MailKey computeKey(int keyIndex) {
+        return new MailKey("mail" + keyIndex);
     }
 
     default int computeKeyIndex(int nbKeys, Integer i) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/api/MailKeyTest.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/api/MailKeyTest.java b/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/api/MailKeyTest.java
new file mode 100644
index 0000000..01079b7
--- /dev/null
+++ b/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/api/MailKeyTest.java
@@ -0,0 +1,54 @@
+/****************************************************************
+ * 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.mailrepository.api;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.apache.mailet.base.test.FakeMail;
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class MailKeyTest {
+
+    @Test
+    public void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(MailKey.class)
+            .verify();
+    }
+
+    @Test
+    public void forMailShouldBeBasedOnTheName() throws Exception {
+        String name = "toto";
+        assertThat(
+            MailKey.forMail(FakeMail.builder()
+                .name(name)
+                .build()))
+            .isEqualTo(new MailKey(name));
+    }
+
+    @Test
+    public void constructorShouldThrowWhenNull() {
+        assertThatThrownBy(() -> new MailKey(null))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/api/MailRepositoryUrlTest.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/api/MailRepositoryUrlTest.java b/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/api/MailRepositoryUrlTest.java
new file mode 100644
index 0000000..7b14795
--- /dev/null
+++ b/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/api/MailRepositoryUrlTest.java
@@ -0,0 +1,71 @@
+/****************************************************************
+ * 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.mailrepository.api;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class MailRepositoryUrlTest {
+    @Test
+    public void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(MailRepositoryUrl.class)
+            .verify();
+    }
+
+    @Test
+    public void constructorShouldThrowWhenNull() {
+        assertThatThrownBy(() -> MailRepositoryUrl.from(null))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void constructorShouldThrowWhenNoSeparator() {
+        assertThatThrownBy(() -> MailRepositoryUrl.from("invalid"))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    public void getProtocolShouldReturnValue() {
+        assertThat(MailRepositoryUrl.from("proto://abc").getProtocol())
+            .isEqualTo(new Protocol("proto"));
+    }
+
+    @Test
+    public void getProtocolShouldReturnValueWhenEmpty() {
+        assertThat(MailRepositoryUrl.from("://abc").getProtocol())
+            .isEqualTo(new Protocol(""));
+    }
+
+    @Test
+    public void fromEncodedShouldReturnDecodedValue() throws Exception {
+        assertThat(MailRepositoryUrl.fromEncoded("url%3A%2F%2FmyRepo"))
+            .isEqualTo(MailRepositoryUrl.from("url://myRepo"));
+    }
+
+    @Test
+    public void encodedValueShouldEncodeUnderlyingValue() throws Exception {
+        assertThat(MailRepositoryUrl.from("url://myRepo").urlEncoded())
+            .isEqualTo("url%3A%2F%2FmyRepo");
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/api/ProtocolTest.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/api/ProtocolTest.java b/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/api/ProtocolTest.java
new file mode 100644
index 0000000..adccac0
--- /dev/null
+++ b/server/mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/api/ProtocolTest.java
@@ -0,0 +1,42 @@
+/****************************************************************
+ * 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.mailrepository.api;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class ProtocolTest {
+
+    @Test
+    public void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(Protocol.class)
+            .verify();
+    }
+
+    @Test
+    public void constructorShouldThrowWhenNull() {
+        assertThatThrownBy(() -> new Protocol(null))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepository.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepository.java b/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepository.java
index 774d904..c2e449d 100644
--- a/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepository.java
+++ b/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepository.java
@@ -39,7 +39,9 @@ import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.james.blob.api.BlobId;
 import org.apache.james.blob.api.ObjectStore;
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepository;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.util.BodyOffsetInputStream;
 import org.apache.james.util.CompletableFutureUtil;
 import org.apache.james.util.FluentFutureStream;
@@ -50,13 +52,13 @@ import com.google.common.primitives.Bytes;
 
 public class CassandraMailRepository implements MailRepository {
 
-    private final String url;
+    private final MailRepositoryUrl url;
     private final CassandraMailRepositoryKeysDAO keysDAO;
     private final CassandraMailRepositoryCountDAO countDAO;
     private final CassandraMailRepositoryMailDAO mailDAO;
     private final ObjectStore objectStore;
 
-    public CassandraMailRepository(String url, CassandraMailRepositoryKeysDAO keysDAO, CassandraMailRepositoryCountDAO countDAO, CassandraMailRepositoryMailDAO mailDAO, ObjectStore objectStore) {
+    public CassandraMailRepository(MailRepositoryUrl url, CassandraMailRepositoryKeysDAO keysDAO, CassandraMailRepositoryCountDAO countDAO, CassandraMailRepositoryMailDAO mailDAO, ObjectStore objectStore) {
         this.url = url;
         this.keysDAO = keysDAO;
         this.countDAO = countDAO;
@@ -65,8 +67,9 @@ public class CassandraMailRepository implements MailRepository {
     }
 
     @Override
-    public void store(Mail mail) throws MessagingException {
+    public MailKey store(Mail mail) throws MessagingException {
         try {
+            MailKey mailKey = MailKey.forMail(mail);
             Pair<byte[], byte[]> splitHeaderBody = splitHeaderBody(mail.getMessage());
 
             CompletableFuture<Pair<BlobId, BlobId>> blobIds = CompletableFutureUtil.combine(
@@ -78,8 +81,9 @@ public class CassandraMailRepository implements MailRepository {
                 mailDAO.store(url, mail, pair.getLeft(), pair.getRight())))
                 .thenCompose(any -> CompletableFuture.allOf(
                     countDAO.increment(url),
-                    keysDAO.store(url, mail.getName())))
+                    keysDAO.store(url, mailKey)))
                 .join();
+            return mailKey;
         } catch (IOException e) {
             throw new MessagingException("Exception while storing mail", e);
         }
@@ -137,14 +141,14 @@ public class CassandraMailRepository implements MailRepository {
     }
 
     @Override
-    public Iterator<String> list() {
+    public Iterator<MailKey> list() {
         return keysDAO.list(url)
             .join()
             .iterator();
     }
 
     @Override
-    public Mail retrieve(String key) {
+    public Mail retrieve(MailKey key) {
         return CompletableFutureUtil
             .unwrap(mailDAO.read(url, key)
                 .thenApply(optional -> optional.map(this::toMail)))
@@ -173,23 +177,23 @@ public class CassandraMailRepository implements MailRepository {
 
     @Override
     public void remove(Mail mail) {
-        removeAsync(mail.getName()).join();
+        removeAsync(MailKey.forMail(mail)).join();
     }
 
     @Override
     public void remove(Collection<Mail> toRemove) {
         FluentFutureStream.of(toRemove.stream()
-            .map(Mail::getName)
+            .map(MailKey::forMail)
             .map(this::removeAsync))
             .join();
     }
 
     @Override
-    public void remove(String key) {
+    public void remove(MailKey key) {
         removeAsync(key).join();
     }
 
-    public CompletableFuture<Void> removeAsync(String key) {
+    public CompletableFuture<Void> removeAsync(MailKey key) {
         return CompletableFuture.allOf(
             keysDAO.remove(url, key),
             countDAO.decrement(url))
@@ -210,12 +214,12 @@ public class CassandraMailRepository implements MailRepository {
     }
 
     @Override
-    public boolean lock(String key) {
+    public boolean lock(MailKey key) {
         return false;
     }
 
     @Override
-    public boolean unlock(String key) {
+    public boolean unlock(MailKey key) {
         return false;
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryCountDAO.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryCountDAO.java b/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryCountDAO.java
index eab17cf..cc2d4ea 100644
--- a/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryCountDAO.java
+++ b/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryCountDAO.java
@@ -35,6 +35,7 @@ import java.util.concurrent.CompletableFuture;
 import javax.inject.Inject;
 
 import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 
 import com.datastax.driver.core.PreparedStatement;
 import com.datastax.driver.core.Row;
@@ -74,19 +75,19 @@ public class CassandraMailRepositoryCountDAO {
             .where(eq(REPOSITORY_NAME, bindMarker(REPOSITORY_NAME))));
     }
 
-    public CompletableFuture<Void> increment(String url) {
+    public CompletableFuture<Void> increment(MailRepositoryUrl url) {
         return executor.executeVoid(increment.bind()
-            .setString(REPOSITORY_NAME, url));
+            .setString(REPOSITORY_NAME, url.asString()));
     }
 
-    public CompletableFuture<Void> decrement(String url) {
+    public CompletableFuture<Void> decrement(MailRepositoryUrl url) {
         return executor.executeVoid(decrement.bind()
-            .setString(REPOSITORY_NAME, url));
+            .setString(REPOSITORY_NAME, url.asString()));
     }
 
-    public CompletableFuture<Long> getCount(String url) {
+    public CompletableFuture<Long> getCount(MailRepositoryUrl url) {
         return executor.executeSingleRow(select.bind()
-                .setString(REPOSITORY_NAME, url))
+                .setString(REPOSITORY_NAME, url.asString()))
             .thenApply(this::toCount);
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryKeysDAO.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryKeysDAO.java b/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryKeysDAO.java
index 9d8a8f4..8b01f15 100644
--- a/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryKeysDAO.java
+++ b/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryKeysDAO.java
@@ -35,6 +35,8 @@ import javax.inject.Inject;
 
 import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
 import org.apache.james.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.mailrepository.api.MailKey;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 
 import com.datastax.driver.core.PreparedStatement;
 import com.datastax.driver.core.Session;
@@ -76,22 +78,22 @@ public class CassandraMailRepositoryKeysDAO {
             .value(MAIL_KEY, bindMarker(MAIL_KEY)));
     }
 
-    public CompletableFuture<Void> store(String url, String key) {
+    public CompletableFuture<Void> store(MailRepositoryUrl url, MailKey key) {
         return executor.executeVoid(insertKey.bind()
-            .setString(REPOSITORY_NAME, url)
-            .setString(MAIL_KEY, key));
+            .setString(REPOSITORY_NAME, url.asString())
+            .setString(MAIL_KEY, key.asString()));
     }
 
-    public CompletableFuture<Stream<String>> list(String url) {
+    public CompletableFuture<Stream<MailKey>> list(MailRepositoryUrl url) {
         return executor.execute(listKeys.bind()
-            .setString(REPOSITORY_NAME, url))
+            .setString(REPOSITORY_NAME, url.asString()))
             .thenApply(cassandraUtils::convertToStream)
-            .thenApply(stream -> stream.map(row -> row.getString(MAIL_KEY)));
+            .thenApply(stream -> stream.map(row -> new MailKey(row.getString(MAIL_KEY))));
     }
 
-    public CompletableFuture<Void> remove(String url, String key) {
+    public CompletableFuture<Void> remove(MailRepositoryUrl url, MailKey key) {
         return executor.executeVoid(deleteKey.bind()
-            .setString(REPOSITORY_NAME, url)
-            .setString(MAIL_KEY, key));
+            .setString(REPOSITORY_NAME, url.asString())
+            .setString(MAIL_KEY, key.asString()));
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryMailDAO.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryMailDAO.java b/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryMailDAO.java
index 4a8fb04..b106437 100644
--- a/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryMailDAO.java
+++ b/server/mailrepository/mailrepository-cassandra/src/main/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryMailDAO.java
@@ -68,6 +68,8 @@ import org.apache.james.backends.cassandra.init.CassandraTypesProvider;
 import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
 import org.apache.james.blob.api.BlobId;
 import org.apache.james.core.MailAddress;
+import org.apache.james.mailrepository.api.MailKey;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.james.server.core.MailImpl;
 import org.apache.james.util.streams.Iterators;
 import org.apache.mailet.Mail;
@@ -136,9 +138,9 @@ public class CassandraMailRepositoryMailDAO {
                 .and(eq(MAIL_KEY, bindMarker(MAIL_KEY))));
     }
 
-    public CompletableFuture<Void> store(String url, Mail mail, BlobId headerId, BlobId bodyId) throws MessagingException {
+    public CompletableFuture<Void> store(MailRepositoryUrl url, Mail mail, BlobId headerId, BlobId bodyId) throws MessagingException {
         return executor.executeVoid(insertMail.bind()
-            .setString(REPOSITORY_NAME, url)
+            .setString(REPOSITORY_NAME, url.asString())
             .setString(MAIL_KEY, mail.getName())
             .setString(HEADER_BLOB_ID, headerId.asString())
             .setString(BODY_BLOB_ID, bodyId.asString())
@@ -157,16 +159,16 @@ public class CassandraMailRepositoryMailDAO {
         );
     }
 
-    public CompletableFuture<Void> remove(String url, String key) {
+    public CompletableFuture<Void> remove(MailRepositoryUrl url, MailKey key) {
         return executor.executeVoid(deleteMail.bind()
-            .setString(REPOSITORY_NAME, url)
-            .setString(MAIL_KEY, key));
+            .setString(REPOSITORY_NAME, url.asString())
+            .setString(MAIL_KEY, key.asString()));
     }
 
-    public CompletableFuture<Optional<MailDTO>> read(String url, String key) {
+    public CompletableFuture<Optional<MailDTO>> read(MailRepositoryUrl url, MailKey key) {
         return executor.executeSingleRow(selectMail.bind()
-            .setString(REPOSITORY_NAME, url)
-            .setString(MAIL_KEY, key))
+            .setString(REPOSITORY_NAME, url.asString())
+            .setString(MAIL_KEY, key.asString()))
             .thenApply(rowOptional -> rowOptional.map(this::toMail));
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryCountDAOTest.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryCountDAOTest.java b/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryCountDAOTest.java
index 9f3a7e3..e6ba146 100644
--- a/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryCountDAOTest.java
+++ b/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryCountDAOTest.java
@@ -23,6 +23,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 import org.apache.james.backends.cassandra.CassandraCluster;
 import org.apache.james.backends.cassandra.DockerCassandraExtension;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -30,8 +31,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
 
 @ExtendWith(DockerCassandraExtension.class)
 public class CassandraMailRepositoryCountDAOTest {
-    static final String URL = "url";
-    static final String URL2 = "url2";
+    static final MailRepositoryUrl URL = MailRepositoryUrl.from("proto://url");
+    static final MailRepositoryUrl URL2 = MailRepositoryUrl.from("proto://url2");
 
     CassandraCluster cassandra;
     CassandraMailRepositoryCountDAO testee;

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryKeysDAOTest.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryKeysDAOTest.java b/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryKeysDAOTest.java
index b261d48..e22b234 100644
--- a/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryKeysDAOTest.java
+++ b/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryKeysDAOTest.java
@@ -24,6 +24,8 @@ import static org.assertj.core.api.Assertions.assertThat;
 import org.apache.james.backends.cassandra.CassandraCluster;
 import org.apache.james.backends.cassandra.DockerCassandraExtension;
 import org.apache.james.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.mailrepository.api.MailKey;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -32,12 +34,11 @@ import org.junit.jupiter.api.extension.ExtendWith;
 @ExtendWith(DockerCassandraExtension.class)
 public class CassandraMailRepositoryKeysDAOTest {
 
-
-    static final String URL = "url";
-    static final String URL2 = "url2";
-    static final String KEY_1 = "key1";
-    static final String KEY_2 = "key2";
-    static final String KEY_3 = "key3";
+    static final MailRepositoryUrl URL = MailRepositoryUrl.from("proto://url");
+    static final MailRepositoryUrl URL2 = MailRepositoryUrl.from("proto://url2");
+    static final MailKey KEY_1 = new MailKey("key1");
+    static final MailKey KEY_2 = new MailKey("key2");
+    static final MailKey KEY_3 = new MailKey("key3");
 
     CassandraCluster cassandra;
     CassandraMailRepositoryKeysDAO testee;

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryMailDAOTest.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryMailDAOTest.java b/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryMailDAOTest.java
index eb8a846..b6f738f 100644
--- a/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryMailDAOTest.java
+++ b/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryMailDAOTest.java
@@ -26,6 +26,8 @@ import org.apache.james.backends.cassandra.CassandraCluster;
 import org.apache.james.backends.cassandra.DockerCassandraExtension;
 import org.apache.james.blob.api.BlobId;
 import org.apache.james.blob.api.TestBlobId;
+import org.apache.james.mailrepository.api.MailKey;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.apache.mailet.Mail;
 import org.apache.mailet.PerRecipientHeaders;
 import org.apache.mailet.base.MailAddressFixture;
@@ -39,8 +41,8 @@ import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
 @ExtendWith(DockerCassandraExtension.class)
 public class CassandraMailRepositoryMailDAOTest {
 
-    static final String URL = "url";
-    static final String KEY_1 = "key1";
+    static final MailRepositoryUrl URL = MailRepositoryUrl.from("proto://url");
+    static final MailKey KEY_1 = new MailKey("key1");
     static final TestBlobId.Factory BLOB_ID_FACTORY = new TestBlobId.Factory();
 
     CassandraCluster cassandra;
@@ -79,7 +81,7 @@ public class CassandraMailRepositoryMailDAOTest {
 
         testee.store(URL,
             FakeMail.builder()
-                .name(KEY_1)
+                .name(KEY_1.asString())
                 .sender(MailAddressFixture.SENDER)
                 .recipients(MailAddressFixture.RECIPIENT1, MailAddressFixture.RECIPIENT2)
                 .errorMessage(errorMessage)
@@ -99,7 +101,7 @@ public class CassandraMailRepositoryMailDAOTest {
         assertAll(
             () -> assertThat(mailDTO.getBodyBlobId()).isEqualTo(blobIdBody),
             () -> assertThat(mailDTO.getHeaderBlobId()).isEqualTo(blobIdHeader),
-            () -> assertThat(partialMail.getName()).isEqualTo(KEY_1),
+            () -> assertThat(partialMail.getName()).isEqualTo(KEY_1.asString()),
             () -> assertThat(partialMail.getErrorMessage()).isEqualTo(errorMessage),
             () -> assertThat(partialMail.getState()).isEqualTo(state),
             () -> assertThat(partialMail.getRemoteAddr()).isEqualTo(remoteAddr),
@@ -121,7 +123,7 @@ public class CassandraMailRepositoryMailDAOTest {
 
         testee.store(URL,
             FakeMail.builder()
-                .name(KEY_1)
+                .name(KEY_1.asString())
                 .build(),
             blobIdHeader,
             blobIdBody)
@@ -133,7 +135,7 @@ public class CassandraMailRepositoryMailDAOTest {
         assertAll(
             () -> assertThat(mailDTO.getBodyBlobId()).isEqualTo(blobIdBody),
             () -> assertThat(mailDTO.getHeaderBlobId()).isEqualTo(blobIdHeader),
-            () -> assertThat(partialMail.getName()).isEqualTo(KEY_1));
+            () -> assertThat(partialMail.getName()).isEqualTo(KEY_1.asString()));
     }
 
     @Test
@@ -143,7 +145,7 @@ public class CassandraMailRepositoryMailDAOTest {
 
         testee.store(URL,
             FakeMail.builder()
-                .name(KEY_1)
+                .name(KEY_1.asString())
                 .build(),
             blobIdHeader,
             blobIdBody)

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryTest.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryTest.java b/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryTest.java
index 31abe57..bfbed2c 100644
--- a/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryTest.java
+++ b/server/mailrepository/mailrepository-cassandra/src/test/java/org/apache/james/mailrepository/cassandra/CassandraMailRepositoryTest.java
@@ -28,6 +28,7 @@ import org.apache.james.blob.cassandra.CassandraBlobModule;
 import org.apache.james.blob.cassandra.CassandraBlobsDAO;
 import org.apache.james.mailrepository.MailRepositoryContract;
 import org.apache.james.mailrepository.api.MailRepository;
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
@@ -36,7 +37,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
 
 @ExtendWith(DockerCassandraExtension.class)
 class CassandraMailRepositoryTest implements MailRepositoryContract {
-    static final String URL = "url";
+    static final MailRepositoryUrl URL = MailRepositoryUrl.from("proto://url");
     static final CassandraBlobId.Factory BLOB_ID_FACTORY = new CassandraBlobId.Factory();
 
     CassandraMailRepository cassandraMailRepository;

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/mailrepository/mailrepository-memory/src/main/java/org/apache/james/mailrepository/memory/MemoryMailRepository.java
----------------------------------------------------------------------
diff --git a/server/mailrepository/mailrepository-memory/src/main/java/org/apache/james/mailrepository/memory/MemoryMailRepository.java b/server/mailrepository/mailrepository-memory/src/main/java/org/apache/james/mailrepository/memory/MemoryMailRepository.java
index 6d83596..46ee7d7 100644
--- a/server/mailrepository/mailrepository-memory/src/main/java/org/apache/james/mailrepository/memory/MemoryMailRepository.java
+++ b/server/mailrepository/mailrepository-memory/src/main/java/org/apache/james/mailrepository/memory/MemoryMailRepository.java
@@ -23,54 +23,57 @@ import java.util.Collection;
 import java.util.Iterator;
 import java.util.concurrent.ConcurrentHashMap;
 
+import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.mailet.Mail;
 
 public class MemoryMailRepository implements MailRepository {
 
-    private final ConcurrentHashMap<String, Mail> mails;
+    private final ConcurrentHashMap<MailKey, Mail> mails;
 
     public MemoryMailRepository() {
         mails = new ConcurrentHashMap<>();
     }
 
     @Override
-    public void store(Mail mail) {
-        mails.put(mail.getName(), mail);
+    public MailKey store(Mail mail) {
+        MailKey mailKey = MailKey.forMail(mail);
+        mails.put(mailKey, mail);
+        return mailKey;
     }
 
     @Override
-    public Iterator<String> list() {
+    public Iterator<MailKey> list() {
         return mails.keySet().iterator();
     }
 
     @Override
-    public Mail retrieve(String key) {
+    public Mail retrieve(MailKey key) {
         return mails.get(key);
     }
 
     @Override
     public void remove(Mail mail) {
-        mails.remove(mail.getName());
+        mails.remove(MailKey.forMail(mail));
     }
 
     @Override
     public void remove(Collection<Mail> toRemove) {
-        toRemove.stream().map(Mail::getName).forEach(this::remove);
+        toRemove.stream().map(MailKey::forMail).forEach(this::remove);
     }
 
     @Override
-    public void remove(String key) {
+    public void remove(MailKey key) {
         mails.remove(key);
     }
 
     @Override
-    public boolean lock(String key) {
+    public boolean lock(MailKey key) {
         return false;
     }
 
     @Override
-    public boolean unlock(String key) {
+    public boolean unlock(MailKey key) {
         return false;
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/ExtendedMailRepositoryResponse.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/ExtendedMailRepositoryResponse.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/ExtendedMailRepositoryResponse.java
index c8258be..a1775a0 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/ExtendedMailRepositoryResponse.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/ExtendedMailRepositoryResponse.java
@@ -19,11 +19,13 @@
 
 package org.apache.james.webadmin.dto;
 
+import org.apache.james.mailrepository.api.MailRepositoryUrl;
+
 public class ExtendedMailRepositoryResponse extends MailRepositoryResponse {
 
     private final long size;
 
-    public ExtendedMailRepositoryResponse(String repository, long size) {
+    public ExtendedMailRepositoryResponse(MailRepositoryUrl repository, long size) {
         super(repository);
         this.size = size;
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/946c68be/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailKey.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailKey.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailKey.java
deleted file mode 100644
index 9d44dae..0000000
--- a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailKey.java
+++ /dev/null
@@ -1,50 +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.webadmin.dto;
-
-import java.util.Objects;
-
-public class MailKey {
-
-    private final String repository;
-
-    public MailKey(String mailKey) {
-        this.repository = mailKey;
-    }
-
-    public String getMailKey() {
-        return repository;
-    }
-
-    @Override
-    public final boolean equals(Object o) {
-        if (o instanceof MailKey) {
-            MailKey mailKey = (MailKey) o;
-
-            return Objects.equals(this.repository, mailKey.repository);
-        }
-        return false;
-    }
-
-    @Override
-    public final int hashCode() {
-        return Objects.hash(repository);
-    }
-}


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