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

rombert pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-email.git

commit f5de0a002534fffcbebc8e805ba25199a060d140
Author: Robert Munteanu <[email protected]>
AuthorDate: Fri Jun 9 14:36:36 2017 +0000

    SLING-6949 - Create testing utilities for email-enabled applications
    
    Initial commit of a bundle which starts an SMTP server inside a Sling
    application and then exposes the received messages via HTTP.
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1798227 
13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            | 123 ++++++++++++++++++
 .../sling/testing/email/impl/EMailServlet.java     | 109 ++++++++++++++++
 .../testing/email/impl/SmtpServerWrapper.java      | 114 +++++++++++++++++
 .../sling/testing/email/impl/EmailServletTest.java | 142 +++++++++++++++++++++
 .../testing/email/impl/SmtpServerWrapperTest.java  |  61 +++++++++
 5 files changed, 549 insertions(+)

diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..68e43b8
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>30</version>
+        <relativePath />
+    </parent>
+
+    <artifactId>org.apache.sling.testing.email</artifactId>
+    <version>0.9.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <name>Apache Sling Testing Email Support</name>
+    <description>
+        Contains utilities that assist in validating email-enabled OSGi 
applications.
+    </description>
+    <scm>
+        
<connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/testing/email</connection>
+        <developerConnection> 
scm:svn:https://svn.apache.org/repos/asf/sling/trunk/testing/email</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/testing/email</url>
+    </scm>
+    
+    <build>
+        <plugins>
+             <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <!--
+                            subetha-smpt and subetha-wiser are not OSGi bundles
+                            Since the potential for reuse inside the runtime 
is quite small,
+                            let bnd embed the needed packages inside this 
bundle
+                            
+                            Also embed the felix-utils JSON Writer
+                        -->
+                        
<Conditional-Package>org.subethamail.*,org.apache.felix.utils.json</Conditional-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.subethamail</groupId>
+            <artifactId>subethasmtp-wiser</artifactId>
+            <version>1.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.core</artifactId>
+        </dependency>        
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.http.whiteboard</artifactId>
+            <version>1.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.utils</artifactId>
+            <version>1.9.0</version>
+            <scope>provided</scope>
+        </dependency>
+        
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <version>1.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.testing.clients</artifactId>
+            <version>1.1.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.testing.sling-mock</artifactId>
+            <version>2.2.12</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.jayway.jsonpath</groupId>
+            <artifactId>json-path</artifactId>
+            <version>2.2.0</version>
+            <scope>test</scope>
+        </dependency>        
+    </dependencies>
+</project>
diff --git 
a/src/main/java/org/apache/sling/testing/email/impl/EMailServlet.java 
b/src/main/java/org/apache/sling/testing/email/impl/EMailServlet.java
new file mode 100644
index 0000000..25ff6ac
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/email/impl/EMailServlet.java
@@ -0,0 +1,109 @@
+/*
+ * 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.sling.testing.email.impl;
+
+import static 
org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME;
+import static 
org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT;
+import static 
org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import javax.mail.Header;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.felix.utils.json.JSONWriter;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * Exposes emails stored by the {@link SmtpServerWrapper} through an HTTP API
+ * 
+ * <p>The entry points for the servlet are:
+ * 
+ * <ol>
+ * <li><tt>GET /system/sling/testing/email/config</tt>, which returns 
+ *     a JSON object containing the configuration properties of the {@link 
SmtpServerWrapper}</li>
+ * <li><tt>GET /system/sling/testing/email/messages</tt>, which returns the 
messages
+ *  currently held by the {@link SmtpServerWrapper}</li>
+ * </ol>
+ */
+@Component(service = Servlet.class, 
+                  property = {
+                                  HTTP_WHITEBOARD_SERVLET_PATTERN + 
"=/system/sling/testing/email",
+                                  HTTP_WHITEBOARD_CONTEXT_SELECT + "=(" + 
HTTP_WHITEBOARD_CONTEXT_NAME + "=org.osgi.service.http)"
+                  })
+public class EMailServlet extends HttpServlet {
+       
+       private static final long serialVersionUID = 1L;
+       
+       @Reference
+       private SmtpServerWrapper wiser;
+       
+       @Override
+       protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException, IOException {
+               
+               resp.setContentType("application/json");
+               JSONWriter w = new JSONWriter(resp.getWriter());
+               
+               String action = ( req.getPathInfo() == null || 
req.getPathInfo().isEmpty() ) ? "messages" : req.getPathInfo().substring(1);  
+
+               switch (action) {
+                       case "messages":
+                               w.object();
+                               w.key("messages");
+                               w.array();
+                               for ( MimeMessage msg : wiser.getMessages() ) {
+                                       w.object();
+                                       try {
+                                               Enumeration<?> headers = 
msg.getAllHeaders();
+                                               while ( 
headers.hasMoreElements()) {
+                                                       Header header = 
(Header) headers.nextElement();
+                                                       
w.key(header.getName()).value(header.getValue());
+                                               }
+                                               
+                                               
w.key("-Content-").value(msg.getContent());
+                                               
+                                       } catch (MessagingException e) {
+                                               throw new 
ServletException("Failed retrieving message data", e);
+                                       }
+                                       w.endObject();
+                               }
+                               w.endArray();
+                               w.endObject();
+                               break;
+                       
+                       case "config":
+                               w.object();
+                               
w.key("bindPort").value(wiser.getEffectiveBindPort());
+                               w.endObject();
+                               break;
+                       
+                       default:
+                               
resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+                               break;
+               }
+               
+
+       }
+}
diff --git 
a/src/main/java/org/apache/sling/testing/email/impl/SmtpServerWrapper.java 
b/src/main/java/org/apache/sling/testing/email/impl/SmtpServerWrapper.java
new file mode 100644
index 0000000..046b626
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/email/impl/SmtpServerWrapper.java
@@ -0,0 +1,114 @@
+/*
+ * 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.sling.testing.email.impl;
+
+import java.lang.reflect.Field;
+import java.net.ServerSocket;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.subethamail.smtp.server.SMTPServer;
+import org.subethamail.wiser.Wiser;
+import org.subethamail.wiser.WiserMessage;
+
+// we want the component to be immediate since it is usable outside the OSGi 
service registry
+// via SMTP connections
+@Component(service = SmtpServerWrapper.class, immediate = true)
+@Designate(ocd = SmtpServerWrapper.Config.class)
+public class SmtpServerWrapper {
+       
+       @ObjectClassDefinition(name="Apache Sling Testing SMTP Server Wrapper")
+       public @interface Config {
+               
+               @AttributeDefinition(name="Bind port", description="The port on 
which the server will bind. A value of 0 requests binding on any available port 
")
+               int bind_port() default 0;
+       }
+
+       // wiser is not thread-safe so guard access to the instance
+       private final Object sync = new Object();
+       private Wiser wiser;
+       private int effectiveBindPort;
+
+       @Activate
+       public void activate(Config cfg) throws ReflectiveOperationException {
+               
+               int bindPort;
+               
+               synchronized (sync) {
+                       wiser = new Wiser();
+                       wiser.setPort(cfg.bind_port());
+                       wiser.start();
+                       bindPort = cfg.bind_port() == 0 ? 
reflectiveGetEffectiveBindPort(wiser.getServer()) : cfg.bind_port();
+               }
+               
+               effectiveBindPort = bindPort;
+       }
+
+       private int reflectiveGetEffectiveBindPort(SMTPServer server) throws 
ReflectiveOperationException {
+               
+               // we control the version of Wiser used so there is no risk of 
an exception here 
+               Field field = SMTPServer.class.getDeclaredField("serverSocket");
+               field.setAccessible(true);
+               ServerSocket socket = (ServerSocket) field.get(server);
+
+               return socket.getLocalPort();
+       }
+
+       @Deactivate
+       public void deactivate() {
+               
+               synchronized (sync) {
+                       wiser.stop();
+               }
+       }
+       
+       public int getEffectiveBindPort() {
+               
+               return effectiveBindPort;
+       }
+
+       public void clearMessages() {
+               
+               synchronized (sync) {
+                       wiser.getMessages().clear();    
+               }
+       }
+       
+       public List<MimeMessage> getMessages() {
+               
+               try {
+                       List<MimeMessage> messages = new  ArrayList<>();
+                       synchronized (sync) {
+                               for ( WiserMessage message : 
wiser.getMessages()) {
+                                       messages.add(message.getMimeMessage());
+                               }
+                       }
+                       return messages;
+               } catch (MessagingException e) {
+                       throw new RuntimeException("Failed converting to " + 
MimeMessage.class.getName(), e);
+               }
+       }
+}
diff --git 
a/src/test/java/org/apache/sling/testing/email/impl/EmailServletTest.java 
b/src/test/java/org/apache/sling/testing/email/impl/EmailServletTest.java
new file mode 100644
index 0000000..f07d2d4
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/email/impl/EmailServletTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.sling.testing.email.impl;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+
+import javax.mail.Message.RecipientType;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.servlethelpers.MockSlingHttpServletRequest;
+import org.apache.sling.servlethelpers.MockSlingHttpServletResponse;
+import org.apache.sling.testing.clients.util.PortAllocator;
+import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import com.jayway.jsonpath.JsonPath;
+
+public class EmailServletTest {
+       
+       @Rule
+       public SlingContext ctx = new SlingContext();
+       private int bindPort;
+       private EMailServlet servlet;
+       
+       @Before
+       public void prepare() {
+               
+               bindPort = new PortAllocator().allocatePort();
+               
+               ctx.registerInjectActivateService(new SmtpServerWrapper(), 
Collections.singletonMap("bind.port", bindPort));
+               
+               servlet = ctx.registerInjectActivateService(new EMailServlet());
+       }
+
+       @Test
+       public void getBindPort() throws ServletException, IOException {
+               
+               // SLING-6947
+               MockSlingHttpServletRequest request = new 
MockSlingHttpServletRequest(ctx.resourceResolver()) {
+                       @Override
+                       public String getPathInfo() {
+                               return "/config";
+                       }
+               };
+               
+               MockSlingHttpServletResponse response = new 
MockSlingHttpServletResponse();
+               servlet.service(request, response);
+               
+               assertEquals("response.status", HttpServletResponse.SC_OK, 
response.getStatus());
+               
+               // SLING-6948
+               byte[] out = response.getOutputAsString().getBytes();
+               int configuredPort = JsonPath.read(new 
ByteArrayInputStream(out), "$.bindPort");
+               
+               assertThat("bindPort", configuredPort, equalTo(bindPort));
+       }
+       
+       @Test
+       public void getMessages() throws ServletException, IOException, 
MessagingException {
+               
+               String subject1 = "Test email";
+               String body1 = "A long message \r\nbody";
+               sendEmail(subject1, body1);
+               
+               String subject2 = "Verification email";
+               String body2 = "A shorter message body";
+               sendEmail(subject2, body2);
+               
+               // SLING-6947
+               MockSlingHttpServletRequest request = new 
MockSlingHttpServletRequest(ctx.resourceResolver()) {
+                       @Override
+                       public String getPathInfo() {
+                               return "/messages";
+                       }
+               };
+               
+               MockSlingHttpServletResponse response = new 
MockSlingHttpServletResponse();
+               servlet.service(request, response);
+               
+               assertEquals("response.status", HttpServletResponse.SC_OK, 
response.getStatus());
+               
+               // SLING-6948
+               byte[] out = response.getOutputAsString().getBytes();
+               List<String> subjects = JsonPath.read(new 
ByteArrayInputStream(out), "$.messages[*].Subject");
+               
+               assertThat("subjects.size", subjects, hasSize(2));
+               assertThat("subjects", subjects, Matchers.hasItems(subject1, 
subject2));
+               
+               String readBody = JsonPath.read(new ByteArrayInputStream(out), 
"$.messages[0].['-Content-']");
+               assertThat("body", readBody, equalTo(body1));
+       }
+
+       private void sendEmail(String subject, String body) throws 
MessagingException, AddressException {
+               
+               Properties mailProps = new Properties();
+               mailProps.put("mail.smtp.host", "localhost");
+               mailProps.put("mail.smtp.port", String.valueOf(bindPort));
+               
+               Session mailSession = Session.getInstance(mailProps);
+               
+               MimeMessage msg = new MimeMessage(mailSession);
+               msg.setFrom(new InternetAddress("sender@localhost"));
+               msg.addRecipient(RecipientType.TO, new 
InternetAddress("receiver@localhost"));
+               msg.setSubject(subject);
+               msg.setText(body);
+               
+               Transport.send(msg);
+       }
+}
diff --git 
a/src/test/java/org/apache/sling/testing/email/impl/SmtpServerWrapperTest.java 
b/src/test/java/org/apache/sling/testing/email/impl/SmtpServerWrapperTest.java
new file mode 100644
index 0000000..14e28a8
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/testing/email/impl/SmtpServerWrapperTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.sling.testing.email.impl;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.junit.Assert.assertThat;
+
+import java.util.Collections;
+
+import org.apache.sling.testing.clients.util.PortAllocator;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class SmtpServerWrapperTest {
+       
+       @Rule public OsgiContext ctx = new OsgiContext();
+       
+       private SmtpServerWrapper wrapper;
+
+       @After
+       public void cleanUp() {
+               if ( wrapper != null ) {
+                       wrapper.deactivate();
+               }
+       }
+
+       @Test
+       public void startupWithPreconfiguredPort() throws 
ReflectiveOperationException {
+               
+               final int configuredPort = new PortAllocator().allocatePort();
+               
+               wrapper = ctx.registerInjectActivateService(new 
SmtpServerWrapper(), Collections.singletonMap("bind.port", configuredPort));
+               
+               assertThat("bindPort", wrapper.getEffectiveBindPort(), 
equalTo(configuredPort));
+       }
+       
+       @Test
+       public void startupWithRandomPort() throws ReflectiveOperationException 
{
+               
+               wrapper = ctx.registerInjectActivateService(new 
SmtpServerWrapper(), Collections.singletonMap("bind.port", 0));
+               
+               assertThat("bindPort", wrapper.getEffectiveBindPort(), 
greaterThan(0));
+       }
+}

-- 
To stop receiving notification emails like this one, please contact
"[email protected]" <[email protected]>.

Reply via email to