JAMES-1742 Introduce the JMAP download endpoint

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

Branch: refs/heads/master
Commit: 4df388be2d7b867f472b7c094070e21eb397cd8f
Parents: 3848954
Author: Antoine Duprat <adup...@linagora.com>
Authored: Wed May 25 16:26:24 2016 +0200
Committer: Antoine Duprat <adup...@linagora.com>
Committed: Mon Jun 6 13:05:37 2016 +0200

----------------------------------------------------------------------
 .../apache/james/http/jetty/Configuration.java  |  20 ++-
 .../james/http/jetty/ConfigurationTest.java     |  49 +++++-
 server/pom.xml                                  |   6 +-
 .../cassandra-jmap-integration-testing/pom.xml  |   4 +-
 ...CassandraSetMailboxesMethodCucumberTest.java |  31 ----
 .../CassandraSetMailboxesMethodStepdefs.java    |  60 -------
 .../cucumber/CassandraDownloadCucumberTest.java |  31 ++++
 ...CassandraSetMailboxesMethodCucumberTest.java |  31 ++++
 .../cassandra/cucumber/CassandraStepdefs.java   |  74 ++++++++
 .../jmap-integration-testing-common/pom.xml     |   5 +
 .../integration/SetMailboxesMethodStepdefs.java | 174 -------------------
 .../integration/cucumber/DownloadStepdefs.java  | 105 +++++++++++
 .../integration/cucumber/MainStepdefs.java      |  48 +++++
 .../cucumber/SetMailboxesMethodStepdefs.java    | 150 ++++++++++++++++
 .../integration/cucumber/UserStepdefs.java      |  54 ++++++
 .../resources/cucumber/DownloadEndpoint.feature |  31 ++++
 .../memory-jmap-integration-testing/pom.xml     |   2 +-
 .../MemorySetMailboxesMethodCucumberTest.java   |  31 ----
 .../MemorySetMailboxesMethodStepdefs.java       |  54 ------
 .../cucumber/MemoryDownloadCucumberTest.java    |  31 ++++
 .../MemorySetMailboxesMethodCucumberTest.java   |  31 ++++
 .../jmap/memory/cucumber/MemoryStepdefs.java    |  60 +++++++
 .../org/apache/james/jmap/DownloadServlet.java  |  44 +++++
 .../java/org/apache/james/jmap/JMAPServer.java  |   7 +-
 24 files changed, 773 insertions(+), 360 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/container/jetty/src/main/java/org/apache/james/http/jetty/Configuration.java
----------------------------------------------------------------------
diff --git 
a/server/container/jetty/src/main/java/org/apache/james/http/jetty/Configuration.java
 
