You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2020/03/24 02:03:57 UTC

[james-project] 01/05: JAMES-3078 Migrate mock SMTP to reactor-netty

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

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

commit e784cd945c9beff94776c6174bd6d9a6595e4013
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Sat Mar 21 11:01:36 2020 +0700

    JAMES-3078 Migrate mock SMTP to reactor-netty
---
 server/mailet/mock-smtp-server/pom.xml             |   8 +-
 .../mock/smtp/server/HTTPConfigurationServer.java  | 186 ++++++++++++---------
 .../mock/smtp/server/ConfigurationClientTest.java  |   4 +-
 .../smtp/server/HTTPConfigurationServerTest.java   |  19 +--
 4 files changed, 121 insertions(+), 96 deletions(-)

diff --git a/server/mailet/mock-smtp-server/pom.xml b/server/mailet/mock-smtp-server/pom.xml
index 14c00b3..2c59171 100644
--- a/server/mailet/mock-smtp-server/pom.xml
+++ b/server/mailet/mock-smtp-server/pom.xml
@@ -43,10 +43,6 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-jetty</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-util</artifactId>
         </dependency>
         <dependency>
@@ -86,6 +82,10 @@
             <artifactId>feign-slf4j</artifactId>
         </dependency>
         <dependency>
+            <groupId>io.projectreactor.netty</groupId>
+            <artifactId>reactor-netty</artifactId>
+        </dependency>
+        <dependency>
             <groupId>io.rest-assured</groupId>
             <artifactId>rest-assured</artifactId>
             <scope>test</scope>
diff --git a/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/HTTPConfigurationServer.java b/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/HTTPConfigurationServer.java
index 83f6ac0..32f7746 100644
--- a/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/HTTPConfigurationServer.java
+++ b/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/HTTPConfigurationServer.java
@@ -19,105 +19,78 @@
 
 package org.apache.james.mock.smtp.server;
 
-import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
-import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
-import static javax.servlet.http.HttpServletResponse.SC_OK;
+import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
+import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
+import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;
+import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT;
+import static io.netty.handler.codec.http.HttpResponseStatus.OK;
 
 import java.io.IOException;
+import java.util.Optional;
 
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.james.http.jetty.Configuration;
-import org.apache.james.http.jetty.JettyHttpServer;
 import org.apache.james.mock.smtp.server.jackson.MailAddressModule;
 import org.apache.james.mock.smtp.server.model.Mails;
 import org.apache.james.mock.smtp.server.model.MockSMTPBehaviorInformation;
 import org.apache.james.mock.smtp.server.model.MockSmtpBehaviors;
 import org.apache.james.util.Port;
+import org.reactivestreams.Publisher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.datatype.guava.GuavaModule;
 import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
 import com.github.steveash.guavate.Guavate;
 
