You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ol...@apache.org on 2019/12/15 18:19:36 UTC

[sling-org-apache-sling-commons-messaging-mail] branch master updated (68e4650 -> fd1350d)

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

olli pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-messaging-mail.git.


    from 68e4650  Updating badges for org-apache-sling-commons-messaging-mail
     new e70c098  SLING-8841 Update to Sling Bundle Parent 35
     new 04518e8  SLING-5644 Provide a messaging implementation based on Commons Email
     new fd1350d  SLING-8920 Provide a simple API and implementation to build and send mails

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 README.md                                          |  62 ++-
 bnd.bnd                                            |  12 -
 pom.xml                                            | 160 ++++---
 .../sling/commons/messaging/mail/MailResult.java   |  41 --
 .../mail/{MailUtil.java => MailService.java}       |  20 +-
 .../commons/messaging/mail/MessageBuilder.java     |  85 ++++
 .../{MailBuilder.java => MessageIdProvider.java}   |   9 +-
 .../messaging/mail/internal/SimpleMailBuilder.java | 109 -----
 .../internal/SimpleMailBuilderConfiguration.java   |  73 ----
 .../messaging/mail/internal/SimpleMailService.java | 124 ++++--
 .../internal/SimpleMailServiceConfiguration.java   |  66 ++-
 .../mail/internal/SimpleMessageBuilder.java        | 473 +++++++++++++++++++++
 .../mail/internal/SimpleMessageIdProvider.java     |  76 ++++
 ...a => SimpleMessageIdProviderConfiguration.java} |  21 +-
 .../messaging/mail/MailBuilderConfigurations.java  |  52 ---
 .../commons/messaging/mail/MailTestSupport.java    |  99 -----
 .../mail/internal/SimpleMailBuilderIT.java         |  93 ----
 .../mail/internal/SimpleMailServiceIT.java         | 112 -----
 .../messaging/mail/it/tests/MailTestSupport.java   | 144 +++++++
 .../mail/it/tests/SimpleMailServiceIT.java         | 414 ++++++++++++++++++
 src/test/resources/SupportApache-small.png         | Bin 0 -> 96596 bytes
 src/test/resources/password                        |   1 +
 src/test/resources/sling.png                       | Bin 0 -> 2957 bytes
 src/test/resources/template-inlines.html           |  38 ++
 src/test/resources/template.html                   |  35 ++
 src/test/resources/template.txt                    |   3 +
 26 files changed, 1615 insertions(+), 707 deletions(-)
 delete mode 100644 src/main/java/org/apache/sling/commons/messaging/mail/MailResult.java
 rename src/main/java/org/apache/sling/commons/messaging/mail/{MailUtil.java => MailService.java} (65%)
 create mode 100644 src/main/java/org/apache/sling/commons/messaging/mail/MessageBuilder.java
 rename src/main/java/org/apache/sling/commons/messaging/mail/{MailBuilder.java => MessageIdProvider.java} (78%)
 delete mode 100644 src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailBuilder.java
 delete mode 100644 src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailBuilderConfiguration.java
 create mode 100644 src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMessageBuilder.java
 create mode 100644 src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMessageIdProvider.java
 copy src/main/java/org/apache/sling/commons/messaging/mail/internal/{SimpleMailServiceConfiguration.java => SimpleMessageIdProviderConfiguration.java} (63%)
 delete mode 100644 src/test/java/org/apache/sling/commons/messaging/mail/MailBuilderConfigurations.java
 delete mode 100644 src/test/java/org/apache/sling/commons/messaging/mail/MailTestSupport.java
 delete mode 100644 src/test/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailBuilderIT.java
 delete mode 100644 src/test/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceIT.java
 create mode 100644 src/test/java/org/apache/sling/commons/messaging/mail/it/tests/MailTestSupport.java
 create mode 100644 src/test/java/org/apache/sling/commons/messaging/mail/it/tests/SimpleMailServiceIT.java
 create mode 100644 src/test/resources/SupportApache-small.png
 create mode 100644 src/test/resources/password
 create mode 100644 src/test/resources/sling.png
 create mode 100644 src/test/resources/template-inlines.html
 create mode 100644 src/test/resources/template.html
 create mode 100644 src/test/resources/template.txt


[sling-org-apache-sling-commons-messaging-mail] 01/03: SLING-8841 Update to Sling Bundle Parent 35

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-messaging-mail.git

commit e70c098e3e3252f7d6ed7473489c905d6ca00eb8
Author: Oliver Lietz <ol...@apache.org>
AuthorDate: Sun Dec 15 19:11:20 2019 +0100

    SLING-8841 Update to Sling Bundle Parent 35
---
 bnd.bnd | 12 ------------
 pom.xml | 14 +++++---------
 2 files changed, 5 insertions(+), 21 deletions(-)

diff --git a/bnd.bnd b/bnd.bnd
index 6f12f39..e36a0d6 100644
--- a/bnd.bnd
+++ b/bnd.bnd
@@ -1,15 +1,3 @@
-Bundle-Category: sling
-
-Bundle-Description: ${project.description}
-
-Bundle-DocURL: https://sling.apache.org
-
-Bundle-License: Apache License, Version 2.0
-
-Bundle-Vendor: The Apache Software Foundation
-
--exportcontents: ${packages;VERSIONED}
-
 -removeheaders:\
   Include-Resource,\
   Private-Package
diff --git a/pom.xml b/pom.xml
index 9d2c8fb..983cc59 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,8 +23,8 @@
 
   <parent>
     <groupId>org.apache.sling</groupId>
-    <artifactId>sling</artifactId>
-    <version>34</version>
+    <artifactId>sling-bundle-parent</artifactId>
+    <version>35</version>
     <relativePath/>
   </parent>
 
@@ -51,15 +51,11 @@
     <plugins>
       <plugin>
         <groupId>biz.aQute.bnd</groupId>
-        <artifactId>bnd-maven-plugin</artifactId>
-      </plugin>
-      <!-- enable baseline after initial release -->
-      <!--
-      <plugin>
-        <groupId>biz.aQute.bnd</groupId>
         <artifactId>bnd-baseline-maven-plugin</artifactId>
+        <configuration>
+          <failOnMissing>false</failOnMissing>
+        </configuration>
       </plugin>
-      -->
       <plugin>
         <groupId>org.apache.servicemix.tooling</groupId>
         <artifactId>depends-maven-plugin</artifactId>


[sling-org-apache-sling-commons-messaging-mail] 03/03: SLING-8920 Provide a simple API and implementation to build and send mails

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-messaging-mail.git

commit fd1350d97590bb335e57e19c3ad8c208670fe316
Author: Oliver Lietz <ol...@apache.org>
AuthorDate: Sun Dec 15 19:18:17 2019 +0100

    SLING-8920 Provide a simple API and implementation to build and send mails
---
 README.md                                          |  62 ++-
 pom.xml                                            | 146 +++++--
 ...lServiceConfiguration.java => MailService.java} |  26 +-
 .../commons/messaging/mail/MessageBuilder.java     |  85 ++++
 ...ceConfiguration.java => MessageIdProvider.java} |  22 +-
 .../messaging/mail/internal/SimpleMailService.java | 124 ++++--
 .../internal/SimpleMailServiceConfiguration.java   |  66 ++-
 .../mail/internal/SimpleMessageBuilder.java        | 473 +++++++++++++++++++++
 .../mail/internal/SimpleMessageIdProvider.java     |  76 ++++
 ...a => SimpleMessageIdProviderConfiguration.java} |  21 +-
 .../messaging/mail/it/tests/MailTestSupport.java   | 144 +++++++
 .../mail/it/tests/SimpleMailServiceIT.java         | 414 ++++++++++++++++++
 src/test/resources/SupportApache-small.png         | Bin 0 -> 96596 bytes
 src/test/resources/password                        |   1 +
 src/test/resources/sling.png                       | Bin 0 -> 2957 bytes
 src/test/resources/template-inlines.html           |  38 ++
 src/test/resources/template.html                   |  35 ++
 src/test/resources/template.txt                    |   3 +
 18 files changed, 1618 insertions(+), 118 deletions(-)