b/server/container/jetty/src/main/java/org/apache/james/http/jetty/Configuration.java
index 362a1a2..f638e9f 100644
--- 
a/server/container/jetty/src/main/java/org/apache/james/http/jetty/Configuration.java
+++ 
b/server/container/jetty/src/main/java/org/apache/james/http/jetty/Configuration.java
@@ -43,6 +43,7 @@ public class Configuration {
     public static class Builder {
         
         private static final Range<Integer> VALID_PORT_RANGE = Range.closed(1, 
65535);
+        private static final String TEMPLATE_LEVEL1 = "/*";
 
         private final ImmutableMap.Builder<String, Object> mappings;
         private final ImmutableListMultimap.Builder<String, Object> filters;
@@ -104,15 +105,28 @@ public class Configuration {
         }
         
         public ServletBinder serve(String mappingUrl) {
-            Preconditions.checkNotNull(mappingUrl);
-            Preconditions.checkArgument(!mappingUrl.trim().isEmpty());
+            urlPreconditions(mappingUrl);
             return new ServletBinder(mappingUrl);
         }
         
+        public ServletBinder serveAsOneLevelTemplate(String mappingUrl) {
+            urlPreconditions(mappingUrl);
+            return new ServletBinder(mappingUrl + TEMPLATE_LEVEL1);
+        }
+        
         public FilterBinder filter(String mappingUrl) {
+            urlPreconditions(mappingUrl);
+            return new FilterBinder(mappingUrl);
+        }
+        
+        public FilterBinder filterAsOneLevelTemplate(String mappingUrl) {
+            urlPreconditions(mappingUrl);
+            return new FilterBinder(mappingUrl + TEMPLATE_LEVEL1);
+        }
+
+        private void urlPreconditions(String mappingUrl) {
             Preconditions.checkNotNull(mappingUrl);
             Preconditions.checkArgument(!mappingUrl.trim().isEmpty());
-            return new FilterBinder(mappingUrl);
         }
 
         public Builder port(int port) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/container/jetty/src/test/java/org/apache/james/http/jetty/ConfigurationTest.java
----------------------------------------------------------------------
diff --git 
a/server/container/jetty/src/test/java/org/apache/james/http/jetty/ConfigurationTest.java
 
b/server/container/jetty/src/test/java/org/apache/james/http/jetty/ConfigurationTest.java
index 1cc00bb..3311873 100644
--- 
a/server/container/jetty/src/test/java/org/apache/james/http/jetty/ConfigurationTest.java
+++ 
b/server/container/jetty/src/test/java/org/apache/james/http/jetty/ConfigurationTest.java
@@ -54,12 +54,15 @@ public class ConfigurationTest {
                 .and(anotherFilter).only()
                 .filter("/456")
                 .with(spyFilter).only()
+                .serveAsOneLevelTemplate("/level")
+                .with(Ok200.class)
                 .build();
         assertThat(testee.getPort()).isPresent().contains(2000);
         assertThat(testee.getMappings())
-            .hasSize(2)
+            .hasSize(3)
             .containsEntry("/abc", Ok200.class)
-            .containsEntry("/def", bad400);
+            .containsEntry("/def", bad400)
+            .containsEntry("/level/*", Ok200.class);
         assertThat(testee.getFilters().asMap())
             .hasSize(2)
             .containsEntry("/123", ImmutableList.of(CoolFilter.class, 
anotherFilter))
@@ -121,6 +124,21 @@ public class ConfigurationTest {
         assertThatThrownBy(() -> 
Configuration.builder().serve("/").with((Class<? extends 
Servlet>)null)).isInstanceOf(NullPointerException.class);
     }
     
+    @Test
+    public void shouldNotAllowNullServletAsOneLevelTemplateMappingUrl() {
+        assertThatThrownBy(() -> 
Configuration.builder().serveAsOneLevelTemplate(null)).isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void shouldNotAllowEmptyServletAsOneLevelTemplateMappingUrl() {
+        assertThatThrownBy(() -> 
Configuration.builder().serveAsOneLevelTemplate("")).isInstanceOf(IllegalArgumentException.class);
+    }
+
+
+    @Test
+    public void 
shouldNotAllowWhitespaceOnlyServletAsOneLevelTemplateMappingUrl() {
+        assertThatThrownBy(() -> 
Configuration.builder().serveAsOneLevelTemplate("    
")).isInstanceOf(IllegalArgumentException.class);
+    }
 
     @Test
     public void shouldNotAllowNullFilterMappingUrl() {
@@ -148,4 +166,31 @@ public class ConfigurationTest {
     public void shouldNotAllowNullFilterClassname() {
         assertThatThrownBy(() -> 
Configuration.builder().filter("/").with((Class<? extends 
Filter>)null)).isInstanceOf(NullPointerException.class);
     }
+
+    @Test
+    public void shouldNotAllowNullFilterAsOneLevelTemplateMappingUrl() {
+        assertThatThrownBy(() -> 
Configuration.builder().filterAsOneLevelTemplate(null)).isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void shouldNotAllowEmptyFilterAsOneLevelTemplateMappingUrl() {
+        assertThatThrownBy(() -> 
Configuration.builder().filterAsOneLevelTemplate("")).isInstanceOf(IllegalArgumentException.class);
+    }
+
+
+    @Test
+    public void 
shouldNotAllowWhitespaceOnlyFilterAsOneLevelTemplateMappingUrl() {
+        assertThatThrownBy(() -> 
Configuration.builder().filterAsOneLevelTemplate("    
")).isInstanceOf(IllegalArgumentException.class);
+    }
+    
+
+    @Test
+    public void shouldNotAllowNullFilterAsOneLevelTemplate() {
+        assertThatThrownBy(() -> 
Configuration.builder().filterAsOneLevelTemplate("/").with((Filter)null)).isInstanceOf(NullPointerException.class);
+    }
+    
+    @Test
+    public void shouldNotAllowNullFilterClassnameAsOneLevelTemplate() {
+        assertThatThrownBy(() -> 
Configuration.builder().filterAsOneLevelTemplate("/").with((Class<? extends 
Filter>)null)).isInstanceOf(NullPointerException.class);
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/pom.xml
----------------------------------------------------------------------
diff --git a/server/pom.xml b/server/pom.xml
index 037263f..6f582bb 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -1428,6 +1428,11 @@
             </dependency>
             <dependency>
                 <groupId>info.cukes</groupId>
+                <artifactId>cucumber-guice</artifactId>
+                <version>${cucumber.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>info.cukes</groupId>
                 <artifactId>cucumber-java</artifactId>
                 <version>${cucumber.version}</version>
             </dependency>
@@ -1440,7 +1445,6 @@
                 <groupId>info.cukes</groupId>
                 <artifactId>cucumber-picocontainer</artifactId>
                 <version>${cucumber.version}</version>
-                <scope>test</scope>
             </dependency>
             <dependency>
                 <groupId>com.github.fge</groupId>

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/pom.xml
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/pom.xml
 
b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/pom.xml
index 19b7410..e832966 100644
--- 
a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/pom.xml
+++ 
b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/pom.xml
@@ -189,12 +189,12 @@
                 </dependency>
                 <dependency>
                     <groupId>info.cukes</groupId>
-                    <artifactId>cucumber-junit</artifactId>
+                    <artifactId>cucumber-guice</artifactId>
                     <scope>test</scope>
                 </dependency>
                 <dependency>
                     <groupId>info.cukes</groupId>
-                    <artifactId>cucumber-picocontainer</artifactId>
+                    <artifactId>cucumber-junit</artifactId>
                     <scope>test</scope>
                 </dependency>
                 <dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/CassandraSetMailboxesMethodCucumberTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/CassandraSetMailboxesMethodCucumberTest.java
 
b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/CassandraSetMailboxesMethodCucumberTest.java
deleted file mode 100644
index a859fa9..0000000
--- 
a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/CassandraSetMailboxesMethodCucumberTest.java
+++ /dev/null
@@ -1,31 +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.jmap.cassandra;
-
-import org.junit.runner.RunWith;
-
-import cucumber.api.CucumberOptions;
-import cucumber.api.junit.Cucumber;
-
-@RunWith(Cucumber.class)
-@CucumberOptions(features="classpath:cucumber/MailboxModification.feature",
-                glue={"org.apache.james.jmap.methods.integration", 
"org.apache.james.jmap.cassandra"})
-public class CassandraSetMailboxesMethodCucumberTest {
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/CassandraSetMailboxesMethodStepdefs.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/CassandraSetMailboxesMethodStepdefs.java
 
b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/CassandraSetMailboxesMethodStepdefs.java
deleted file mode 100644
index fe80ae9..0000000
--- 
a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/CassandraSetMailboxesMethodStepdefs.java
+++ /dev/null
@@ -1,60 +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.jmap.cassandra;
-
-import org.apache.james.CassandraJamesServerMain;
-import org.apache.james.GuiceJamesServer;
-import org.apache.james.backends.cassandra.EmbeddedCassandra;
-import org.apache.james.jmap.methods.integration.SetMailboxesMethodStepdefs;
-import org.apache.james.mailbox.elasticsearch.EmbeddedElasticSearch;
-import org.apache.james.modules.CassandraJmapServerModule;
-import org.junit.rules.TemporaryFolder;
-
-import cucumber.api.java.After;
-import cucumber.api.java.Before;
-
-public class CassandraSetMailboxesMethodStepdefs {
-    private final SetMailboxesMethodStepdefs mainStepdefs;
-    private TemporaryFolder temporaryFolder = new TemporaryFolder();
-    private EmbeddedElasticSearch embeddedElasticSearch = new 
EmbeddedElasticSearch(temporaryFolder);
-    private EmbeddedCassandra cassandra = 
EmbeddedCassandra.createStartServer();
-
-    public CassandraSetMailboxesMethodStepdefs(SetMailboxesMethodStepdefs 
mainStepdefs) {
-        this.mainStepdefs = mainStepdefs;
-    }
-
-    @Before
-    public void init() throws Exception {
-        temporaryFolder.create();
-        embeddedElasticSearch.before();
-        mainStepdefs.jmapServer = new GuiceJamesServer()
-                .combineWith(CassandraJamesServerMain.cassandraServerModule)
-                .overrideWith(new CassandraJmapServerModule(temporaryFolder, 
embeddedElasticSearch, cassandra));
-        mainStepdefs.awaitMethod = () -> 
embeddedElasticSearch.awaitForElasticSearch();
-        mainStepdefs.init();
-    }
-
-    @After
-    public void tearDown() {
-        mainStepdefs.tearDown();
-        embeddedElasticSearch.after();
-        temporaryFolder.delete();
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/cucumber/CassandraDownloadCucumberTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/cucumber/CassandraDownloadCucumberTest.java
 
b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/cucumber/CassandraDownloadCucumberTest.java
new file mode 100644
index 0000000..1ad24f8
--- /dev/null
+++ 
b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/cucumber/CassandraDownloadCucumberTest.java
@@ -0,0 +1,31 @@
+/****************************************************************
+ * 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.jmap.cassandra.cucumber;
+
+import org.junit.runner.RunWith;
+
+import cucumber.api.CucumberOptions;
+import cucumber.api.junit.Cucumber;
+
+@RunWith(Cucumber.class)
+@CucumberOptions(features="classpath:cucumber/DownloadEndpoint.feature",
+                glue={"org.apache.james.jmap.methods.integration", 
"org.apache.james.jmap.cassandra.cucumber"})
+public class CassandraDownloadCucumberTest {
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/cucumber/CassandraSetMailboxesMethodCucumberTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/cucumber/CassandraSetMailboxesMethodCucumberTest.java
 
b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/cucumber/CassandraSetMailboxesMethodCucumberTest.java
new file mode 100644
index 0000000..6e1ee33
--- /dev/null
+++ 
b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/cucumber/CassandraSetMailboxesMethodCucumberTest.java
@@ -0,0 +1,31 @@
+/****************************************************************
+ * 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.jmap.cassandra.cucumber;
+
+import org.junit.runner.RunWith;
+
+import cucumber.api.CucumberOptions;
+import cucumber.api.junit.Cucumber;
+
+@RunWith(Cucumber.class)
+@CucumberOptions(features="classpath:cucumber/MailboxModification.feature",
+                glue={"org.apache.james.jmap.methods.integration", 
"org.apache.james.jmap.cassandra.cucumber"})
+public class CassandraSetMailboxesMethodCucumberTest {
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/cucumber/CassandraStepdefs.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/cucumber/CassandraStepdefs.java
 
b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/cucumber/CassandraStepdefs.java
new file mode 100644
index 0000000..fa2f77a
--- /dev/null
+++ 
b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/java/org/apache/james/jmap/cassandra/cucumber/CassandraStepdefs.java
@@ -0,0 +1,74 @@
+/****************************************************************
+ * 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.jmap.cassandra.cucumber;
+
+import javax.inject.Inject;
+
+import org.apache.james.CassandraJamesServerMain;
+import org.apache.james.GuiceJamesServer;
+import org.apache.james.backends.cassandra.EmbeddedCassandra;
+import org.apache.james.jmap.methods.integration.cucumber.MainStepdefs;
+import org.apache.james.mailbox.elasticsearch.EmbeddedElasticSearch;
+import org.apache.james.modules.CassandraJmapServerModule;
+import org.junit.rules.TemporaryFolder;
+
+import cucumber.api.java.After;
+import cucumber.api.java.Before;
+import cucumber.runtime.java.guice.ScenarioScoped;
+
+@ScenarioScoped
+public class CassandraStepdefs {
+
+    private final MainStepdefs mainStepdefs;
+    private TemporaryFolder temporaryFolder = new TemporaryFolder();
+    private EmbeddedElasticSearch embeddedElasticSearch = new 
EmbeddedElasticSearch(temporaryFolder);
+    private EmbeddedCassandra cassandra = 
EmbeddedCassandra.createStartServer();
+
+    @Inject
+    private CassandraStepdefs(MainStepdefs mainStepdefs) {
+        this.mainStepdefs = mainStepdefs;
+    }
+
+    @Before
+    public void init() throws Exception {
+        temporaryFolder.create();
+        embeddedElasticSearch.before();
+        mainStepdefs.jmapServer = new GuiceJamesServer()
+                .combineWith(CassandraJamesServerMain.cassandraServerModule)
+                .overrideWith(new CassandraJmapServerModule(temporaryFolder, 
embeddedElasticSearch, cassandra));
+        mainStepdefs.awaitMethod = () -> 
embeddedElasticSearch.awaitForElasticSearch();
+        mainStepdefs.init();
+    }
+
+    @After
+    public void tearDown() {
+        tearDown(() -> mainStepdefs.tearDown());
+        tearDown(() -> embeddedElasticSearch.after());
+        tearDown(() -> temporaryFolder.delete());
+    }
+
+    private void tearDown(Runnable cleaningFunction) {
+        try {
+            cleaningFunction.run();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/jmap-integration-testing-common/pom.xml
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/pom.xml
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/pom.xml
index 7adbda6..408e7a2 100644
--- 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/pom.xml
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/pom.xml
@@ -171,6 +171,11 @@
                 </dependency>
                 <dependency>
                     <groupId>info.cukes</groupId>
+                    <artifactId>cucumber-guice</artifactId>
+                    <scope>test</scope>
+                </dependency>
+                <dependency>
+                    <groupId>info.cukes</groupId>
                     <artifactId>cucumber-java</artifactId>
                     <scope>test</scope>
                 </dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMailboxesMethodStepdefs.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMailboxesMethodStepdefs.java
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMailboxesMethodStepdefs.java
deleted file mode 100644
index 6b6fa58..0000000
--- 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMailboxesMethodStepdefs.java
+++ /dev/null
@@ -1,174 +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.jmap.methods.integration;
-
-import static com.jayway.restassured.RestAssured.given;
-import static com.jayway.restassured.RestAssured.with;
-import static com.jayway.restassured.config.EncoderConfig.encoderConfig;
-import static com.jayway.restassured.config.RestAssuredConfig.newConfig;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.hasSize;
-
-import java.io.ByteArrayInputStream;
-import java.util.Date;
-import java.util.stream.IntStream;
-
-import javax.mail.Flags;
-
-import org.apache.james.GuiceJamesServer;
-import org.apache.james.jmap.JmapAuthentication;
-import org.apache.james.jmap.api.access.AccessToken;
-import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.model.MailboxConstants;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.store.mail.model.Mailbox;
-
-import com.github.fge.lambdas.Throwing;
-import com.google.common.base.Charsets;
-import com.jayway.restassured.RestAssured;
-import com.jayway.restassured.http.ContentType;
-
-import cucumber.api.java.en.Given;
-import cucumber.api.java.en.Then;
-import cucumber.api.java.en.When;
-
-public class SetMailboxesMethodStepdefs {
-    private static final String NAME = "[0][0]";
-    private static final String ARGUMENTS = "[0][1]";
-
-    public GuiceJamesServer jmapServer;
-    public Runnable awaitMethod = () -> {};
-
-    private AccessToken accessToken;
-    private String username;
-
-    public void init() throws Exception {
-        jmapServer.start();
-        RestAssured.port = jmapServer.getJmapPort();
-        RestAssured.config = 
newConfig().encoderConfig(encoderConfig().defaultContentCharset(Charsets.UTF_8));
-        RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
-    }
-    
-    public void tearDown() {
-        jmapServer.stop();
-    }
-    
-
-    @Given("^a domain named \"([^\"]*)\"$")
-    public void createDomain(String domain) throws Throwable {
-        jmapServer.serverProbe().addDomain(domain);
-    }
-
-    @Given("^a current user with username \"([^\"]*)\" and password 
\"([^\"]*)\"$")
-    public void createUserWithPasswordAndAuthenticate(String username, String 
password) throws Throwable {
-        this.username = username;
-        jmapServer.serverProbe().addUser(username, password);
-        accessToken = JmapAuthentication.authenticateJamesUser(username, 
password);
-    }
-
-    @Given("^mailbox \"([^\"]*)\" with (\\d+) messages$")
-    public void mailboxWithMessages(String mailboxName, int messageCount) 
throws Throwable {
-        jmapServer.serverProbe().createMailbox("#private", username, 
mailboxName);
-        MailboxPath mailboxPath = new 
MailboxPath(MailboxConstants.USER_NAMESPACE, username, mailboxName);
-        IntStream
-            .range(0, messageCount)
-            .forEach(Throwing.intConsumer(i -> appendMessage(mailboxPath, i)));
-        awaitMethod.run();
-    }
-
-    private void appendMessage(MailboxPath mailboxPath, int i) throws 
MailboxException {
-        String content = "Subject: test" + i + "\r\n\r\n"
-                + "testBody" + i;
-        jmapServer.serverProbe().appendMessage(username, mailboxPath,
-                new ByteArrayInputStream(content.getBytes()), new Date(), 
false, new Flags());
-    }
-
-    @When("^renaming mailbox \"([^\"]*)\" to \"([^\"]*)\"")
-    public void renamingMailbox(String actualMailboxName, String 
newMailboxName) throws Throwable {
-        Mailbox mailbox = jmapServer.serverProbe().getMailbox("#private", 
username, actualMailboxName);
-        String mailboxId = mailbox.getMailboxId().serialize();
-        String requestBody =
-                "[" +
-                    "  [ \"setMailboxes\"," +
-                    "    {" +
-                    "      \"update\": {" +
-                    "        \"" + mailboxId + "\" : {" +
-                    "          \"name\" : \"" + newMailboxName + "\"" +
-                    "        }" +
-                    "      }" +
-                    "    }," +
-                    "    \"#0\"" +
-                    "  ]" +
-                    "]";
-
-        with()
-            .accept(ContentType.JSON)
-            .contentType(ContentType.JSON)
-            .header("Authorization", this.accessToken.serialize())
-            .body(requestBody)
-            .post("/jmap");
-    }
-
-    @When("^moving mailbox \"([^\"]*)\" to \"([^\"]*)\"$")
-    public void movingMailbox(String actualMailboxPath, String 
newParentMailboxPath) throws Throwable {
-        Mailbox mailbox = jmapServer.serverProbe().getMailbox("#private", 
username, actualMailboxPath);
-        String mailboxId = mailbox.getMailboxId().serialize();
-        Mailbox parent = jmapServer.serverProbe().getMailbox("#private", 
username, newParentMailboxPath);
-        String parentId = parent.getMailboxId().serialize();
-
-        String requestBody =
-                "[" +
-                    "  [ \"setMailboxes\"," +
-                    "    {" +
-                    "      \"update\": {" +
-                    "        \"" + mailboxId + "\" : {" +
-                    "          \"parentId\" : \"" + parentId + "\"" +
-                    "        }" +
-                    "      }" +
-                    "    }," +
-                    "    \"#0\"" +
-                    "  ]" +
-                    "]";
-
-        with()
-            .accept(ContentType.JSON)
-            .contentType(ContentType.JSON)
-            .header("Authorization", this.accessToken.serialize())
-            .body(requestBody)
-            .post("/jmap");
-    }
-
-    @Then("^mailbox \"([^\"]*)\" contains (\\d+) messages$")
-    public void mailboxContainsMessages(String mailboxName, int messageCount) 
throws Throwable {
-        Mailbox mailbox = jmapServer.serverProbe().getMailbox("#private", 
username, mailboxName);
-        String mailboxId = mailbox.getMailboxId().serialize();
-        given()
-            .accept(ContentType.JSON)
-            .contentType(ContentType.JSON)
-            .header("Authorization", accessToken.serialize())
-            .body("[[\"getMessageList\", {\"filter\":{\"inMailboxes\":[\"" + 
mailboxId + "\"]}}, \"#0\"]]")
-        .when()
-            .post("/jmap")
-        .then()
-            .statusCode(200)
-            .body(NAME, equalTo("messageList"))
-            .body(ARGUMENTS + ".messageIds", hasSize(messageCount));
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java
new file mode 100644
index 0000000..e703f23
--- /dev/null
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java
@@ -0,0 +1,105 @@
+/****************************************************************
+ * 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.jmap.methods.integration.cucumber;
+
+import static com.jayway.restassured.RestAssured.with;
+
+import javax.inject.Inject;
+
+import com.jayway.restassured.http.ContentType;
+import com.jayway.restassured.response.Response;
+import com.jayway.restassured.specification.RequestSpecification;
+
+import cucumber.api.java.en.Given;
+import cucumber.api.java.en.Then;
+import cucumber.api.java.en.When;
+import cucumber.runtime.java.guice.ScenarioScoped;
+
+@ScenarioScoped
+public class DownloadStepdefs {
+
+    private final UserStepdefs userStepdefs;
+    private final MainStepdefs mainStepdefs;
+    private Response response;
+
+    @Inject
+    private DownloadStepdefs(MainStepdefs mainStepdefs, UserStepdefs 
userStepdefs) {
+        this.mainStepdefs = mainStepdefs;
+        this.userStepdefs = userStepdefs;
+    }
+
+    @Given("^an unknown current user with username \"([^\"]*)\" and password 
\"([^\"]*)\"$")
+    public void createUserWithPassword(String username, String password) 
throws Exception {
+        mainStepdefs.jmapServer.serverProbe().addUser(username, password);
+    }
+
+    @When("^checking for the availability of the attachment endpoint$")
+    public void optionDownload() throws Throwable {
+        RequestSpecification requestSpecification = with()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON);
+
+        if (userStepdefs.accessToken != null) {
+            requestSpecification.header("Authorization", 
userStepdefs.accessToken.serialize());
+        }
+
+        response = requestSpecification.options("/download/myBlob");
+    }
+
+    @When("^asking for an attachment$")
+    public void getDownload() throws Exception {
+        RequestSpecification requestSpecification = with()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON);
+
+        if (userStepdefs.accessToken != null) {
+            requestSpecification.header("Authorization", 
userStepdefs.accessToken.serialize());
+        }
+
+        response = requestSpecification.get("/download/myBlob");
+    }
+
+    @When("^asking for an attachment without blobId parameter$")
+    public void getDownloadWithoutBlobId() throws Throwable {
+        response = with()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", userStepdefs.accessToken.serialize())
+            .get("/download/");
+    }
+
+    @Then("^the user should be authorized$")
+    public void httpOkStatus() throws Exception {
+        response.then()
+            .statusCode(200);
+    }
+
+    @Then("^the user should not be authorized$")
+    public void httpUnauthorizedStatus() throws Exception {
+        response.then()
+            .statusCode(401);
+    }
+
+    @Then("^the user should receive a bad request response$")
+    public void httpBadRequestStatus() throws Throwable {
+        response.then()
+            .statusCode(400);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/MainStepdefs.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/MainStepdefs.java
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/MainStepdefs.java
new file mode 100644
index 0000000..d7ed5c9
--- /dev/null
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/MainStepdefs.java
@@ -0,0 +1,48 @@
+/****************************************************************
+ * 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.jmap.methods.integration.cucumber;
+
+import static com.jayway.restassured.config.EncoderConfig.encoderConfig;
+import static com.jayway.restassured.config.RestAssuredConfig.newConfig;
+
+import org.apache.james.GuiceJamesServer;
+
+import com.google.common.base.Charsets;
+import com.jayway.restassured.RestAssured;
+
+import cucumber.runtime.java.guice.ScenarioScoped;
+
+@ScenarioScoped
+public class MainStepdefs {
+
+    public GuiceJamesServer jmapServer;
+    public Runnable awaitMethod = () -> {};
+
+    public void init() throws Exception {
+        jmapServer.start();
+        RestAssured.port = jmapServer.getJmapPort();
+        RestAssured.config = 
newConfig().encoderConfig(encoderConfig().defaultContentCharset(Charsets.UTF_8));
+        RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
+    }
+    
+    public void tearDown() {
+        jmapServer.stop();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMailboxesMethodStepdefs.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMailboxesMethodStepdefs.java
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMailboxesMethodStepdefs.java
new file mode 100644
index 0000000..52d0214
--- /dev/null
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMailboxesMethodStepdefs.java
@@ -0,0 +1,150 @@
+/****************************************************************
+ * 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.jmap.methods.integration.cucumber;
+
+import static com.jayway.restassured.RestAssured.given;
+import static com.jayway.restassured.RestAssured.with;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+
+import java.io.ByteArrayInputStream;
+import java.util.Date;
+import java.util.stream.IntStream;
+
+import javax.inject.Inject;
+import javax.mail.Flags;
+
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.model.MailboxConstants;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.store.mail.model.Mailbox;
+
+import com.github.fge.lambdas.Throwing;
+import com.jayway.restassured.http.ContentType;
+
+import cucumber.api.java.en.Given;
+import cucumber.api.java.en.Then;
+import cucumber.api.java.en.When;
+import cucumber.runtime.java.guice.ScenarioScoped;
+
+@ScenarioScoped
+public class SetMailboxesMethodStepdefs {
+
+    private static final String NAME = "[0][0]";
+    private static final String ARGUMENTS = "[0][1]";
+
+    private final MainStepdefs mainStepdefs;
+    private final UserStepdefs userStepdefs;
+
+    @Inject
+    private SetMailboxesMethodStepdefs(MainStepdefs mainStepdefs, UserStepdefs 
userStepdefs) {
+        this.mainStepdefs = mainStepdefs;
+        this.userStepdefs = userStepdefs;
+    }
+
+    @Given("^mailbox \"([^\"]*)\" with (\\d+) messages$")
+    public void mailboxWithMessages(String mailboxName, int messageCount) 
throws Throwable {
+        mainStepdefs.jmapServer.serverProbe().createMailbox("#private", 
userStepdefs.username, mailboxName);
+        MailboxPath mailboxPath = new 
MailboxPath(MailboxConstants.USER_NAMESPACE, userStepdefs.username, 
mailboxName);
+        IntStream
+            .range(0, messageCount)
+            .forEach(Throwing.intConsumer(i -> appendMessage(mailboxPath, i)));
+        mainStepdefs.awaitMethod.run();
+    }
+
+    private void appendMessage(MailboxPath mailboxPath, int i) throws 
MailboxException {
+        String content = "Subject: test" + i + "\r\n\r\n"
+                + "testBody" + i;
+        
mainStepdefs.jmapServer.serverProbe().appendMessage(userStepdefs.username, 
mailboxPath,
+                new ByteArrayInputStream(content.getBytes()), new Date(), 
false, new Flags());
+    }
+
+    @When("^renaming mailbox \"([^\"]*)\" to \"([^\"]*)\"")
+    public void renamingMailbox(String actualMailboxName, String 
newMailboxName) throws Throwable {
+        Mailbox mailbox = 
mainStepdefs.jmapServer.serverProbe().getMailbox("#private", 
userStepdefs.username, actualMailboxName);
+        String mailboxId = mailbox.getMailboxId().serialize();
+        String requestBody =
+                "[" +
+                    "  [ \"setMailboxes\"," +
+                    "    {" +
+                    "      \"update\": {" +
+                    "        \"" + mailboxId + "\" : {" +
+                    "          \"name\" : \"" + newMailboxName + "\"" +
+                    "        }" +
+                    "      }" +
+                    "    }," +
+                    "    \"#0\"" +
+                    "  ]" +
+                    "]";
+
+        with()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", userStepdefs.accessToken.serialize())
+            .body(requestBody)
+            .post("/jmap");
+    }
+
+    @When("^moving mailbox \"([^\"]*)\" to \"([^\"]*)\"$")
+    public void movingMailbox(String actualMailboxPath, String 
newParentMailboxPath) throws Throwable {
+        Mailbox mailbox = 
mainStepdefs.jmapServer.serverProbe().getMailbox("#private", 
userStepdefs.username, actualMailboxPath);
+        String mailboxId = mailbox.getMailboxId().serialize();
+        Mailbox parent = 
mainStepdefs.jmapServer.serverProbe().getMailbox("#private", 
userStepdefs.username, newParentMailboxPath);
+        String parentId = parent.getMailboxId().serialize();
+
+        String requestBody =
+                "[" +
+                    "  [ \"setMailboxes\"," +
+                    "    {" +
+                    "      \"update\": {" +
+                    "        \"" + mailboxId + "\" : {" +
+                    "          \"parentId\" : \"" + parentId + "\"" +
+                    "        }" +
+                    "      }" +
+                    "    }," +
+                    "    \"#0\"" +
+                    "  ]" +
+                    "]";
+
+        with()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", userStepdefs.accessToken.serialize())
+            .body(requestBody)
+            .post("/jmap");
+    }
+
+    @Then("^mailbox \"([^\"]*)\" contains (\\d+) messages$")
+    public void mailboxContainsMessages(String mailboxName, int messageCount) 
throws Throwable {
+        Mailbox mailbox = 
mainStepdefs.jmapServer.serverProbe().getMailbox("#private", 
userStepdefs.username, mailboxName);
+        String mailboxId = mailbox.getMailboxId().serialize();
+        given()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", userStepdefs.accessToken.serialize())
+            .body("[[\"getMessageList\", {\"filter\":{\"inMailboxes\":[\"" + 
mailboxId + "\"]}}, \"#0\"]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .body(NAME, equalTo("messageList"))
+            .body(ARGUMENTS + ".messageIds", hasSize(messageCount));
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/UserStepdefs.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/UserStepdefs.java
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/UserStepdefs.java
new file mode 100644
index 0000000..01f0496
--- /dev/null
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/UserStepdefs.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.jmap.methods.integration.cucumber;
+
+import javax.inject.Inject;
+
+import org.apache.james.jmap.JmapAuthentication;
+import org.apache.james.jmap.api.access.AccessToken;
+
+import cucumber.api.java.en.Given;
+import cucumber.runtime.java.guice.ScenarioScoped;
+
+@ScenarioScoped
+public class UserStepdefs {
+
+    private final MainStepdefs mainStepdefs;
+    protected String username;
+    protected AccessToken accessToken;
+
+    @Inject
+    private UserStepdefs(MainStepdefs mainStepdefs) {
+        this.mainStepdefs = mainStepdefs;
+    }
+
+    @Given("^a domain named \"([^\"]*)\"$")
+    public void createDomain(String domain) throws Throwable {
+        mainStepdefs.jmapServer.serverProbe().addDomain(domain);
+    }
+
+    @Given("^a current user with username \"([^\"]*)\" and password 
\"([^\"]*)\"$")
+    public void createUserWithPasswordAndAuthenticate(String username, String 
password) throws Throwable {
+        this.username = username;
+        mainStepdefs.jmapServer.serverProbe().addUser(username, password);
+        accessToken = JmapAuthentication.authenticateJamesUser(username, 
password);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadEndpoint.feature
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadEndpoint.feature
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadEndpoint.feature
new file mode 100644
index 0000000..a483d8e
--- /dev/null
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/DownloadEndpoint.feature
@@ -0,0 +1,31 @@
+Feature: Download endpoint
+  As a James user
+  I want to access to the download endpoint
+
+  Background:
+    Given a domain named "domain.tld"
+
+  Scenario: A known user should initiate the access to the download endpoint
+    Given a current user with username "usern...@domain.tld" and password 
"secret"
+    When checking for the availability of the attachment endpoint
+    Then the user should be authorized
+
+  Scenario: An unknown user should initiate the access to the download endpoint
+    Given an unknown current user with username "unkn...@domain.tld" and 
password "secret"
+    When checking for the availability of the attachment endpoint
+    Then the user should be authorized
+
+  Scenario: A known user should have access to the download endpoint
+    Given a current user with username "usern...@domain.tld" and password 
"secret"
+    When asking for an attachment
+    Then the user should be authorized
+
+  Scenario: An unknown user should not have access to the download endpoint
+    Given an unknown current user with username "unkn...@domain.tld" and 
password "secret"
+    When asking for an attachment
+    Then the user should not be authorized
+
+  Scenario: A known user should not have access to the download endpoint 
without a blobId
+    Given a current user with username "usern...@domain.tld" and password 
"secret"
+    When asking for an attachment without blobId parameter
+    Then the user should receive a bad request response

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/pom.xml
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/pom.xml
 
b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/pom.xml
index 4254a7e..5a078e4 100644
--- 
a/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/pom.xml
+++ 
b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/pom.xml
@@ -182,7 +182,7 @@
                 </dependency>
                 <dependency>
                     <groupId>info.cukes</groupId>
-                    <artifactId>cucumber-picocontainer</artifactId>
+                    <artifactId>cucumber-guice</artifactId>
                     <scope>test</scope>
                 </dependency>
                 <dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/MemorySetMailboxesMethodCucumberTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/MemorySetMailboxesMethodCucumberTest.java
 
b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/MemorySetMailboxesMethodCucumberTest.java
deleted file mode 100644
index a856c0b..0000000
--- 
a/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/MemorySetMailboxesMethodCucumberTest.java
+++ /dev/null
@@ -1,31 +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.jmap.memory;
-
-import org.junit.runner.RunWith;
-
-import cucumber.api.CucumberOptions;
-import cucumber.api.junit.Cucumber;
-
-@RunWith(Cucumber.class)
-@CucumberOptions(features="classpath:cucumber/MailboxModification.feature",
-                glue={"org.apache.james.jmap.methods.integration", 
"org.apache.james.jmap.memory"})
-public class MemorySetMailboxesMethodCucumberTest {
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/MemorySetMailboxesMethodStepdefs.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/MemorySetMailboxesMethodStepdefs.java
 
b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/MemorySetMailboxesMethodStepdefs.java
deleted file mode 100644
index cbdca46..0000000
--- 
a/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/MemorySetMailboxesMethodStepdefs.java
+++ /dev/null
@@ -1,54 +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.jmap.memory;
-
-import org.apache.james.GuiceJamesServer;
-import org.apache.james.MemoryJamesServerMain;
-import org.apache.james.jmap.methods.integration.SetMailboxesMethodStepdefs;
-import org.apache.james.jmap.servers.MemoryJmapServerModule;
-import org.junit.rules.TemporaryFolder;
-
-import cucumber.api.java.After;
-import cucumber.api.java.Before;
-
-public class MemorySetMailboxesMethodStepdefs {
-    private final SetMailboxesMethodStepdefs mainStepdefs;
-    private final TemporaryFolder temporaryFolder;
-
-    public MemorySetMailboxesMethodStepdefs(SetMailboxesMethodStepdefs 
mainStepdefs) {
-        this.mainStepdefs = mainStepdefs;
-        this.temporaryFolder = new TemporaryFolder();
-    }
-
-    @Before
-    public void init() throws Exception {
-        temporaryFolder.create();
-        mainStepdefs.jmapServer = new GuiceJamesServer()
-                .combineWith(MemoryJamesServerMain.inMemoryServerModule)
-                .overrideWith(new MemoryJmapServerModule(temporaryFolder));
-        mainStepdefs.init();
-    }
-
-    @After
-    public void tearDown() {
-        mainStepdefs.tearDown();
-        temporaryFolder.delete();
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/cucumber/MemoryDownloadCucumberTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/cucumber/MemoryDownloadCucumberTest.java
 
b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/cucumber/MemoryDownloadCucumberTest.java
new file mode 100644
index 0000000..b6a0f3e
--- /dev/null
+++ 
b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/cucumber/MemoryDownloadCucumberTest.java
@@ -0,0 +1,31 @@
+/****************************************************************
+ * 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.jmap.memory.cucumber;
+
+import org.junit.runner.RunWith;
+
+import cucumber.api.CucumberOptions;
+import cucumber.api.junit.Cucumber;
+
+@RunWith(Cucumber.class)
+@CucumberOptions(features="classpath:cucumber/DownloadEndpoint.feature",
+                glue={"org.apache.james.jmap.methods.integration", 
"org.apache.james.jmap.memory.cucumber"})
+public class MemoryDownloadCucumberTest {
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/cucumber/MemorySetMailboxesMethodCucumberTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/cucumber/MemorySetMailboxesMethodCucumberTest.java
 
b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/cucumber/MemorySetMailboxesMethodCucumberTest.java
new file mode 100644
index 0000000..52b25fd
--- /dev/null
+++ 
b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/cucumber/MemorySetMailboxesMethodCucumberTest.java
@@ -0,0 +1,31 @@
+/****************************************************************
+ * 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.jmap.memory.cucumber;
+
+import org.junit.runner.RunWith;
+
+import cucumber.api.CucumberOptions;
+import cucumber.api.junit.Cucumber;
+
+@RunWith(Cucumber.class)
+@CucumberOptions(features="classpath:cucumber/MailboxModification.feature",
+                glue={"org.apache.james.jmap.methods.integration", 
"org.apache.james.jmap.memory.cucumber"})
+public class MemorySetMailboxesMethodCucumberTest {
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/cucumber/MemoryStepdefs.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/cucumber/MemoryStepdefs.java
 
b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/cucumber/MemoryStepdefs.java
new file mode 100644
index 0000000..2aa16b1
--- /dev/null
+++ 
b/server/protocols/jmap-integration-testing/memory-jmap-integration-testing/src/test/java/org/apache/james/jmap/memory/cucumber/MemoryStepdefs.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.jmap.memory.cucumber;
+
+import javax.inject.Inject;
+
+import org.apache.james.GuiceJamesServer;
+import org.apache.james.MemoryJamesServerMain;
+import org.apache.james.jmap.methods.integration.cucumber.MainStepdefs;
+import org.apache.james.jmap.servers.MemoryJmapServerModule;
+import org.junit.rules.TemporaryFolder;
+
+import cucumber.api.java.After;
+import cucumber.api.java.Before;
+import cucumber.runtime.java.guice.ScenarioScoped;
+
+@ScenarioScoped
+public class MemoryStepdefs {
+
+    private final MainStepdefs mainStepdefs;
+    private final TemporaryFolder temporaryFolder;
+
+    @Inject
+    private MemoryStepdefs(MainStepdefs mainStepdefs) {
+        this.mainStepdefs = mainStepdefs;
+        this.temporaryFolder = new TemporaryFolder();
+    }
+
+    @Before
+    public void init() throws Exception {
+        temporaryFolder.create();
+        mainStepdefs.jmapServer = new GuiceJamesServer()
+                .combineWith(MemoryJamesServerMain.inMemoryServerModule)
+                .overrideWith(new MemoryJmapServerModule(temporaryFolder));
+        mainStepdefs.init();
+    }
+
+    @After
+    public void tearDown() {
+        mainStepdefs.tearDown();
+        temporaryFolder.delete();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap/src/main/java/org/apache/james/jmap/DownloadServlet.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/DownloadServlet.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/DownloadServlet.java
new file mode 100644
index 0000000..d4a7597
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/DownloadServlet.java
@@ -0,0 +1,44 @@
+/****************************************************************
+ * 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.jmap;
+
+import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.google.common.base.Strings;
+
+public class DownloadServlet extends HttpServlet {
+
+    private static final String ROOT_URL = "/";
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException {
+        String pathInfo = req.getPathInfo();
+        if (Strings.isNullOrEmpty(pathInfo) || pathInfo.equals(ROOT_URL)) {
+            resp.setStatus(SC_BAD_REQUEST);
+        } else {
+            resp.setStatus(SC_OK);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4df388be/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java
index c3b0665..5161412 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java
@@ -39,7 +39,7 @@ public class JMAPServer implements Configurable {
 
     @Inject
     private JMAPServer(JMAPConfiguration jmapConfiguration,
-                       AuthenticationServlet authenticationServlet, 
JMAPServlet jmapServlet,
+                       AuthenticationServlet authenticationServlet, 
JMAPServlet jmapServlet, DownloadServlet downloadServlet,
                        AuthenticationFilter authenticationFilter, 
FirstUserConnectionFilter firstUserConnectionFilter) {
 
         server = JettyHttpServer.create(
@@ -55,6 +55,11 @@ public class JMAPServer implements Configurable {
                             .with(new 
AllowAllCrossOriginRequests(bypass(authenticationFilter).on("OPTIONS").only()))
                             .and(firstUserConnectionFilter)
                             .only()
+                        .serveAsOneLevelTemplate(JMAPUrls.DOWNLOAD)
+                            .with(downloadServlet)
+                        .filterAsOneLevelTemplate(JMAPUrls.DOWNLOAD)
+                            .with(new 
AllowAllCrossOriginRequests(bypass(authenticationFilter).on("OPTIONS").only()))
+                            .only()
                         .build());
     }
 


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

Reply via email to