-public class HTTPConfigurationServer {
-
-    public static class RunningStage {
-        private final HTTPConfigurationServer underlyingServer;
-
-        private RunningStage(HTTPConfigurationServer underlyingServer) {
-            this.underlyingServer = underlyingServer;
-        }
-
-        public Port getPort() {
-            return underlyingServer.getPort();
-        }
+import reactor.core.publisher.Mono;
+import reactor.netty.DisposableServer;
+import reactor.netty.http.server.HttpServer;
+import reactor.netty.http.server.HttpServerRequest;
+import reactor.netty.http.server.HttpServerResponse;
 
-        public void stop() throws Exception {
-            underlyingServer.stop();
-        }
-    }
+public class HTTPConfigurationServer {
 
-    static class SMTPBehaviorsServlet extends HttpServlet {
-        private final SMTPBehaviorRepository smtpBehaviorRepository;
+    public static final String APPLICATION_JSON = "application/json";
 
-        SMTPBehaviorsServlet(SMTPBehaviorRepository smtpBehaviorRepository) {
-            this.smtpBehaviorRepository = smtpBehaviorRepository;
+    public static class Configuration {
+        static Configuration port(Port port) {
+            return new Configuration(Optional.of(port));
         }
 
-        @Override
-        protected void doPut(HttpServletRequest req, HttpServletResponse resp) {
-            try {
-                MockSmtpBehaviors behaviors = OBJECT_MAPPER.readValue(req.getInputStream(), MockSmtpBehaviors.class);
-                smtpBehaviorRepository.setBehaviors(behaviors);
-                resp.setStatus(SC_NO_CONTENT);
-            } catch (IOException e) {
-                resp.setStatus(SC_BAD_REQUEST);
-            }
+        static Configuration randomPort() {
+            return new Configuration(Optional.empty());
         }
 
-        @Override
-        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
-            MockSmtpBehaviors mockSmtpBehaviors = new MockSmtpBehaviors(smtpBehaviorRepository.remainingBehaviors()
-                .map(MockSMTPBehaviorInformation::getBehavior)
-                .collect(Guavate.toImmutableList()));
+        private final Optional<Port> port;
 
-            resp.setStatus(SC_OK);
-            resp.setContentType("application/json");
-            OBJECT_MAPPER.writeValue(resp.getOutputStream(), mockSmtpBehaviors);
+        Configuration(Optional<Port> port) {
+            this.port = port;
         }
 
-        @Override
-        protected void doDelete(HttpServletRequest req, HttpServletResponse resp) {
-            smtpBehaviorRepository.clearBehaviors();
-            resp.setStatus(SC_NO_CONTENT);
+        Optional<Port> getPort() {
+            return port;
         }
     }
 
-    static class SMTPMailsServlet extends HttpServlet {
-        private final ReceivedMailRepository receivedMailRepository;
+    public static class RunningStage {
+        private final DisposableServer server;
 
-        SMTPMailsServlet(ReceivedMailRepository receivedMailRepository) {
-            this.receivedMailRepository = receivedMailRepository;
+        private RunningStage(DisposableServer server) {
+            this.server = server;
         }
 
-        @Override
-        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
-            Mails mails = new Mails(receivedMailRepository.list());
-            resp.setStatus(SC_OK);
-            resp.setContentType("application/json");
-            OBJECT_MAPPER.writeValue(resp.getOutputStream(), mails);
+        public Port getPort() {
+            return Port.of(server.port());
         }
 
-        @Override
-        protected void doDelete(HttpServletRequest req, HttpServletResponse resp) {
-            receivedMailRepository.clear();
-            resp.setStatus(SC_NO_CONTENT);
+        public void stop() {
+            server.disposeNow();
         }
     }
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(HTTPConfigurationServer.class);
+    private static final int RANDOM_PORT = 0;
     static final String SMTP_BEHAVIORS = "/smtpBehaviors";
     static final String SMTP_MAILS = "/smtpMails";
 
@@ -129,35 +102,88 @@ public class HTTPConfigurationServer {
     public static HTTPConfigurationServer onRandomPort(SMTPBehaviorRepository smtpBehaviorRepository, ReceivedMailRepository receivedMailRepository) {
         return new HTTPConfigurationServer(smtpBehaviorRepository,
             receivedMailRepository,
-            Configuration.builder().randomPort());
+            Configuration.randomPort());
     }
 
     public static HTTPConfigurationServer onPort(SMTPBehaviorRepository smtpBehaviorRepository, ReceivedMailRepository receivedMailRepository, Port port) {
         return new HTTPConfigurationServer(smtpBehaviorRepository,
             receivedMailRepository,
-            Configuration.builder().port(port.getValue()));
+            Configuration.port(port));
     }
 
-    private final JettyHttpServer jettyHttpServer;
+    private final SMTPBehaviorRepository smtpBehaviorRepository;
+    private final ReceivedMailRepository receivedMailRepository;
+    private final Configuration configuration;
+
+    private HTTPConfigurationServer(SMTPBehaviorRepository smtpBehaviorRepository, ReceivedMailRepository receivedMailRepository, Configuration configuration) {
+        this.smtpBehaviorRepository = smtpBehaviorRepository;
+        this.receivedMailRepository = receivedMailRepository;
+        this.configuration = configuration;
+    }
 
-    private HTTPConfigurationServer(SMTPBehaviorRepository smtpBehaviorRepository, ReceivedMailRepository receivedMailRepository, Configuration.Builder configurationBuilder) {
-        jettyHttpServer = JettyHttpServer.create(configurationBuilder.serve(SMTP_BEHAVIORS)
-            .with(new SMTPBehaviorsServlet(smtpBehaviorRepository))
-            .serve(SMTP_MAILS)
-            .with(new SMTPMailsServlet(receivedMailRepository))
-            .build());
+    public RunningStage start() {
+        return new RunningStage(HttpServer.create()
+            .port(configuration.getPort()
+                .map(Port::getValue)
+                .orElse(RANDOM_PORT))
+            .route(routes -> routes
+                .get(SMTP_BEHAVIORS, this::getBehaviors)
+                .put(SMTP_BEHAVIORS, this::putBehaviors)
+                .delete(SMTP_BEHAVIORS, this::deleteBehaviors)
+                .get(SMTP_MAILS, this::getMails)
+                .delete(SMTP_MAILS, this::deleteMails))
+            .bindNow());
     }
 
-    public RunningStage start() throws Exception {
-        jettyHttpServer.start();
-        return new RunningStage(this);
+    private Publisher<Void> getBehaviors(HttpServerRequest req, HttpServerResponse res) {
+        MockSmtpBehaviors mockSmtpBehaviors = new MockSmtpBehaviors(smtpBehaviorRepository.remainingBehaviors()
+            .map(MockSMTPBehaviorInformation::getBehavior)
+            .collect(Guavate.toImmutableList()));
+
+        try {
+            return res.status(OK)
+                .header(CONTENT_TYPE, APPLICATION_JSON)
+                .sendString(Mono.just(OBJECT_MAPPER.writeValueAsString(mockSmtpBehaviors)));
+        } catch (JsonProcessingException e) {
+            LOGGER.error("Could not serialize JSON", e);
+            return res.status(INTERNAL_SERVER_ERROR).send();
+        }
     }
 
-    private Port getPort() {
-        return new Port(jettyHttpServer.getPort());
+    private Publisher<Void> putBehaviors(HttpServerRequest req, HttpServerResponse res) {
+        return req.receive().aggregate().asInputStream()
+            .flatMap(inputStream -> {
+                try {
+                    MockSmtpBehaviors behaviors = OBJECT_MAPPER.readValue(inputStream, MockSmtpBehaviors.class);
+                    smtpBehaviorRepository.setBehaviors(behaviors);
+                    return res.status(NO_CONTENT).send();
+                } catch (IOException e) {
+                    LOGGER.info("Bad request", e);
+                    return res.status(BAD_REQUEST).send();
+                }
+            });
     }
 
-    private void stop() throws Exception {
-        jettyHttpServer.stop();
+    private Publisher<Void> deleteBehaviors(HttpServerRequest req, HttpServerResponse res) {
+        smtpBehaviorRepository.clearBehaviors();
+        return res.status(NO_CONTENT).send();
+    }
+
+    private Publisher<Void> deleteMails(HttpServerRequest req, HttpServerResponse res) {
+        receivedMailRepository.clear();
+        return res.status(NO_CONTENT).send();
+    }
+
+    private Publisher<Void> getMails(HttpServerRequest req, HttpServerResponse res) {
+        Mails mails = new Mails(receivedMailRepository.list());
+
+        try {
+            return res.status(OK)
+                .header(CONTENT_TYPE, APPLICATION_JSON)
+                .sendString(Mono.just(OBJECT_MAPPER.writeValueAsString(mails)));
+        } catch (JsonProcessingException e) {
+            LOGGER.error("Could not serialize JSON", e);
+            return res.status(INTERNAL_SERVER_ERROR).send();
+        }
     }
 }
diff --git a/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/ConfigurationClientTest.java b/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/ConfigurationClientTest.java
index c1d9c1f..ae1bdc3 100644
--- a/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/ConfigurationClientTest.java
+++ b/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/ConfigurationClientTest.java
@@ -34,7 +34,7 @@ class ConfigurationClientTest {
     private ReceivedMailRepository mailRepository;
 
     @BeforeEach
-    void setUp() throws Exception {
+    void setUp() {
         behaviorRepository = new SMTPBehaviorRepository();
         mailRepository = new ReceivedMailRepository();
         server = HTTPConfigurationServer.onRandomPort(behaviorRepository, mailRepository)
@@ -44,7 +44,7 @@ class ConfigurationClientTest {
     }
 
     @AfterEach
-    void tearDown() throws Exception {
+    void tearDown() {
         server.stop();
     }
 
diff --git a/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/HTTPConfigurationServerTest.java b/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/HTTPConfigurationServerTest.java
index 4cc18aa..962f1c8 100644
--- a/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/HTTPConfigurationServerTest.java
+++ b/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/HTTPConfigurationServerTest.java
@@ -24,8 +24,8 @@ import static io.restassured.RestAssured.when;
 import static io.restassured.RestAssured.with;
 import static io.restassured.config.EncoderConfig.encoderConfig;
 import static io.restassured.config.RestAssuredConfig.newConfig;
-import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
 import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
+import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT;
 import static org.apache.james.mock.smtp.server.Fixture.JSON_BEHAVIORS;
 import static org.apache.james.mock.smtp.server.Fixture.JSON_MAIL;
 import static org.apache.james.mock.smtp.server.Fixture.JSON_MAILS_LIST;
@@ -34,7 +34,6 @@ import static org.hamcrest.Matchers.hasSize;
 import java.nio.charset.StandardCharsets;
 
 import org.apache.james.mock.smtp.server.Fixture.MailsFixutre;
-import org.eclipse.jetty.http.HttpStatus;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
@@ -52,7 +51,7 @@ class HTTPConfigurationServerTest {
     @Nested
     class SMTPBehaviorsTest {
         @BeforeEach
-        void setUp() throws Exception {
+        void setUp() {
             server = HTTPConfigurationServer.onRandomPort(new SMTPBehaviorRepository(), new ReceivedMailRepository())
                 .start();
 
@@ -66,7 +65,7 @@ class HTTPConfigurationServerTest {
         }
 
         @AfterEach
-        void tearDown() throws Exception {
+        void tearDown() {
             server.stop();
         }
 
@@ -115,7 +114,7 @@ class HTTPConfigurationServerTest {
             .when()
                 .put()
             .then()
-                .statusCode(HttpStatus.NO_CONTENT_204);
+                .statusCode(NO_CONTENT.code());
         }
 
         @Test
@@ -127,7 +126,7 @@ class HTTPConfigurationServerTest {
             .when()
                 .put()
             .then()
-                .statusCode(HttpStatus.NO_CONTENT_204);
+                .statusCode(NO_CONTENT.code());
         }
 
         @Test
@@ -135,7 +134,7 @@ class HTTPConfigurationServerTest {
             when()
                 .delete()
             .then()
-                .statusCode(HttpStatus.NO_CONTENT_204);
+                .statusCode(NO_CONTENT.code());
         }
     }
 
@@ -144,7 +143,7 @@ class HTTPConfigurationServerTest {
         private ReceivedMailRepository mailRepository;
 
         @BeforeEach
-        void setUp() throws Exception {
+        void setUp() {
             mailRepository = new ReceivedMailRepository();
 
             server = HTTPConfigurationServer.onRandomPort(new SMTPBehaviorRepository(), mailRepository)
@@ -160,7 +159,7 @@ class HTTPConfigurationServerTest {
         }
 
         @AfterEach
-        void tearDown() throws Exception {
+        void tearDown() {
             server.stop();
         }
 
@@ -225,7 +224,7 @@ class HTTPConfigurationServerTest {
             when()
                 .delete()
             .then()
-                .statusCode(SC_NO_CONTENT);
+                .statusCode(NO_CONTENT.code());
         }
 
         @Test


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