You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 10:17:17 UTC

[sling-org-apache-sling-testing-email] 01/05: SLING-6949 - Create testing utilities for email-enabled applications

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

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

commit 2710b1fba47e799aff7d52ed15ea6c5a7973dd0d
Author: Robert Munteanu <ro...@apache.org>
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/testing/email@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
"commits@sling.apache.org" <co...@sling.apache.org>.