diff --git a/README.md b/README.md
index a055395..3d9141a 100644
--- a/README.md
+++ b/README.md
@@ -6,19 +6,57 @@
 
 This module is part of the [Apache Sling](https://sling.apache.org) project.
 
-Provide an OSGi Configuration for `SimpleMailBuilder` or a custom `MailBuilder` to send messages using [Apache Commons Email](https://commons.apache.org/proper/commons-email/).
+This module provides a simple layer on top of [Jakarta Mail](https://eclipse-ee4j.github.io/mail/) (former [JavaMail](https://javaee.github.io/javamail/)) including a message builder and a service to send mails via SMTPS.
 
-To extend or override `SimpleMailBuilder`​s configuration call `MessageService#send(String, String, Map):Future<Result>` and supply a configuration map `mail` within the third parameter:
+* Mail Service: sends MIME messages.
+* Message Builder: builds plain text and HTML messages with attachments and inline images 
+* Message ID Provider: allows overwriting default message IDs by custom ones
+
+
+## Example
 
 ```
-{
-  "mail" : {
-    "mail.subject": <String>,
-    "mail.from": <String>,
-    "mail.smtp.hostname": <String>,
-    "mail.smtp.port": <int>,
-    "mail.smtp.username": <String>,
-    "mail.smtp.password": <String>
-  }
-}
+    @Reference
+    MailService mailService;
+
+    String subject = "Rudy, A Message to You";
+    String text = "Stop your messing around\nBetter think of your future\nTime you straighten right out\nCreating problems in town\n…";
+    String html = […];
+    byte[] attachment = […];
+    byte[] inline = […];
+
+    MimeMessage message = mailService.getMessageBuilder()
+        .from("dandy.livingstone@kingston.jamaica.example.net", "Dandy Livingstone")
+        .to("the.specials@coventry.england.example.net", "The Specials")
+        .replyTo("rocksteady@jamaica.example.net");
+        .subject(subject)
+        .text(text)
+        .html(html)
+        .attachment(attachment, "image/png", "attachment.png")
+        .inline(inline, "image/png", "inline")
+        .build();
+
+    mailService.sendMessage(message);
 ```
+
+
+## Integration Tests
+
+Integration tests require a running SMTP server. By default a [GreenMail](http://www.icegreen.com/greenmail/) server is started.
+
+An external SMTP server for validating messages with real mail clients can be used by setting required properties:
+
+    mvn clean install\
+      -Dsling.test.mail.smtps.server.external=true\
+      -Dsling.test.mail.smtps.from=envelope-from@example.org\
+      -Dsling.test.mail.smtps.host=localhost\
+      -Dsling.test.mail.smtps.port=465\
+      -Dsling.test.mail.smtps.username=username\
+      -Dsling.test.mail.smtps.password=password\
+      -Dsling.test.mail.from.address=from@example.org\
+      -Dsling.test.mail.from.name=From\ Sender\
+      -Dsling.test.mail.to.address=to@example.org\
+      -Dsling.test.mail.to.name=To\ Recipient\
+      -Dsling.test.mail.replyTo.address=replyto@example.org\
+      -Dsling.test.mail.replyTo.name=Reply\ To
+
diff --git a/pom.xml b/pom.xml
index 983cc59..4a2baf2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,13 +32,13 @@
   <version>0.0.1-SNAPSHOT</version>
 
   <name>Apache Sling Commons Messaging Mail</name>
-  <description>Messaging service using Commons Email to send mails.</description>
+  <description>Send mails via SMTPS</description>
 
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
     <sling.java.version>8</sling.java.version>
-    <org.ops4j.pax.exam.version>4.9.1</org.ops4j.pax.exam.version>
+    <org.ops4j.pax.exam.version>4.13.1</org.ops4j.pax.exam.version>
   </properties>
 
   <scm>
@@ -61,6 +61,16 @@
         <artifactId>depends-maven-plugin</artifactId>
       </plugin>
       <plugin>
+        <groupId>org.apache.rat</groupId>
+        <artifactId>apache-rat-plugin</artifactId>
+        <configuration>
+          <excludes combine.children="append">
+            <exclude>**/*.txt</exclude>
+            <exclude>src/test/resources/password</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-failsafe-plugin</artifactId>
         <executions>
@@ -72,6 +82,7 @@
           </execution>
         </executions>
         <configuration>
+          <redirectTestOutputToFile>true</redirectTestOutputToFile>
           <systemProperties>
             <property>
               <name>bundle.filename</name>
@@ -84,34 +95,45 @@
   </build>
 
   <dependencies>
-    <!-- javax -->
+    <!-- javax/jakarta -->
     <dependency>
       <groupId>javax.inject</groupId>
       <artifactId>javax.inject</artifactId>
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>javax.mail</groupId>
-      <artifactId>javax.mail-api</artifactId>
-      <version>1.5.5</version>
+      <groupId>jakarta.mail</groupId>
+      <artifactId>jakarta.mail-api</artifactId>
+      <version>1.6.4</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.servicemix.specs</groupId>
+      <artifactId>org.apache.servicemix.specs.activation-api-1.1</artifactId>
+      <version>2.9.0</version>
       <scope>provided</scope>
     </dependency>
     <!-- Sun -->
     <dependency>
       <groupId>com.sun.mail</groupId>
-      <artifactId>javax.mail</artifactId>
-      <version>1.5.5</version>
+      <artifactId>jakarta.mail</artifactId>
+      <version>1.6.4</version>
       <scope>provided</scope>
     </dependency>
     <!-- OSGi -->
     <dependency>
       <groupId>org.osgi</groupId>
-      <artifactId>osgi.core</artifactId>
+      <artifactId>org.osgi.annotation.versioning</artifactId>
       <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.osgi</groupId>
-      <artifactId>org.osgi.annotation.versioning</artifactId>
+      <artifactId>osgi.cmpn</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>osgi.core</artifactId>
       <scope>provided</scope>
     </dependency>
     <dependency>
@@ -126,34 +148,35 @@
     </dependency>
     <!-- Apache Commons -->
     <dependency>
-      <groupId>org.apache.commons</groupId>
-      <artifactId>commons-email</artifactId>
-      <version>1.4</version>
-      <scope>provided</scope>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+      <version>2.5</version>
+      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
-      <version>3.4</version>
+      <version>3.9</version>
+      <scope>provided</scope>
     </dependency>
-    <!-- Apache Felix -->
     <dependency>
-      <groupId>org.apache.felix</groupId>
-      <artifactId>org.apache.felix.configadmin</artifactId>
-      <version>1.8.8</version>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-email</artifactId>
+      <version>1.5</version>
       <scope>test</scope>
     </dependency>
+    <!-- Apache Felix -->
     <dependency>
       <groupId>org.apache.felix</groupId>
-      <artifactId>org.apache.felix.scr</artifactId>
-      <version>2.0.2</version>
+      <artifactId>org.apache.felix.framework</artifactId>
+      <version>6.0.3</version>
       <scope>test</scope>
     </dependency>
     <!-- Apache Sling -->
     <dependency>
       <groupId>org.apache.sling</groupId>
-      <artifactId>org.apache.sling.commons.threads</artifactId>
-      <version>3.2.6</version>
+      <artifactId>org.apache.sling.commons.crypto</artifactId>
+      <version>1.0.0-SNAPSHOT</version>
       <scope>provided</scope>
     </dependency>
     <dependency>
@@ -162,6 +185,45 @@
       <version>0.0.1-SNAPSHOT</version>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.commons.threads</artifactId>
+      <version>3.2.6</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.testing.paxexam</artifactId>
+      <version>3.1.0</version>
+      <scope>test</scope>
+    </dependency>
+    <!-- Jasypt -->
+    <dependency>
+      <groupId>org.apache.servicemix.bundles</groupId>
+      <artifactId>org.apache.servicemix.bundles.jasypt</artifactId>
+      <version>1.9.3_1</version>
+      <scope>test</scope>
+    </dependency>
+    <!-- Google -->
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>28.1-jre</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>failureaccess</artifactId>
+      <version>1.0.1</version>
+      <scope>test</scope>
+    </dependency>
+    <!-- Thymeleaf -->
+    <dependency>
+      <groupId>org.thymeleaf</groupId>
+      <artifactId>thymeleaf</artifactId>
+      <version>3.0.11.RELEASE</version>
+      <scope>test</scope>
+    </dependency>
     <!-- logging -->
     <dependency>
       <groupId>org.slf4j</groupId>
@@ -173,7 +235,7 @@
       <artifactId>slf4j-simple</artifactId>
       <scope>test</scope>
     </dependency>
-    <!-- JSR 305-->
+    <!-- nullability -->
     <dependency>
       <groupId>org.jetbrains</groupId>
       <artifactId>annotations</artifactId>
@@ -186,6 +248,24 @@
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>com.google.truth</groupId>
+      <artifactId>truth</artifactId>
+      <version>1.0</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency> <!-- truth dep -->
+      <groupId>com.googlecode.java-diff-utils</groupId>
+      <artifactId>diffutils</artifactId>
+      <version>1.3.0</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.icegreen</groupId>
+      <artifactId>greenmail</artifactId>
+      <version>1.5.11</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
       <groupId>org.ops4j.pax.exam</groupId>
       <artifactId>pax-exam-container-forked</artifactId>
       <version>${org.ops4j.pax.exam.version}</version>
@@ -206,31 +286,19 @@
     <dependency>
       <groupId>org.ops4j.pax.url</groupId>
       <artifactId>pax-url-aether</artifactId>
-      <version>2.4.6</version>
+      <version>2.6.2</version>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.ops4j.pax.url</groupId>
       <artifactId>pax-url-reference</artifactId>
-      <version>2.4.6</version>
+      <version>2.6.2</version>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.ops4j.pax.url</groupId>
       <artifactId>pax-url-wrap</artifactId>
-      <version>2.4.6</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.felix</groupId>
-      <artifactId>org.apache.felix.framework</artifactId>
-      <version>5.4.0</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.subethamail</groupId>
-      <artifactId>subethasmtp</artifactId>
-      <version>3.1.7</version>
+      <version>2.6.2</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
diff --git a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceConfiguration.java b/src/main/java/org/apache/sling/commons/messaging/mail/MailService.java
similarity index 57%
copy from src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceConfiguration.java
copy to src/main/java/org/apache/sling/commons/messaging/mail/MailService.java
index 493041b..258b619 100644
--- a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceConfiguration.java
+++ b/src/main/java/org/apache/sling/commons/messaging/mail/MailService.java
@@ -16,21 +16,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.commons.messaging.mail.internal;
+package org.apache.sling.commons.messaging.mail;
 
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import java.util.concurrent.CompletableFuture;
 
-@ObjectClassDefinition(
-    name = "Apache Sling Commons Messaging Mail “Simple Mail Service”",
-    description = "simple mail service for Sling Commons Messaging"
-)
-@interface SimpleMailServiceConfiguration {
+import javax.mail.internet.MimeMessage;
 
-    @AttributeDefinition(
-        name = "ThreadPool name",
-        description = "name of the ThreadPool to use for sending mails"
-    )
-    String threadpoolName() default "default";
+import org.apache.sling.commons.messaging.MessageService;
+import org.jetbrains.annotations.NotNull;
+import org.osgi.annotation.versioning.ProviderType;
+
+@ProviderType
+public interface MailService extends MessageService<MimeMessage> {
+
+    @NotNull MessageBuilder getMessageBuilder();
+
+    @NotNull CompletableFuture<MimeMessage> sendMessage(@NotNull final MimeMessage message);
 
 }
diff --git a/src/main/java/org/apache/sling/commons/messaging/mail/MessageBuilder.java b/src/main/java/org/apache/sling/commons/messaging/mail/MessageBuilder.java
new file mode 100644
index 0000000..a8f4af3
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/messaging/mail/MessageBuilder.java
@@ -0,0 +1,85 @@
+/*
+ * 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.commons.messaging.mail;
+
+import javax.mail.Header;
+import javax.mail.MessagingException;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.InternetHeaders;
+import javax.mail.internet.MimeMessage;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.annotation.versioning.ProviderType;
+
+@ProviderType
+public interface MessageBuilder {
+
+    @NotNull MessageBuilder header(@NotNull final String name, @Nullable final String value);
+
+    @NotNull MessageBuilder headers(@NotNull final InternetHeaders headers);
+
+    @NotNull MessageBuilder from(@NotNull final InternetAddress from);
+
+    @NotNull MessageBuilder from(@NotNull final String address) throws AddressException;
+
+    @NotNull MessageBuilder from(@NotNull final String address, @NotNull final String name) throws AddressException;
+
+    @NotNull MessageBuilder to(@NotNull final InternetAddress to);
+
+    @NotNull MessageBuilder to(@NotNull final String address) throws AddressException;
+
+    @NotNull MessageBuilder to(@NotNull final String address, @NotNull final String name) throws AddressException;
+
+    @NotNull MessageBuilder cc(@NotNull final InternetAddress cc);
+
+    @NotNull MessageBuilder cc(@NotNull final String address) throws AddressException;
+
+    @NotNull MessageBuilder cc(@NotNull final String address, @NotNull final String name) throws AddressException;
+
+    @NotNull MessageBuilder bcc(@NotNull final InternetAddress bcc);
+
+    @NotNull MessageBuilder bcc(@NotNull final String address) throws AddressException;
+
+    @NotNull MessageBuilder bcc(@NotNull final String address, final String name) throws AddressException;
+
+    @NotNull MessageBuilder replyTo(@NotNull final InternetAddress replyTo);
+
+    @NotNull MessageBuilder replyTo(@NotNull final String address) throws AddressException;
+
+    @NotNull MessageBuilder replyTo(@NotNull final String address, final String name) throws AddressException;
+
+    @NotNull MessageBuilder subject(@NotNull final String subject);
+
+    @NotNull MessageBuilder text(@NotNull final String text);
+
+    @NotNull MessageBuilder html(@NotNull final String html);
+
+    @NotNull MessageBuilder attachment(@NotNull final byte[] content, @NotNull final String type, @NotNull final String filename);
+
+    @NotNull MessageBuilder attachment(@NotNull final byte[] content, @NotNull final String type, @NotNull final String filename, @Nullable Header[] headers);
+
+    @NotNull MessageBuilder inline(@NotNull final byte[] content, @NotNull final String type, @NotNull final String cid);
+
+    @NotNull MessageBuilder inline(@NotNull final byte[] content, @NotNull final String type, @NotNull final String cid, @Nullable Header[] headers);
+
+    @NotNull MimeMessage build() throws MessagingException;
+
+}
diff --git a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceConfiguration.java b/src/main/java/org/apache/sling/commons/messaging/mail/MessageIdProvider.java
similarity index 57%
copy from src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceConfiguration.java
copy to src/main/java/org/apache/sling/commons/messaging/mail/MessageIdProvider.java
index 493041b..4b0eb5b 100644
--- a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceConfiguration.java
+++ b/src/main/java/org/apache/sling/commons/messaging/mail/MessageIdProvider.java
@@ -16,21 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.commons.messaging.mail.internal;
+package org.apache.sling.commons.messaging.mail;
 
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
 
-@ObjectClassDefinition(
-    name = "Apache Sling Commons Messaging Mail “Simple Mail Service”",
-    description = "simple mail service for Sling Commons Messaging"
-)
-@interface SimpleMailServiceConfiguration {
+import org.jetbrains.annotations.NotNull;
+import org.osgi.annotation.versioning.ProviderType;
 
-    @AttributeDefinition(
-        name = "ThreadPool name",
-        description = "name of the ThreadPool to use for sending mails"
-    )
-    String threadpoolName() default "default";
+@ProviderType
+public interface MessageIdProvider {
+
+    @NotNull String getMessageId(@NotNull final MimeMessage message) throws MessagingException;
 
 }
diff --git a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailService.java b/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailService.java
index 20031f1..1e73eab 100644
--- a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailService.java
+++ b/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailService.java
@@ -18,21 +18,23 @@
  */
 package org.apache.sling.commons.messaging.mail.internal;
 
-import java.io.IOException;
-import java.util.Collections;
-import java.util.Map;
+import java.util.List;
+import java.util.Properties;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionException;
 
 import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.event.TransportListener;
+import javax.mail.internet.MimeMessage;
 
-import org.apache.commons.mail.Email;
-import org.apache.commons.mail.EmailException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.commons.crypto.CryptoService;
 import org.apache.sling.commons.messaging.MessageService;
-import org.apache.sling.commons.messaging.Result;
-import org.apache.sling.commons.messaging.mail.MailBuilder;
-import org.apache.sling.commons.messaging.mail.MailResult;
-import org.apache.sling.commons.messaging.mail.MailUtil;
+import org.apache.sling.commons.messaging.mail.MailService;
+import org.apache.sling.commons.messaging.mail.MessageBuilder;
+import org.apache.sling.commons.messaging.mail.MessageIdProvider;
 import org.apache.sling.commons.threads.ThreadPool;
 import org.apache.sling.commons.threads.ThreadPoolManager;
 import org.jetbrains.annotations.NotNull;
@@ -50,23 +52,28 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @Component(
-    service = MessageService.class,
+    service = {
+        MessageService.class,
+        MailService.class
+    },
     property = {
-        Constants.SERVICE_DESCRIPTION + "=Service to send messages by mail.",
-        Constants.SERVICE_VENDOR + "=The Apache Software Foundation"
+        Constants.SERVICE_DESCRIPTION + "=Apache Sling Commons Messaging Mail – Simple Mail Service",
+        Constants.SERVICE_VENDOR + "=The Apache Software Foundation",
+        "protocol=SMTPS"
     }
 )
 @Designate(
-    ocd = SimpleMailServiceConfiguration.class
+    ocd = SimpleMailServiceConfiguration.class,
+    factory = true
 )
-public class SimpleMailService implements MessageService {
+public class SimpleMailService implements MailService {
 
     @Reference(
-        cardinality = ReferenceCardinality.MANDATORY,
+        cardinality = ReferenceCardinality.OPTIONAL,
         policy = ReferencePolicy.DYNAMIC,
         policyOption = ReferencePolicyOption.GREEDY
     )
-    private volatile MailBuilder mailBuilder;
+    private volatile MessageIdProvider messageIdProvider;
 
     @Reference(
         cardinality = ReferenceCardinality.MANDATORY,
@@ -75,9 +82,34 @@ public class SimpleMailService implements MessageService {
     )
     private volatile ThreadPoolManager threadPoolManager;
 
-    // the ThreadPool used for sending mails
+    @Reference(
+        cardinality = ReferenceCardinality.MANDATORY,
+        policy = ReferencePolicy.DYNAMIC,
+        policyOption = ReferencePolicyOption.GREEDY
+    )
+    private volatile CryptoService cryptoService;
+
+    @Reference(
+        cardinality = ReferenceCardinality.MULTIPLE,
+        policy = ReferencePolicy.DYNAMIC,
+        policyOption = ReferencePolicyOption.GREEDY
+    )
+    private volatile List<TransportListener> transportListeners;
+
     private ThreadPool threadPool;
 
+    private SimpleMailServiceConfiguration configuration;
+
+    private Session session;
+
+    private static final String SMTPS_PROTOCOL = "smtps";
+
+    // https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html
+
+    private static final String MAIL_SMTPS_FROM = "mail.smtps.from";
+
+    private static final String MESSAGE_ID_HEADER = "Message-ID";
+
     private final Logger logger = LoggerFactory.getLogger(SimpleMailService.class);
 
     public SimpleMailService() {
@@ -85,46 +117,72 @@ public class SimpleMailService implements MessageService {
 
     @Activate
     private void activate(final SimpleMailServiceConfiguration configuration) {
-        logger.debug("activate");
+        logger.debug("activating");
+        this.configuration = configuration;
         configure(configuration);
     }
 
     @Modified
     private void modified(final SimpleMailServiceConfiguration configuration) {
-        logger.debug("modified");
+        logger.debug("modifying");
+        this.configuration = configuration;
         configure(configuration);
     }
 
     @Deactivate
-    protected void deactivate() {
-        logger.info("deactivate");
+    private void deactivate() {
+        logger.debug("deactivating");
+        this.configuration = null;
         threadPoolManager.release(threadPool);
         threadPool = null;
+        session = null;
     }
 
     private void configure(final SimpleMailServiceConfiguration configuration) {
         threadPoolManager.release(threadPool);
-        threadPool = threadPoolManager.get(configuration.threadpoolName());
+        threadPool = threadPoolManager.get(configuration.threadpool_name());
+
+        final Properties properties = new Properties();
+        final String from = configuration.mail_smtps_from();
+        if (StringUtils.isNotBlank(from)) {
+            properties.setProperty(MAIL_SMTPS_FROM, from);
+        }
+
+        session = Session.getInstance(properties);
     }
 
     @Override
-    public CompletableFuture<Result> send(@NotNull final String message, @NotNull final String recipient) {
-        return send(message, recipient, Collections.emptyMap());
+    public @NotNull MessageBuilder getMessageBuilder() {
+        return new SimpleMessageBuilder(session);
     }
 
     @Override
-    public CompletableFuture<Result> send(@NotNull final String message, @NotNull final String recipient, @NotNull final Map data) {
-        return CompletableFuture.supplyAsync(() -> sendMail(message, recipient, data, mailBuilder), runnable -> threadPool.submit(runnable));
+    public @NotNull CompletableFuture<MimeMessage> sendMessage(@NotNull final MimeMessage message) {
+        return CompletableFuture.supplyAsync(() -> send(message), runnable -> threadPool.submit(runnable));
     }
 
-    private MailResult sendMail(final String message, final String recipient, final Map data, final MailBuilder mailBuilder) {
+    private @NotNull MimeMessage send(@NotNull final MimeMessage message) {
         try {
-            final Email email = mailBuilder.build(message, recipient, data);
-            final String messageId = email.send();
-            logger.info("mail '{}' sent", messageId);
-            final byte[] bytes = MailUtil.toByteArray(email);
-            return new MailResult(bytes);
-        } catch (EmailException | MessagingException | IOException e) {
+            final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+            final String password = cryptoService.decrypt(configuration.password());
+            try (final Transport transport = session.getTransport(SMTPS_PROTOCOL)) {
+                final List<TransportListener> listeners = this.transportListeners;
+                listeners.forEach(transport::addTransportListener);
+                transport.connect(configuration.mail_smtps_host(), configuration.mail_smtps_port(), configuration.username(), password);
+                message.saveChanges();
+                final MessageIdProvider messageIdProvider = this.messageIdProvider;
+                if (messageIdProvider != null) {
+                    final String messageId = messageIdProvider.getMessageId(message);
+                    message.setHeader(MESSAGE_ID_HEADER, String.format("<%s>", messageId));
+                }
+                logger.debug("sending message '{}'", message.getMessageID());
+                transport.sendMessage(message, message.getAllRecipients());
+                return message;
+            } finally {
+                Thread.currentThread().setContextClassLoader(tccl);
+            }
+        } catch (MessagingException e) {
             throw new CompletionException(e);
         }
     }
diff --git a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceConfiguration.java b/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceConfiguration.java
index 493041b..82a79fd 100644
--- a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceConfiguration.java
+++ b/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceConfiguration.java
@@ -19,18 +19,80 @@
 package org.apache.sling.commons.messaging.mail.internal;
 
 import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.AttributeType;
 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
 
 @ObjectClassDefinition(
     name = "Apache Sling Commons Messaging Mail “Simple Mail Service”",
-    description = "simple mail service for Sling Commons Messaging"
+    description = "Simple mail service sending MIME messages via SMTPS"
 )
 @interface SimpleMailServiceConfiguration {
 
     @AttributeDefinition(
+        name = "Names",
+        description = "names of this service",
+        required = false
+    )
+    String[] names() default {"default"};
+
+    @AttributeDefinition(
         name = "ThreadPool name",
         description = "name of the ThreadPool to use for sending mails"
     )
-    String threadpoolName() default "default";
+    String threadpool_name() default "default";
+
+    @AttributeDefinition(
+        name = "SMTP from",
+        description = "from address"
+    )
+    String mail_smtps_from();
+
+    @AttributeDefinition(
+        name = "SMTP host",
+        description = "host of SMTP server"
+    )
+    String mail_smtps_host() default "localhost";
+
+    @AttributeDefinition(
+        name = "SMTP port",
+        description = "port of SMTP server"
+    )
+    int mail_smtps_port() default 465;
+
+    @AttributeDefinition(
+        name = "Username",
+        description = "username for SMTP server"
+    )
+    String username();
+
+    @AttributeDefinition(
+        name = "Password",
+        description = "password for SMTP server",
+        type = AttributeType.PASSWORD
+    )
+    String password();
+
+    @AttributeDefinition(
+        name = "Message ID Provider target",
+        description = "filter expression to target a Message ID Provider",
+        required = false
+    )
+    String messageIdProvider_target();
+
+    @AttributeDefinition(
+        name = "Crypto Service target",
+        description = "filter expression to target a Crypto Service",
+        required = false
+    )
+    String cryptoService_target();
+
+    @AttributeDefinition(
+        name = "Transport Listeners target",
+        description = "filter expression to target Transport Listeners",
+        required = false
+    )
+    String transportListeners_target();
+
+    String webconsole_configurationFactory_nameHint() default "{names} {username}@{mail_smtps_host}:{mail_smtps_port}";
 
 }
diff --git a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMessageBuilder.java b/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMessageBuilder.java
new file mode 100644
index 0000000..57a7b50
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMessageBuilder.java
@@ -0,0 +1,473 @@
+/*
+ * 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.commons.messaging.mail.internal;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.mail.Address;
+import javax.mail.Header;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Part;
+import javax.mail.Session;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.InternetHeaders;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+import javax.mail.util.ByteArrayDataSource;
+
+import org.apache.sling.commons.messaging.mail.MessageBuilder;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class SimpleMessageBuilder implements MessageBuilder {
+
+    private final Session session;
+
+    private InternetHeaders headers = new InternetHeaders();
+
+    private InternetAddress from;
+
+    private List<InternetAddress> toRecipients = new LinkedList<>();
+
+    private List<InternetAddress> ccRecipients = new LinkedList<>();
+
+    private List<InternetAddress> bccRecipients = new LinkedList<>();
+
+    private List<InternetAddress> replyTos = new LinkedList<>();
+
+    private String subject;
+
+    private String text;
+
+    private String html;
+
+    private List<Attachment> attachments = new LinkedList<>();
+
+    private List<Inline> inlines = new LinkedList<>();
+
+    private static final String CONTENT_TYPE_TEXT_HTML = "text/html; charset=utf-8";
+
+    private static final String CONTENT_TYPE_TEXT_PLAIN = "text/plain; charset=utf-8";
+
+    private static final String CHARSET_UTF8 = "utf-8";
+
+    private static final String MULTIPART_SUBTYPE_MIXED = "mixed";
+
+    private static final String MULTIPART_SUBTYPE_ALTERNATIVE = "alternative";
+
+    private static final String MULTIPART_SUBTYPE_RELATED = "related";
+
+    SimpleMessageBuilder(@NotNull final Session session) {
+        this.session = session;
+    }
+
+    @Override
+    public @NotNull MessageBuilder header(@NotNull final String name, @Nullable final String value) {
+        headers.setHeader(name, value);
+        return this;
+    }
+
+    @Override
+    public @NotNull MessageBuilder headers(@NotNull final InternetHeaders headers) {
+        while (headers.getAllHeaders().hasMoreElements()) {
+            final Header header = headers.getAllHeaders().nextElement();
+            this.headers.setHeader(header.getName(), header.getValue());
+        }
+        return this;
+    }
+
+    @Override
+    public @NotNull MessageBuilder from(@NotNull final InternetAddress from) {
+        this.from = from;
+        return this;
+    }
+
+    @Override
+    public @NotNull MessageBuilder from(@NotNull final String address) throws AddressException {
+        final InternetAddress from = new InternetAddress(address);
+        return from(from);
+    }
+
+    @Override
+    public @NotNull MessageBuilder from(@NotNull final String address, @NotNull final String name) throws AddressException {
+        final InternetAddress from = new InternetAddress(address);
+        try {
+            from.setPersonal(name, StandardCharsets.UTF_8.name());
+        } catch (UnsupportedEncodingException e) {
+            //
+        }
+        return from(from);
+    }
+
+    @Override
+    public @NotNull MessageBuilder to(@NotNull final InternetAddress to) {
+        toRecipients.add(to);
+        return this;
+    }
+
+    @Override
+    public @NotNull MessageBuilder to(@NotNull final String address) throws AddressException {
+        final InternetAddress to = new InternetAddress(address);
+        return to(to);
+    }
+
+    @Override
+    public @NotNull MessageBuilder to(@NotNull final String address, @NotNull final String name) throws AddressException {
+        final InternetAddress to = new InternetAddress(address);
+        try {
+            to.setPersonal(name, StandardCharsets.UTF_8.name());
+        } catch (UnsupportedEncodingException e) {
+            //
+        }
+        return to(to);
+    }
+
+    @Override
+    public @NotNull MessageBuilder cc(@NotNull final InternetAddress cc) {
+        ccRecipients.add(cc);
+        return this;
+    }
+
+    @Override
+    public @NotNull MessageBuilder cc(@NotNull final String address) throws AddressException {
+        final InternetAddress cc = new InternetAddress(address);
+        return cc(cc);
+    }
+
+    @Override
+    public @NotNull MessageBuilder cc(@NotNull final String address, @NotNull final String name) throws AddressException {
+        final InternetAddress cc = new InternetAddress(address);
+        try {
+            cc.setPersonal(name, StandardCharsets.UTF_8.name());
+        } catch (UnsupportedEncodingException e) {
+            //
+        }
+        return cc(cc);
+    }
+
+    @Override
+    public @NotNull MessageBuilder bcc(@NotNull final InternetAddress bcc) {
+        bccRecipients.add(bcc);
+        return this;
+    }
+
+    @Override
+    public @NotNull MessageBuilder bcc(@NotNull final String address) throws AddressException {
+        final InternetAddress bcc = new InternetAddress(address);
+        return bcc(bcc);
+    }
+
+    public @NotNull MessageBuilder bcc(@NotNull final String address, final String name) throws AddressException {
+        final InternetAddress bcc = new InternetAddress(address);
+        try {
+            bcc.setPersonal(name, StandardCharsets.UTF_8.name());
+        } catch (UnsupportedEncodingException e) {
+            //
+        }
+        return bcc(bcc);
+    }
+
+    @Override
+    public @NotNull MessageBuilder replyTo(@NotNull final InternetAddress replyTo) {
+        replyTos.add(replyTo);
+        return this;
+    }
+
+    @Override
+    public @NotNull MessageBuilder replyTo(@NotNull final String address) throws AddressException {
+        final InternetAddress replyTo = new InternetAddress(address);
+        return replyTo(replyTo);
+    }
+
+    @Override
+    public @NotNull MessageBuilder replyTo(@NotNull final String address, @NotNull final String name) throws AddressException {
+        final InternetAddress replyTo = new InternetAddress(address);
+        try {
+            replyTo.setPersonal(name, StandardCharsets.UTF_8.name());
+        } catch (UnsupportedEncodingException e) {
+            //
+        }
+        return replyTo(replyTo);
+    }
+
+    @Override
+    public @NotNull MessageBuilder subject(@NotNull final String subject) {
+        this.subject = subject;
+        return this;
+    }
+
+    @Override
+    public @NotNull MessageBuilder text(@NotNull final String text) {
+        this.text = text;
+        return this;
+    }
+
+    @Override
+    public @NotNull MessageBuilder html(@NotNull final String html) {
+        this.html = html;
+        return this;
+    }
+
+    @Override
+    public @NotNull MessageBuilder attachment(@NotNull final byte[] content, @NotNull final String type, @NotNull final String filename) {
+        return attachment(content, type, filename, null);
+    }
+
+    @Override
+    public @NotNull MessageBuilder attachment(@NotNull final byte[] content, @NotNull final String type, @NotNull final String filename, @Nullable Header[] headers) {
+        final Attachment attachment = new Attachment(content, type, filename, null);
+        this.attachments.add(attachment);
+        return this;
+    }
+
+    @Override
+    public @NotNull MessageBuilder inline(@NotNull final byte[] content, @NotNull final String type, @NotNull final String cid) {
+        return inline(content, type, cid, null);
+    }
+
+    @Override
+    public @NotNull MessageBuilder inline(@NotNull final byte[] content, @NotNull final String type, @NotNull final String cid, @Nullable Header[] headers) {
+        final Inline inline = new Inline(content, type, cid, headers);
+        this.inlines.add(inline);
+        return this;
+    }
+
+    private InternetHeaders headers() {
+        return headers;
+    }
+
+    private InternetAddress from() {
+        return from;
+    }
+
+    private List<InternetAddress> to() {
+        return toRecipients;
+    }
+
+    private List<InternetAddress> cc() {
+        return ccRecipients;
+    }
+
+    private List<InternetAddress> bcc() {
+        return bccRecipients;
+    }
+
+    private List<InternetAddress> replyTo() {
+        return replyTos;
+    }
+
+    private String subject() {
+        return subject;
+    }
+
+    private String text() {
+        return text;
+    }
+
+    private String html() {
+        return html;
+    }
+
+    private List<Attachment> attachments() {
+        return attachments;
+    }
+
+    private List<Inline> inlines() {
+        return inlines;
+    }
+
+    private boolean hasText() {
+        return text() != null;
+    }
+
+    private boolean hasHtml() {
+        return html() != null;
+    }
+
+    private boolean hasAttachments() {
+        return !attachments().isEmpty();
+    }
+
+    private boolean hasInlines() {
+        return !inlines().isEmpty();
+    }
+
+    public @NotNull MimeMessage build() throws MessagingException {
+        final MimeMessage message = new MimeMessage(session);
+
+        while (headers().getAllHeaders().hasMoreElements()) {
+            final Header header = headers.getAllHeaders().nextElement();
+            message.setHeader(header.getName(), header.getValue());
+        }
+
+        message.setFrom(from());
+        message.setRecipients(Message.RecipientType.TO, to().toArray(new Address[0]));
+        message.setRecipients(Message.RecipientType.CC, cc().toArray(new Address[0]));
+        message.setRecipients(Message.RecipientType.BCC, bcc().toArray(new Address[0]));
+        message.setReplyTo(replyTos.toArray(new Address[0]));
+        message.setSubject(subject(), StandardCharsets.UTF_8.name());
+
+        if (hasHtml() || hasAttachments() || hasInlines()) {
+            final MimeMultipart content = new MimeMultipart(MULTIPART_SUBTYPE_MIXED);
+
+            if (hasText() && hasHtml()) { // text and html
+                final MimeMultipart alternative = new MimeMultipart(MULTIPART_SUBTYPE_ALTERNATIVE);
+                handleHtmlAndInlines(alternative, html(), inlines());
+                addText(alternative, text());
+                final MimeBodyPart part = new MimeBodyPart();
+                part.setContent(alternative);
+                content.addBodyPart(part);
+            } else if (hasHtml()) { // html only
+                handleHtmlAndInlines(content, html(), inlines());
+            } else { // text only
+                addText(content, text());
+            }
+
+            addAttachments(content, attachments);
+
+            message.setContent(content);
+        } else {
+            message.setText(text(), CHARSET_UTF8);
+        }
+        return message;
+    }
+
+    private static void handleHtmlAndInlines(final MimeMultipart parent, final String html, final List<Inline> inlines) throws MessagingException {
+        if (!inlines.isEmpty()) { // html and inlines
+            final MimeMultipart related = new MimeMultipart(MULTIPART_SUBTYPE_RELATED);
+            addHtml(related, html);
+            addInlines(related, inlines);
+            final MimeBodyPart part = new MimeBodyPart();
+            part.setContent(related);
+            parent.addBodyPart(part);
+        } else { // html
+            addHtml(parent, html);
+        }
+    }
+
+    private static void addText(final MimeMultipart parent, final String text) throws MessagingException {
+        final MimeBodyPart part = new MimeBodyPart();
+        part.setContent(text, CONTENT_TYPE_TEXT_PLAIN);
+        parent.addBodyPart(part);
+    }
+
+    private static void addHtml(final MimeMultipart parent, final String html) throws MessagingException {
+        final MimeBodyPart part = new MimeBodyPart();
+        part.setContent(html, CONTENT_TYPE_TEXT_HTML);
+        parent.addBodyPart(part);
+    }
+
+    private static void addAttachments(final MimeMultipart parent, final List<Attachment> attachments) throws MessagingException {
+        for (final Attachment attachment : attachments) {
+            try (final ByteArrayInputStream inputStream = new ByteArrayInputStream(attachment.content)) {
+                final MimeBodyPart part = new MimeBodyPart();
+                part.setDisposition(Part.ATTACHMENT);
+                part.setFileName(attachment.filename);
+                setDataHandler(part, inputStream, attachment.type);
+                if (attachment.headers != null) {
+                    setHeaders(part, attachment.headers);
+                }
+                parent.addBodyPart(part);
+            } catch (Exception e) {
+                final String message = String.format("Adding attachment failed: %s", attachment.filename);
+                throw new MessagingException(message, e);
+            }
+        }
+    }
+
+    private static void addInlines(final MimeMultipart parent, final List<Inline> inlines) throws MessagingException {
+        for (final Inline inline : inlines) {
+            try (final ByteArrayInputStream inputStream = new ByteArrayInputStream(inline.content)) {
+                final MimeBodyPart part = new MimeBodyPart();
+                part.setDisposition(Part.INLINE);
+                part.setContentID(String.format("<%s>", inline.cid));
+                setDataHandler(part, inputStream, inline.type);
+                if (inline.headers != null) {
+                    setHeaders(part, inline.headers);
+                }
+                parent.addBodyPart(part);
+            } catch (Exception e) {
+                final String message = String.format("Adding inline object failed: %s", inline.cid);
+                throw new MessagingException(message, e);
+            }
+        }
+    }
+
+    private static void setDataHandler(final MimeBodyPart part, final InputStream inputStream, final String type) throws MessagingException, IOException {
+        final DataSource source = new ByteArrayDataSource(inputStream, type);
+        final DataHandler handler = new DataHandler(source);
+        part.setDataHandler(handler);
+    }
+
+    private static void setHeaders(final MimeBodyPart part, final Header[] headers) throws MessagingException {
+        for (final Header header : headers) {
+            part.setHeader(header.getName(), header.getValue());
+        }
+    }
+
+    private static class Attachment {
+
+        final byte[] content;
+
+        final String type;
+
+        final String filename;
+
+        final Header[] headers;
+
+        Attachment(@NotNull final byte[] content, @NotNull final String type, @NotNull final String filename, @Nullable final Header[] headers) {
+            this.content = content;
+            this.type = type;
+            this.filename = filename;
+            this.headers = headers;
+        }
+
+    }
+
+    private static class Inline {
+
+        final byte[] content;
+
+        final String type;
+
+        final String cid;
+
+        final Header[] headers;
+
+        Inline(@NotNull final byte[] content, @NotNull final String type, @NotNull final String cid, @Nullable final Header[] headers) {
+            this.content = content;
+            this.type = type;
+            this.cid = cid;
+            this.headers = headers;
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMessageIdProvider.java b/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMessageIdProvider.java
new file mode 100644
index 0000000..e8c231c
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMessageIdProvider.java
@@ -0,0 +1,76 @@
+/*
+ * 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.commons.messaging.mail.internal;
+
+import java.util.UUID;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.sling.commons.messaging.mail.MessageIdProvider;
+import org.jetbrains.annotations.NotNull;
+import org.osgi.framework.Constants;
+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.component.annotations.Modified;
+import org.osgi.service.metatype.annotations.Designate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(
+    property = {
+        Constants.SERVICE_DESCRIPTION + "=Apache Sling Commons Messaging Mail – Simple Message ID Provider",
+        Constants.SERVICE_VENDOR + "=The Apache Software Foundation"
+    }
+)
+@Designate(
+    ocd = SimpleMessageIdProviderConfiguration.class,
+    factory = true
+)
+public class SimpleMessageIdProvider implements MessageIdProvider {
+
+    private SimpleMessageIdProviderConfiguration configuration;
+
+    private final Logger logger = LoggerFactory.getLogger(SimpleMessageIdProvider.class);
+
+    @Activate
+    private void activate(final SimpleMessageIdProviderConfiguration configuration) {
+        logger.debug("activating");
+        this.configuration = configuration;
+    }
+
+    @Modified
+    private void modified(final SimpleMessageIdProviderConfiguration configuration) {
+        logger.debug("modifying");
+        this.configuration = configuration;
+    }
+
+    @Deactivate
+    private void deactivate() {
+        logger.debug("deactivating");
+        this.configuration = null;
+    }
+
+    @Override
+    public @NotNull String getMessageId(@NotNull MimeMessage message) throws MessagingException {
+        return String.format("%s.%s@%s", UUID.randomUUID().toString(), System.currentTimeMillis(), configuration.host());
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceConfiguration.java b/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMessageIdProviderConfiguration.java
similarity index 63%
copy from src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceConfiguration.java
copy to src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMessageIdProviderConfiguration.java
index 493041b..d098909 100644
--- a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceConfiguration.java
+++ b/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMessageIdProviderConfiguration.java
@@ -22,15 +22,24 @@ import org.osgi.service.metatype.annotations.AttributeDefinition;
 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
 
 @ObjectClassDefinition(
-    name = "Apache Sling Commons Messaging Mail “Simple Mail Service”",
-    description = "simple mail service for Sling Commons Messaging"
+    name = "Apache Sling Commons Messaging Mail “Simple Message ID Provider”",
+    description = "Service to provide a Message ID based on random UUID, timestamp in ms and custom host"
 )
-@interface SimpleMailServiceConfiguration {
+@interface SimpleMessageIdProviderConfiguration {
 
     @AttributeDefinition(
-        name = "ThreadPool name",
-        description = "name of the ThreadPool to use for sending mails"
+        name = "Names",
+        description = "names of this service",
+        required = false
     )
-    String threadpoolName() default "default";
+    String[] names() default {"default"};
+
+    @AttributeDefinition(
+        name = "Host",
+        description = "Host to use in Message ID"
+    )
+    String host() default "localhost";
+
+    String webconsole_configurationFactory_nameHint() default "{names} {host}";
 
 }
diff --git a/src/test/java/org/apache/sling/commons/messaging/mail/it/tests/MailTestSupport.java b/src/test/java/org/apache/sling/commons/messaging/mail/it/tests/MailTestSupport.java
new file mode 100644
index 0000000..6938dee
--- /dev/null
+++ b/src/test/java/org/apache/sling/commons/messaging/mail/it/tests/MailTestSupport.java
@@ -0,0 +1,144 @@
+/*
+ * 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.commons.messaging.mail.it.tests;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.sling.testing.paxexam.SlingOptions;
+import org.apache.sling.testing.paxexam.TestSupport;
+import org.ops4j.pax.exam.options.MavenArtifactProvisionOption;
+import org.ops4j.pax.exam.options.ModifiableCompositeOption;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.thymeleaf.ITemplateEngine;
+import org.thymeleaf.TemplateEngine;
+import org.thymeleaf.TemplateSpec;
+import org.thymeleaf.context.Context;
+import org.thymeleaf.context.IContext;
+import org.thymeleaf.templatemode.TemplateMode;
+
+import static org.apache.sling.testing.paxexam.SlingOptions.backing;
+import static org.apache.sling.testing.paxexam.SlingOptions.scr;
+import static org.apache.sling.testing.paxexam.SlingOptions.slingCommonsThreads;
+import static org.ops4j.pax.exam.CoreOptions.bootClasspathLibrary;
+import static org.ops4j.pax.exam.CoreOptions.composite;
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.wrappedBundle;
+
+public abstract class MailTestSupport extends TestSupport {
+
+    @Inject
+    protected ConfigurationAdmin configurationAdmin;
+
+    private ITemplateEngine templateEngine = new TemplateEngine();
+
+    protected ModifiableCompositeOption baseConfiguration() {
+        return composite(
+            super.baseConfiguration(),
+            // Sling Commons Messaging Mail
+            testBundle("bundle.filename"),
+            mavenBundle().groupId("org.apache.sling").artifactId("org.apache.sling.commons.messaging").versionAsInProject(),
+            mavenBundle().groupId("jakarta.mail").artifactId("jakarta.mail-api").versionAsInProject(),
+            mavenBundle().groupId("com.sun.mail").artifactId("jakarta.mail").versionAsInProject(),
+            mavenBundle().groupId("org.apache.commons").artifactId("commons-lang3").versionAsInProject(),
+            scr(),
+            slingCommonsCrypto(),
+            slingCommonsThreads(),
+            backing(),
+            // testing
+            junitBundles(),
+            wrappedBundle(mavenBundle().groupId("com.google.truth").artifactId("truth").versionAsInProject()),
+            mavenBundle().groupId("com.google.guava").artifactId("guava").versionAsInProject(),
+            mavenBundle().groupId("com.google.guava").artifactId("failureaccess").versionAsInProject(),
+            mavenBundle().groupId("com.googlecode.java-diff-utils").artifactId("diffutils").versionAsInProject(),
+            mavenBundle().groupId("commons-io").artifactId("commons-io").versionAsInProject(),
+            mavenBundle().groupId("org.apache.commons").artifactId("commons-email").versionAsInProject(),
+            greenmail(),
+            thymeleaf()
+        );
+    }
+
+    private static ModifiableCompositeOption greenmail() {
+        final MavenArtifactProvisionOption greenmail = mavenBundle().groupId("com.icegreen").artifactId("greenmail").versionAsInProject();
+        final MavenArtifactProvisionOption slf4j_api = mavenBundle().groupId("org.slf4j").artifactId("slf4j-api").versionAsInProject();
+        final MavenArtifactProvisionOption slf4j_simple = mavenBundle().groupId("org.slf4j").artifactId("slf4j-simple").versionAsInProject();
+        return composite(
+            greenmail,
+            // add GreenMail to boot classpath to allow setting ssl.SocketFactory.provider to GreenMail's DummySSLSocketFactory
+            bootClasspathLibrary(greenmail).afterFramework(),
+            bootClasspathLibrary(slf4j_api).afterFramework(), // GreenMail dependency
+            bootClasspathLibrary(slf4j_simple).afterFramework() // GreenMail dependency
+        );
+    }
+
+    private static ModifiableCompositeOption thymeleaf() {
+        return composite(
+            mavenBundle().groupId("org.apache.servicemix.bundles").artifactId("org.apache.servicemix.bundles.thymeleaf").version(SlingOptions.versionResolver),
+            mavenBundle().groupId("org.attoparser").artifactId("attoparser").version(SlingOptions.versionResolver),
+            mavenBundle().groupId("org.unbescape").artifactId("unbescape").version(SlingOptions.versionResolver),
+            mavenBundle().groupId("org.apache.servicemix.bundles").artifactId("org.apache.servicemix.bundles.ognl").version(SlingOptions.versionResolver),
+            mavenBundle().groupId("org.javassist").artifactId("javassist").version(SlingOptions.versionResolver)
+        );
+    }
+
+    private static ModifiableCompositeOption slingCommonsCrypto() {
+        return composite(
+            mavenBundle().groupId("org.apache.sling").artifactId("org.apache.sling.commons.crypto").versionAsInProject(),
+            mavenBundle().groupId("org.apache.commons").artifactId("commons-lang3").versionAsInProject(),
+            mavenBundle().groupId("org.apache.servicemix.bundles").artifactId("org.apache.servicemix.bundles.jasypt").versionAsInProject()
+        );
+    }
+
+    // helpers for attachments, inline objects and templates
+
+    String getResourceAsString(final String path) throws IOException {
+        try (final InputStream inputStream = getClass().getResourceAsStream(path)) {
+            return IOUtils.toString(inputStream, StandardCharsets.UTF_8);
+        }
+    }
+
+    byte[] getResourceAsByteArray(final String path) throws IOException {
+        try (final InputStream inputStream = getClass().getResourceAsStream(path)) {
+            return IOUtils.toByteArray(inputStream);
+        }
+    }
+
+    String renderHtmlTemplate(final String path, final Map<String, Object> variables) throws IOException {
+        return renderTemplate(path, variables, TemplateMode.HTML);
+    }
+
+    String renderTextTemplate(final String path, final Map<String, Object> variables) throws IOException {
+        return renderTemplate(path, variables, TemplateMode.TEXT);
+    }
+
+    String renderTemplate(final String path, final Map<String, Object> variables, final TemplateMode templateMode) throws IOException {
+        final String template = getResourceAsString(path);
+        final IContext context = new Context(Locale.ENGLISH, variables);
+        final TemplateSpec templateSpec = new TemplateSpec(template, templateMode);
+        return templateEngine.process(templateSpec, context);
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/commons/messaging/mail/it/tests/SimpleMailServiceIT.java b/src/test/java/org/apache/sling/commons/messaging/mail/it/tests/SimpleMailServiceIT.java
new file mode 100644
index 0000000..2557f28
--- /dev/null
+++ b/src/test/java/org/apache/sling/commons/messaging/mail/it/tests/SimpleMailServiceIT.java
@@ -0,0 +1,414 @@
+/*
+ * 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.commons.messaging.mail.it.tests;
+
+import java.io.UnsupportedEncodingException;
+import java.security.Security;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import javax.activation.DataSource;
+import javax.inject.Inject;
+import javax.mail.Message;
+import javax.mail.Session;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+
+import com.icegreen.greenmail.util.DummySSLSocketFactory;
+import com.icegreen.greenmail.util.GreenMail;
+import com.icegreen.greenmail.util.ServerSetup;
+import org.apache.commons.mail.util.MimeMessageParser;
+import org.apache.sling.commons.messaging.MessageService;
+import org.apache.sling.commons.messaging.mail.MailService;
+import org.apache.sling.commons.messaging.mail.MessageBuilder;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.ops4j.pax.exam.util.Filter;
+import org.ops4j.pax.exam.util.PathUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.propagateSystemProperties;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.factoryConfiguration;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class SimpleMailServiceIT extends MailTestSupport {
+
+    private static final boolean local = !Boolean.getBoolean("sling.test.mail.smtps.server.external");
+
+    private static InternetAddress from;
+
+    private static InternetAddress to;
+
+    private static InternetAddress cc;
+
+    private static InternetAddress bcc;
+
+    private static InternetAddress replyTo;
+
+    static {
+        final String from_address = local ? "from@example.org" : System.getProperty("sling.test.mail.from.address");
+        final String from_name = local ? "From Name" : System.getProperty("sling.test.mail.from.name");
+        final String to_address = local ? "to@example.org" : System.getProperty("sling.test.mail.to.address");
+        final String to_name = local ? "To Name" : System.getProperty("sling.test.mail.to.name");
+        final String replyTo_address = local ? "replyto@example.org" : System.getProperty("sling.test.mail.replyTo.address");
+        final String replyTo_name = local ? "ReplyTo Name" : System.getProperty("sling.test.mail.replyTo.name");
+        try {
+            from = new InternetAddress(from_address, from_name);
+            to = new InternetAddress(to_address, to_name);
+            replyTo = new InternetAddress(replyTo_address, replyTo_name);
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private GreenMail greenMail;
+
+    @Inject
+    protected MessageService<MimeMessage> messageService;
+
+    @Inject
+    @Filter(value = "(protocol=SMTPS)")
+    protected MailService mailService;
+
+    @Rule
+    public final ExpectedException exception = ExpectedException.none();
+
+    private final Logger logger = LoggerFactory.getLogger(SimpleMailServiceIT.class);
+
+    @Configuration
+    public Option[] configuration() {
+        final int port = findFreePort();
+        final String path = String.format("%s/src/test/resources/password", PathUtils.getBaseDir());
+        return options(
+            baseConfiguration(),
+            propagateSystemProperties(
+                "sling.test.mail.smtps.server.external",
+                "sling.test.mail.smtps.from",
+                "sling.test.mail.smtps.host",
+                "sling.test.mail.smtps.port",
+                "sling.test.mail.smtps.username",
+                "sling.test.mail.smtps.password",
+                "sling.test.mail.from.address",
+                "sling.test.mail.from.name",
+                "sling.test.mail.to.address",
+                "sling.test.mail.to.name",
+                "sling.test.mail.replyTo.address",
+                "sling.test.mail.replyTo.name"
+            ),
+            factoryConfiguration("org.apache.sling.commons.messaging.mail.internal.SimpleMessageIdProvider")
+                .put("host", "localhost")
+                .asOption(),
+            factoryConfiguration("org.apache.sling.commons.messaging.mail.internal.SimpleMailService")
+                .put("mail.smtps.from", local ? "envelope-from@example.org" : System.getProperty("sling.test.mail.smtps.from"))
+                .put("mail.smtps.host", local ? "localhost" : System.getProperty("sling.test.mail.smtps.host"))
+                .put("mail.smtps.port", local ? port : Integer.getInteger("sling.test.mail.smtps.port"))
+                .put("username", local ? "username" : System.getProperty("sling.test.mail.smtps.username"))
+                .put("password", local ? "OEKPFL5cVJRqVjh4QaDZhvBiqv8wgWBMJ8PGbYHTqev046oV6888mna9w1mIGCXK" : System.getProperty("sling.test.mail.smtps.password"))
+                .asOption(),
+            // Commons Crypto
+            factoryConfiguration("org.apache.sling.commons.crypto.jasypt.internal.JasyptStandardPBEStringCryptoService")
+                .put("algorithm", "PBEWITHHMACSHA512ANDAES_256")
+                .asOption(),
+            factoryConfiguration("org.apache.sling.commons.crypto.jasypt.internal.JasyptRandomIvGeneratorRegistrar")
+                .put("algorithm", "SHA1PRNG")
+                .asOption(),
+            factoryConfiguration("org.apache.sling.commons.crypto.internal.FilePasswordProvider")
+                .put("path", path)
+                .asOption()
+        );
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        logger.info("local server : {}", local);
+        if (local && Objects.isNull(greenMail)) {
+            // set up GreenMail server
+            Security.setProperty("ssl.SocketFactory.provider", DummySSLSocketFactory.class.getName());
+            final org.osgi.service.cm.Configuration[] configurations = configurationAdmin.listConfigurations("(service.factoryPid=org.apache.sling.commons.messaging.mail.internal.SimpleMailService)");
+            final org.osgi.service.cm.Configuration configuration = configurations[0];
+            final int port = (int) configuration.getProperties().get("mail.smtps.port");
+            final ServerSetup serverSetup = new ServerSetup(port, "127.0.0.1", "smtps");
+            greenMail = new GreenMail(serverSetup);
+            greenMail.setUser("username", "password");
+            greenMail.start();
+        }
+    }
+
+    @After
+    public void tearDown() {
+        if (local) {
+            greenMail.stop();
+            greenMail = null;
+        }
+    }
+
+    private MessageBuilder initializeMessageBuilder() {
+        return mailService.getMessageBuilder()
+            .from(from)
+            .to(to)
+            .replyTo(replyTo);
+    }
+
+    @Test
+    public void testMessageService() throws ExecutionException, InterruptedException {
+        exception.expect(ExecutionException.class);
+        assertThat(messageService).isNotNull();
+        final Properties properties = new Properties();
+        final Session session = Session.getDefaultInstance(properties);
+        final MimeMessage message = new MimeMessage(session);
+        final CompletableFuture<MimeMessage> future = messageService.sendMessage(message);
+        future.get();
+    }
+
+    @Test
+    public void testMailService() {
+        assertThat(mailService).isNotNull();
+    }
+
+    @Test
+    public void sendText() throws Exception {
+        final Map<String, Object> variables = Collections.singletonMap("date", new Date());
+        final String subject = "Sling Commons Mail: Text [æåëęïįœøüū] \uD83D\uDCE7";
+        final String text = renderTextTemplate("/template.txt", variables);
+        final MimeMessage message = initializeMessageBuilder()
+            .subject(subject)
+            .text(text)
+            .build();
+
+        final CompletableFuture<MimeMessage> future = mailService.sendMessage(message);
+        future.get();
+
+        if (local) {
+            greenMail.waitForIncomingEmail(1);
+            greenMail.getReceivedMessagesForDomain(to.getAddress());
+            final MimeMessage[] messages = greenMail.getReceivedMessages();
+            final MimeMessage received = messages[0];
+            final MimeMessageParser parser = new MimeMessageParser(message).parse();
+
+            assertThat(received.getMessageID()).endsWith("@localhost>");
+            assertThat(received.getSubject()).isEqualTo(subject);
+            assertThat(received.getFrom()[0]).isEqualTo(from);
+            assertThat(received.getRecipients(Message.RecipientType.TO)[0]).isEqualTo(to);
+            assertThat(received.getReplyTo()[0]).isEqualTo(replyTo);
+
+            assertThat(parser.getPlainContent()).isEqualTo(text);
+
+            assertThat(parser.getAttachmentList()).isEmpty();
+            assertThat(parser.getContentIds()).isEmpty();
+        }
+    }
+
+    @Test
+    public void sendTextAndAttachment() throws Exception {
+        final Map<String, Object> variables = Collections.singletonMap("date", new Date());
+        final String subject = "Sling Commons Mail: Text and Attachment [æåëęïįœøüū] \uD83D\uDCE7";
+        final String text = renderTextTemplate("/template.txt", variables);
+        final byte[] support = getResourceAsByteArray("/SupportApache-small.png");
+        final MimeMessage message = initializeMessageBuilder()
+            .subject(subject)
+            .text(text)
+            .attachment(support, "image/png", "SupportApache-small.png")
+            .build();
+
+        final CompletableFuture<MimeMessage> future = mailService.sendMessage(message);
+        future.get();
+
+        if (local) {
+            greenMail.waitForIncomingEmail(1);
+            final MimeMessage[] messages = greenMail.getReceivedMessages();
+            final MimeMessage received = messages[0];
+            final MimeMessageParser parser = new MimeMessageParser(message).parse();
+
+            assertThat(received.getMessageID()).endsWith("@localhost>");
+            assertThat(received.getSubject()).isEqualTo(subject);
+            assertThat(received.getFrom()[0]).isEqualTo(from);
+            assertThat(received.getRecipients(Message.RecipientType.TO)[0]).isEqualTo(to);
+            assertThat(received.getReplyTo()[0]).isEqualTo(replyTo);
+
+            assertThat(parser.getPlainContent()).isEqualTo(text);
+
+            assertThat(parser.getAttachmentList().get(0).getName()).isEqualTo("SupportApache-small.png");
+            assertThat(parser.getContentIds()).isEmpty();
+        }
+    }
+
+    @Test
+    public void sendHtml() throws Exception {
+        final Map<String, Object> variables = Collections.singletonMap("date", new Date());
+        final String subject = "Sling Commons Mail: HTML [æåëęïįœøüū] \uD83D\uDCE7";
+        final String html = renderHtmlTemplate("/template.html", variables);
+        final MimeMessage message = initializeMessageBuilder()
+            .subject(subject)
+            .html(html)
+            .build();
+
+        final CompletableFuture<MimeMessage> future = mailService.sendMessage(message);
+        future.get();
+
+        if (local) {
+            greenMail.waitForIncomingEmail(1);
+            final MimeMessage[] messages = greenMail.getReceivedMessages();
+            final MimeMessage received = messages[0];
+            final MimeMessageParser parser = new MimeMessageParser(message).parse();
+
+            assertThat(received.getMessageID()).endsWith("@localhost>");
+            assertThat(received.getSubject()).isEqualTo(subject);
+            assertThat(received.getFrom()[0]).isEqualTo(from);
+            assertThat(received.getRecipients(Message.RecipientType.TO)[0]).isEqualTo(to);
+            assertThat(received.getReplyTo()[0]).isEqualTo(replyTo);
+
+            assertThat(parser.getHtmlContent()).isEqualTo(html);
+
+            assertThat(parser.getAttachmentList()).isEmpty();
+            assertThat(parser.getContentIds()).isEmpty();
+        }
+    }
+
+    @Test
+    public void sendHtmlAndAttachment() throws Exception {
+        final Map<String, Object> variables = Collections.singletonMap("date", new Date());
+        final String subject = "Sling Commons Mail: HTML and Attachment [æåëęïįœøüū] \uD83D\uDCE7";
+        final String html = renderHtmlTemplate("/template.html", variables);
+        final byte[] support = getResourceAsByteArray("/SupportApache-small.png");
+        final MimeMessage message = initializeMessageBuilder()
+            .subject(subject)
+            .html(html)
+            .attachment(support, "image/png", "SupportApache-small.png")
+            .build();
+
+        final CompletableFuture<MimeMessage> future = mailService.sendMessage(message);
+        future.get();
+
+        if (local) {
+            greenMail.waitForIncomingEmail(1);
+            final MimeMessage[] messages = greenMail.getReceivedMessages();
+            final MimeMessage received = messages[0];
+            final MimeMessageParser parser = new MimeMessageParser(message).parse();
+
+            assertThat(received.getMessageID()).endsWith("@localhost>");
+            assertThat(received.getSubject()).isEqualTo(subject);
+            assertThat(received.getFrom()[0]).isEqualTo(from);
+            assertThat(received.getRecipients(Message.RecipientType.TO)[0]).isEqualTo(to);
+            assertThat(received.getReplyTo()[0]).isEqualTo(replyTo);
+
+            assertThat(parser.getHtmlContent()).isEqualTo(html);
+
+            assertThat(parser.getAttachmentList().get(0).getName()).isEqualTo("SupportApache-small.png");
+            assertThat(parser.getContentIds()).isEmpty();
+        }
+    }
+
+    @Test
+    public void sendHtmlWithInlineImageAndAttachment() throws Exception {
+        final Map<String, Object> variables = Collections.singletonMap("date", new Date());
+        final String subject = "Sling Commons Mail: HTML with Inline Images and Attachment [æåëęïįœøüū] \uD83D\uDCE7";
+        final String html = renderHtmlTemplate("/template-inlines.html", variables);
+        final byte[] sling = getResourceAsByteArray("/sling.png");
+        final byte[] support = getResourceAsByteArray("/SupportApache-small.png");
+        final MimeMessage message = initializeMessageBuilder()
+            .subject(subject)
+            .html(html)
+            .attachment(support, "image/png", "SupportApache-small.png")
+            .inline(sling, "image/png", "sling")
+            .build();
+
+        final CompletableFuture<MimeMessage> future = mailService.sendMessage(message);
+        future.get();
+
+        if (local) {
+            greenMail.waitForIncomingEmail(1);
+            final MimeMessage[] messages = greenMail.getReceivedMessages();
+            final MimeMessage received = messages[0];
+            final MimeMessageParser parser = new MimeMessageParser(message).parse();
+
+            assertThat(received.getMessageID()).endsWith("@localhost>");
+            assertThat(received.getSubject()).isEqualTo(subject);
+            assertThat(received.getFrom()[0]).isEqualTo(from);
+            assertThat(received.getRecipients(Message.RecipientType.TO)[0]).isEqualTo(to);
+            assertThat(received.getReplyTo()[0]).isEqualTo(replyTo);
+
+            assertThat(parser.getHtmlContent()).isEqualTo(html);
+
+            assertThat(parser.getContentIds()).contains("sling");
+        }
+    }
+
+    @Test
+    public void sendHtmlWithInlineImageAndTextAndAttachment() throws Exception {
+        final Map<String, Object> variables = Collections.singletonMap("date", new Date());
+        final String subject = "Sling Commons Mail: HTML with Inline Images and Text and Attachment [æåëęïįœøüū] \uD83D\uDCE7";
+        final String text = renderTextTemplate("/template.txt", variables);
+        final String html = renderHtmlTemplate("/template-inlines.html", variables);
+        final byte[] sling = getResourceAsByteArray("/sling.png");
+        final byte[] support = getResourceAsByteArray("/SupportApache-small.png");
+        final MimeMessage message = initializeMessageBuilder()
+            .subject(subject)
+            .text(text)
+            .html(html)
+            .attachment(support, "image/png", "SupportApache-small.png")
+            .inline(sling, "image/png", "sling")
+            .build();
+
+        final CompletableFuture<MimeMessage> future = mailService.sendMessage(message);
+        future.get();
+
+        if (local) {
+            greenMail.waitForIncomingEmail(1);
+            final MimeMessage[] messages = greenMail.getReceivedMessages();
+            final MimeMessage received = messages[0];
+            final MimeMessageParser parser = new MimeMessageParser(message).parse();
+
+            assertThat(received.getMessageID()).endsWith("@localhost>");
+            assertThat(received.getSubject()).isEqualTo(subject);
+            assertThat(received.getFrom()[0]).isEqualTo(from);
+            assertThat(received.getRecipients(Message.RecipientType.TO)[0]).isEqualTo(to);
+            assertThat(received.getReplyTo()[0]).isEqualTo(replyTo);
+
+            assertThat(parser.getPlainContent()).isEqualTo(text);
+            assertThat(parser.getHtmlContent()).isEqualTo(html);
+
+            final List<DataSource> sources = parser.getAttachmentList();
+            for (DataSource source : sources) {
+                logger.info("data source: {}, {}", source.getName(), source.getContentType());
+            }
+
+            assertThat(parser.getContentIds()).contains("sling");
+        }
+    }
+
+}
diff --git a/src/test/resources/SupportApache-small.png b/src/test/resources/SupportApache-small.png
new file mode 100644
index 0000000..4a23e05
Binary files /dev/null and b/src/test/resources/SupportApache-small.png differ
diff --git a/src/test/resources/password b/src/test/resources/password
new file mode 100644
index 0000000..ad66ce8
--- /dev/null
+++ b/src/test/resources/password
@@ -0,0 +1 @@
++AQ?aDes!'DBMkrCi:FE6q\sOn=Pbmn=PK8n=PK?
\ No newline at end of file
diff --git a/src/test/resources/sling.png b/src/test/resources/sling.png
new file mode 100644
index 0000000..69163d9
Binary files /dev/null and b/src/test/resources/sling.png differ
diff --git a/src/test/resources/template-inlines.html b/src/test/resources/template-inlines.html
new file mode 100644
index 0000000..33d4e0c
--- /dev/null
+++ b/src/test/resources/template-inlines.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Thymeleaf HTML Mail Template with Inline Image</title>
+</head>
+<body>
+<table>
+  <tr>
+    <td>This message was sent with Apache Sling Commons Messaging Mail. 📧</td>
+  </tr>
+  <tr>
+    <td><img src="cid:sling" width="124" height="63" alt="Apache Sling"></td>
+  </tr>
+  <tr>
+    <td><span data-th-text="${date}">date</span></td>
+  </tr>
+</table>
+</body>
+</html>
diff --git a/src/test/resources/template.html b/src/test/resources/template.html
new file mode 100644
index 0000000..e32bff0
--- /dev/null
+++ b/src/test/resources/template.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>Thymeleaf HTML Mail Template</title>
+</head>
+<body>
+<table>
+  <tr>
+    <td>This message was sent with Apache Sling Commons Messaging Mail. 📧</td>
+  </tr>
+  <tr>
+    <td><span data-th-text="${date}">date</span></td>
+  </tr>
+</table>
+</body>
+</html>
diff --git a/src/test/resources/template.txt b/src/test/resources/template.txt
new file mode 100644
index 0000000..a66ee49
--- /dev/null
+++ b/src/test/resources/template.txt
@@ -0,0 +1,3 @@
+This message was sent with Apache Sling Commons Messaging Mail. 📧
+
+[[${date}]]


[sling-org-apache-sling-commons-messaging-mail] 02/03: SLING-5644 Provide a messaging implementation based on Commons Email

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-messaging-mail.git

commit 04518e828ee05a22068d70c6dd7db591afa85466
Author: Oliver Lietz <ol...@apache.org>
AuthorDate: Sun Dec 15 19:15:52 2019 +0100

    SLING-5644 Provide a messaging implementation based on Commons Email
    
    Remove files related to Commons Email approach
---
 .../sling/commons/messaging/mail/MailBuilder.java  |  33 ------
 .../sling/commons/messaging/mail/MailResult.java   |  41 --------
 .../sling/commons/messaging/mail/MailUtil.java     |  36 -------
 .../messaging/mail/internal/SimpleMailBuilder.java | 109 --------------------
 .../internal/SimpleMailBuilderConfiguration.java   |  73 --------------
 .../messaging/mail/MailBuilderConfigurations.java  |  52 ----------
 .../commons/messaging/mail/MailTestSupport.java    |  99 ------------------
 .../mail/internal/SimpleMailBuilderIT.java         |  93 -----------------
 .../mail/internal/SimpleMailServiceIT.java         | 112 ---------------------
 9 files changed, 648 deletions(-)

diff --git a/src/main/java/org/apache/sling/commons/messaging/mail/MailBuilder.java b/src/main/java/org/apache/sling/commons/messaging/mail/MailBuilder.java
deleted file mode 100644
index 1ad38ed..0000000
--- a/src/main/java/org/apache/sling/commons/messaging/mail/MailBuilder.java
+++ /dev/null
@@ -1,33 +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.sling.commons.messaging.mail;
-
-import java.util.Map;
-
-import org.apache.commons.mail.Email;
-import org.apache.commons.mail.EmailException;
-import org.jetbrains.annotations.NotNull;
-import org.osgi.annotation.versioning.ProviderType;
-
-@ProviderType
-public interface MailBuilder {
-
-    Email build(@NotNull final String message, @NotNull final String recipient, @NotNull final Map data) throws EmailException;
-
-}
diff --git a/src/main/java/org/apache/sling/commons/messaging/mail/MailResult.java b/src/main/java/org/apache/sling/commons/messaging/mail/MailResult.java
deleted file mode 100644
index c6c1b05..0000000
--- a/src/main/java/org/apache/sling/commons/messaging/mail/MailResult.java
+++ /dev/null
@@ -1,41 +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.sling.commons.messaging.mail;
-
-import org.apache.sling.commons.messaging.Result;
-import org.osgi.annotation.versioning.ConsumerType;
-
-@ConsumerType
-public class MailResult implements Result<byte[]> {
-
-    private final byte[] message;
-
-    public MailResult(final byte[] message) {
-        this.message = message;
-    }
-
-    /**
-     * @return the sent message in <a href="https://tools.ietf.org/html/rfc822">RFC 822</a> format
-     */
-    @Override
-    public byte[] getMessage() {
-        return message;
-    }
-
-}
diff --git a/src/main/java/org/apache/sling/commons/messaging/mail/MailUtil.java b/src/main/java/org/apache/sling/commons/messaging/mail/MailUtil.java
deleted file mode 100644
index d258fe2..0000000
--- a/src/main/java/org/apache/sling/commons/messaging/mail/MailUtil.java
+++ /dev/null
@@ -1,36 +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.sling.commons.messaging.mail;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-import javax.mail.MessagingException;
-
-import org.apache.commons.mail.Email;
-
-public class MailUtil {
-
-    public static byte[] toByteArray(final Email email) throws IOException, MessagingException {
-        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        email.getMimeMessage().writeTo(baos);
-        return baos.toByteArray();
-    }
-
-}
diff --git a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailBuilder.java b/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailBuilder.java
deleted file mode 100644
index 263c71b..0000000
--- a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailBuilder.java
+++ /dev/null
@@ -1,109 +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.sling.commons.messaging.mail.internal;
-
-import java.util.Collections;
-import java.util.Map;
-
-import org.apache.commons.mail.Email;
-import org.apache.commons.mail.EmailException;
-import org.apache.commons.mail.SimpleEmail;
-import org.apache.sling.commons.messaging.mail.MailBuilder;
-import org.jetbrains.annotations.NotNull;
-import org.osgi.framework.Constants;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.ConfigurationPolicy;
-import org.osgi.service.component.annotations.Modified;
-import org.osgi.service.metatype.annotations.Designate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Component(
-    service = MailBuilder.class,
-    property = {
-        Constants.SERVICE_DESCRIPTION + "=Service to build simple mails.",
-        Constants.SERVICE_VENDOR + "=The Apache Software Foundation"
-    },
-    configurationPolicy = ConfigurationPolicy.REQUIRE
-)
-@Designate(
-    ocd = SimpleMailBuilderConfiguration.class
-)
-public class SimpleMailBuilder implements MailBuilder {
-
-    // TODO use encryption and support more configuration options
-
-    private SimpleMailBuilderConfiguration configuration;
-
-    private static final String SUBJECT_KEY = "mail.subject";
-
-    private static final String FROM_KEY = "mail.from";
-
-    private static final String CHARSET_KEY = "mail.charset";
-
-    private static final String SMTP_HOSTNAME_KEY = "mail.smtp.hostname";
-
-    private static final String SMTP_PORT_KEY = "mail.smtp.port";
-
-    private static final String SMTP_USERNAME_KEY = "mail.smtp.username";
-
-    private static final String SMTP_PASSWORD_KEY = "mail.smtp.password";
-
-    private final Logger logger = LoggerFactory.getLogger(SimpleMailBuilder.class);
-
-    public SimpleMailBuilder() {
-    }
-
-    @Activate
-    private void activate(final SimpleMailBuilderConfiguration configuration) {
-        logger.debug("activate");
-        this.configuration = configuration;
-    }
-
-    @Modified
-    private void modified(final SimpleMailBuilderConfiguration configuration) {
-        logger.debug("modified");
-        this.configuration = configuration;
-    }
-
-    @Override
-    public Email build(@NotNull final String message, @NotNull final String recipient, @NotNull final Map data) throws EmailException {
-        final Map configuration = (Map) data.getOrDefault("mail", Collections.EMPTY_MAP);
-        final String subject = (String) configuration.getOrDefault(SUBJECT_KEY, this.configuration.subject());
-        final String from = (String) configuration.getOrDefault(FROM_KEY, this.configuration.from());
-        final String charset = (String) configuration.getOrDefault(CHARSET_KEY, this.configuration.charset());
-        final String smtpHostname = (String) configuration.getOrDefault(SMTP_HOSTNAME_KEY, this.configuration.smtpHostname());
-        final int smtpPort = (Integer) configuration.getOrDefault(SMTP_PORT_KEY, this.configuration.smtpPort());
-        final String smtpUsername = (String) configuration.getOrDefault(SMTP_USERNAME_KEY, this.configuration.smtpUsername());
-        final String smtpPassword = (String) configuration.getOrDefault(SMTP_PASSWORD_KEY, this.configuration.smtpPassword());
-
-        final Email email = new SimpleEmail();
-        email.setCharset(charset);
-        email.setMsg(message);
-        email.addTo(recipient);
-        email.setSubject(subject);
-        email.setFrom(from);
-        email.setHostName(smtpHostname);
-        email.setSmtpPort(smtpPort);
-        email.setAuthentication(smtpUsername, smtpPassword);
-        return email;
-    }
-
-}
diff --git a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailBuilderConfiguration.java b/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailBuilderConfiguration.java
deleted file mode 100644
index 4801453..0000000
--- a/src/main/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailBuilderConfiguration.java
+++ /dev/null
@@ -1,73 +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.sling.commons.messaging.mail.internal;
-
-import org.apache.commons.mail.EmailConstants;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-
-@ObjectClassDefinition(
-    name = "Apache Sling Commons Messaging Mail “Simple Mail Builder”",
-    description = "simple mail builder for Sling Commons Messaging Mail"
-)
-@interface SimpleMailBuilderConfiguration {
-
-    @AttributeDefinition(
-        name = "subject",
-        description = "default subject for mails"
-    )
-    String subject();
-
-    @AttributeDefinition(
-        name = "from",
-        description = "default from (sender) address for mails"
-    )
-    String from();
-
-    @AttributeDefinition(
-        name = "charset",
-        description = "charset to use for mails"
-    )
-    String charset() default EmailConstants.UTF_8;
-
-    @AttributeDefinition(
-        name = "SMTP hostname",
-        description = "hostname of SMTP server"
-    )
-    String smtpHostname() default "localhost";
-
-    @AttributeDefinition(
-        name = "SMTP port",
-        description = "port of SMTP server"
-    )
-    int smtpPort() default 25;
-
-    @AttributeDefinition(
-        name = "SMTP username",
-        description = "username for SMTP server"
-    )
-    String smtpUsername();
-
-    @AttributeDefinition(
-        name = "SMTP password",
-        description = "password for SMTP server"
-    )
-    String smtpPassword();
-
-}
diff --git a/src/test/java/org/apache/sling/commons/messaging/mail/MailBuilderConfigurations.java b/src/test/java/org/apache/sling/commons/messaging/mail/MailBuilderConfigurations.java
deleted file mode 100644
index 8950f28..0000000
--- a/src/test/java/org/apache/sling/commons/messaging/mail/MailBuilderConfigurations.java
+++ /dev/null
@@ -1,52 +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.sling.commons.messaging.mail;
-
-import java.util.Dictionary;
-import java.util.Hashtable;
-
-public class MailBuilderConfigurations {
-
-    /**
-     * @return minimal configuration properties for building mails
-     */
-    public static Dictionary<String, Object> minimal() {
-        final Dictionary<String, Object> properties = new Hashtable<>();
-        properties.put("subject", "Rudy, A Message to You");
-        properties.put("from", "dandy.livingstone@kingston.jamaica");
-        properties.put("smtpHostname", "localhost");
-        return properties;
-    }
-
-    /**
-     * @param smtpPort SMTP port to use for sending
-     * @return configuration properties including authentication for sending
-     */
-    public static Dictionary<String, Object> full(final int smtpPort) {
-        final Dictionary<String, Object> properties = new Hashtable<>();
-        properties.put("subject", "Testing the Simple Mail Service");
-        properties.put("from", "sender@example.net");
-        properties.put("smtpHostname", "localhost");
-        properties.put("smtpPort", smtpPort);
-        properties.put("smtpUsername", "test");
-        properties.put("smtpPassword", "test");
-        return properties;
-    }
-
-}
diff --git a/src/test/java/org/apache/sling/commons/messaging/mail/MailTestSupport.java b/src/test/java/org/apache/sling/commons/messaging/mail/MailTestSupport.java
deleted file mode 100644
index 834b3b8..0000000
--- a/src/test/java/org/apache/sling/commons/messaging/mail/MailTestSupport.java
+++ /dev/null
@@ -1,99 +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.sling.commons.messaging.mail;
-
-import java.io.IOException;
-import java.net.ServerSocket;
-import java.util.Dictionary;
-
-import javax.inject.Inject;
-
-import org.ops4j.pax.exam.Option;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static org.ops4j.pax.exam.CoreOptions.bundle;
-import static org.ops4j.pax.exam.CoreOptions.junitBundles;
-import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
-import static org.ops4j.pax.exam.CoreOptions.options;
-import static org.ops4j.pax.exam.CoreOptions.provision;
-import static org.ops4j.pax.exam.CoreOptions.repository;
-import static org.ops4j.pax.exam.CoreOptions.wrappedBundle;
-
-public abstract class MailTestSupport {
-
-    @Inject
-    protected BundleContext bundleContext;
-
-    @Inject
-    protected ConfigurationAdmin configurationAdmin;
-
-    protected final Logger logger = LoggerFactory.getLogger(getClass());
-
-    public MailTestSupport() {
-    }
-
-    protected synchronized int findFreePort() {
-        try {
-            final ServerSocket serverSocket = new ServerSocket(0);
-            final int port = serverSocket.getLocalPort();
-            serverSocket.close();
-            return port;
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    protected void createFactoryConfiguration(final String factoryPid, final Dictionary<String, Object> properties) throws IOException, InterruptedException {
-        final org.osgi.service.cm.Configuration configuration = configurationAdmin.createFactoryConfiguration(factoryPid);
-        configuration.setBundleLocation(null);
-        configuration.update(properties);
-        Thread.sleep(1000);
-        logger.debug("configuration: {}", configurationAdmin.getConfiguration(factoryPid));
-    }
-
-    protected <T> T getService(Class<T> type) {
-        final ServiceReference<T> serviceReference = bundleContext.getServiceReference(type);
-        return bundleContext.getService(serviceReference);
-    }
-
-    protected Option[] baseConfiguration() {
-        final String filename = System.getProperty("bundle.filename");
-        return options(
-            repository("https://repository.apache.org/snapshots/").id("apache-snapshots").allowSnapshots(),
-            junitBundles(),
-            provision(
-                wrappedBundle(mavenBundle().groupId("org.subethamail").artifactId("subethasmtp").versionAsInProject()),
-                mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.configadmin").versionAsInProject(),
-                mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.scr").versionAsInProject(),
-                mavenBundle().groupId("com.sun.mail").artifactId("javax.mail").versionAsInProject(),
-                mavenBundle().groupId("javax.mail").artifactId("javax.mail-api").versionAsInProject(),
-                mavenBundle().groupId("org.apache.commons").artifactId("commons-email").versionAsInProject(),
-                mavenBundle().groupId("org.apache.commons").artifactId("commons-lang3").versionAsInProject(),
-                mavenBundle().groupId("org.apache.sling").artifactId("org.apache.sling.commons.messaging").versionAsInProject(),
-                mavenBundle().groupId("org.apache.sling").artifactId("org.apache.sling.commons.threads").versionAsInProject(),
-                bundle("reference:file:" + filename)
-            )
-        );
-    }
-
-}
diff --git a/src/test/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailBuilderIT.java b/src/test/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailBuilderIT.java
deleted file mode 100644
index 758a9e9..0000000
--- a/src/test/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailBuilderIT.java
+++ /dev/null
@@ -1,93 +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.sling.commons.messaging.mail.internal;
-
-import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.commons.mail.Email;
-import org.apache.sling.commons.messaging.mail.MailBuilder;
-import org.apache.sling.commons.messaging.mail.MailBuilderConfigurations;
-import org.apache.sling.commons.messaging.mail.MailTestSupport;
-import org.apache.sling.commons.messaging.mail.MailUtil;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.ops4j.pax.exam.Configuration;
-import org.ops4j.pax.exam.Option;
-import org.ops4j.pax.exam.junit.PaxExam;
-import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
-import org.ops4j.pax.exam.spi.reactors.PerMethod;
-
-import static org.junit.Assert.assertEquals;
-
-@RunWith(PaxExam.class)
-@ExamReactorStrategy(PerMethod.class)
-public class SimpleMailBuilderIT extends MailTestSupport {
-
-    @Configuration
-    public Option[] configuration() {
-        return baseConfiguration();
-    }
-
-    @Before
-    public void setup() throws Exception {
-        final String factoryPid = "org.apache.sling.commons.messaging.mail.internal.SimpleMailBuilder";
-        final Dictionary<String, Object> properties = MailBuilderConfigurations.minimal();
-        createFactoryConfiguration(factoryPid, properties);
-    }
-
-    @Test
-    public void testBuildWithDefaults() throws Exception {
-        final MailBuilder mailBuilder = getService(MailBuilder.class);
-        final Email email = mailBuilder.build("Stop your messing around, Better think of your future...", "rudy@ghosttown", Collections.emptyMap());
-        email.buildMimeMessage();
-        final byte[] bytes = MailUtil.toByteArray(email);
-        final String mail = new String(bytes, StandardCharsets.UTF_8);
-        logger.debug("mail: " + mail);
-        assertEquals("rudy@ghosttown", email.getToAddresses().get(0).getAddress());
-        assertEquals("Rudy, A Message to You", email.getSubject());
-        assertEquals("dandy.livingstone@kingston.jamaica", email.getFromAddress().getAddress());
-        assertEquals("localhost", email.getHostName());
-        logger.debug(email.getMimeMessage().getContent().toString());
-    }
-
-    @Test
-    public void testBuildWithData() throws Exception {
-        final MailBuilder mailBuilder = getService(MailBuilder.class);
-        final Map<String, String> configuration = new HashMap<>();
-        configuration.put("mail.subject", "Rudy, A Message to You");
-        configuration.put("mail.from", "specials@thespecials.com");
-        final Map data = Collections.singletonMap("mail", configuration);
-        final Email email = mailBuilder.build("A Message to You, Rudy", "rudy@ghosttown", data);
-        email.buildMimeMessage();
-        final byte[] bytes = MailUtil.toByteArray(email);
-        final String mail = new String(bytes, StandardCharsets.UTF_8);
-        logger.debug("mail: " + mail);
-        assertEquals("rudy@ghosttown", email.getToAddresses().get(0).getAddress());
-        assertEquals("Rudy, A Message to You", email.getSubject());
-        assertEquals("specials@thespecials.com", email.getFromAddress().getAddress());
-        assertEquals("localhost", email.getHostName());
-        logger.debug(email.getMimeMessage().getContent().toString());
-    }
-
-}
diff --git a/src/test/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceIT.java b/src/test/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceIT.java
deleted file mode 100644
index 9a8983c..0000000
--- a/src/test/java/org/apache/sling/commons/messaging/mail/internal/SimpleMailServiceIT.java
+++ /dev/null
@@ -1,112 +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.sling.commons.messaging.mail.internal;
-
-import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-
-import javax.mail.AuthenticationFailedException;
-
-import org.apache.commons.lang3.exception.ExceptionUtils;
-import org.apache.sling.commons.messaging.MessageService;
-import org.apache.sling.commons.messaging.Result;
-import org.apache.sling.commons.messaging.mail.MailBuilderConfigurations;
-import org.apache.sling.commons.messaging.mail.MailResult;
-import org.apache.sling.commons.messaging.mail.MailTestSupport;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.ops4j.pax.exam.Configuration;
-import org.ops4j.pax.exam.Option;
-import org.ops4j.pax.exam.junit.PaxExam;
-import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
-import org.ops4j.pax.exam.spi.reactors.PerMethod;
-import org.subethamail.wiser.Wiser;
-
-import static org.junit.Assert.assertTrue;
-
-@RunWith(PaxExam.class)
-@ExamReactorStrategy(PerMethod.class)
-public class SimpleMailServiceIT extends MailTestSupport {
-
-    protected Wiser wiser;
-
-    public static final String FACTORY_PID = "org.apache.sling.commons.messaging.mail.internal.SimpleMailBuilder";
-
-    @Configuration
-    public Option[] configuration() {
-        return baseConfiguration();
-    }
-
-    @Before
-    public void setup() throws Exception {
-        final int smtpPort = findFreePort();
-        wiser = new Wiser(smtpPort);
-        wiser.start();
-    }
-
-    @After
-    public void teardown() {
-        wiser.stop();
-        wiser = null;
-    }
-
-    @Test
-    public void send() throws Exception {
-        final Dictionary<String, Object> properties = MailBuilderConfigurations.full(wiser.getServer().getPort());
-        createFactoryConfiguration(FACTORY_PID, properties);
-        final MessageService messageService = getService(MessageService.class);
-        final CompletableFuture<Result> future = messageService.send("simple test message", "recipient@example.net");
-        final MailResult result = (MailResult) future.get();
-        final String message = new String(result.getMessage(), StandardCharsets.UTF_8);
-        logger.info("message: {}", message); // TODO assert
-    }
-
-    @Test
-    public void sendWithData() throws Exception {
-        final Dictionary<String, Object> properties = MailBuilderConfigurations.full(wiser.getServer().getPort());
-        createFactoryConfiguration(FACTORY_PID, properties);
-        final MessageService messageService = getService(MessageService.class);
-        final Map configuration = Collections.singletonMap("mail.subject", "Testing the Simple Mail Service with a custom subject");
-        final Map data = Collections.singletonMap("mail", configuration);
-        final CompletableFuture<Result> future = messageService.send("simple test message", "recipient@example.net", data);
-        final MailResult result = (MailResult) future.get();
-        final String message = new String(result.getMessage(), StandardCharsets.UTF_8);
-        logger.info("message: {}", message); // TODO assert
-    }
-
-    @Test
-    public void sendWithoutAuthentication() throws Exception {
-        final Dictionary<String, Object> properties = MailBuilderConfigurations.minimal();
-        createFactoryConfiguration(FACTORY_PID, properties);
-        final MessageService messageService = getService(MessageService.class);
-        final CompletableFuture<Result> future = messageService.send("simple test message", "recipient@example.net");
-        try {
-            future.get();
-        } catch (Exception e) {
-            logger.info(e.getMessage(), e);
-            assertTrue(ExceptionUtils.getRootCause(e) instanceof AuthenticationFailedException);
-        }
-    }
-
-}