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("[email protected]"),
                 new MailAddress("[email protected]"));
         MailAddress sender = new MailAddress("[email protected]");
-        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("[email protected]");
@@ -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: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to