You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by pk...@apache.org on 2022/04/19 06:09:34 UTC

[logging-log4j2] branch release-2.x updated: [LOG4J2-3362] Adds a SmtpManager compatible with Jakarta EE 9

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

pkarwasz pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/release-2.x by this push:
     new 30e9563fdb [LOG4J2-3362] Adds a SmtpManager compatible with Jakarta EE 9
30e9563fdb is described below

commit 30e9563fdb358bfbc334e6b86d3781fc7b281d74
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Tue Mar 29 22:54:20 2022 +0200

    [LOG4J2-3362] Adds a SmtpManager compatible with Jakarta EE 9
    
    Since javax.mail and jakarta.mail use different package names, as single
    SmtpAppender can use both through different managers.
    
    Unfortunately the implementations com.sun.mail:javax.mail and
    com.sun.mail:jakarta.mail use the same class names, so it is not
    possible to test both implementations at the same time. A separate test
    execution was configured for the tests using jakarta.* classes.
---
 log4j-core/pom.xml                                 |  10 +
 .../logging/log4j/core/appender/SmtpAppender.java  |  68 ++++---
 .../apache/logging/log4j/core/net/MailManager.java | 211 +++++++++++++++++++++
 .../logging/log4j/core/net/MailManagerFactory.java |  26 +++
 .../apache/logging/log4j/core/net/SmtpManager.java | 165 ++++------------
 .../log4j/core/appender/SmtpAppenderTest.java      |  15 +-
 .../logging/log4j/core/net/SmtpManagerTest.java    |  33 +++-
 log4j-jakarta-smtp/pom.xml                         | 194 +++++++++++++++++++
 .../logging/log4j/smtp/MimeMessageBuilder.java     |  93 +++++++++
 .../apache/logging/log4j/smtp}/SmtpManager.java    | 202 ++++----------------
 ...pache.logging.log4j.core.net.MailManagerFactory |   1 +
 .../logging/log4j/smtp/SmtpAppenderAsyncTest.java  |  98 ++++++++++
 .../logging/log4j/smtp}/SmtpAppenderTest.java      |  24 ++-
 .../logging/log4j/smtp}/SmtpManagerTest.java       |  44 +++--
 .../src/test/resources/SmtpAppenderAsyncTest.xml   |  42 ++++
 pom.xml                                            |  40 +++-
 16 files changed, 913 insertions(+), 353 deletions(-)

diff --git a/log4j-core/pom.xml b/log4j-core/pom.xml
index 304eaf3c30..f21e19afa5 100644
--- a/log4j-core/pom.xml
+++ b/log4j-core/pom.xml
@@ -101,6 +101,16 @@
       <optional>true</optional>
     </dependency>
     <!-- Required for SMTPAppender -->
+    <dependency>
+      <groupId>javax.mail</groupId>
+      <artifactId>javax.mail-api</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>javax.activation</groupId>
+      <artifactId>javax.activation-api</artifactId>
+      <optional>true</optional>
+    </dependency>
     <dependency>
       <groupId>com.sun.mail</groupId>
       <artifactId>javax.mail</artifactId>
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SmtpAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SmtpAppender.java
index 7b0ed6cb89..09c6f60f76 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SmtpAppender.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SmtpAppender.java
@@ -18,6 +18,7 @@
 package org.apache.logging.log4j.core.appender;
 
 import java.io.Serializable;
+import java.lang.invoke.MethodHandles;
 
 import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.Core;
@@ -36,11 +37,18 @@ import org.apache.logging.log4j.core.config.plugins.PluginElement;
 import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
 import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidPort;
 import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.core.layout.AbstractStringLayout.Serializer;
 import org.apache.logging.log4j.core.layout.HtmlLayout;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.net.MailManager;
+import org.apache.logging.log4j.core.net.MailManager.FactoryData;
+import org.apache.logging.log4j.core.net.MailManagerFactory;
 import org.apache.logging.log4j.core.net.SmtpManager;
 import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
 import org.apache.logging.log4j.core.util.Booleans;
 import org.apache.logging.log4j.core.util.Integers;
+import org.apache.logging.log4j.util.ServiceLoaderUtil;
+import org.apache.logging.log4j.util.Strings;
 
 /**
  * Send an e-mail when a specific logging event occurs, typically on errors or
@@ -66,14 +74,18 @@ public final class SmtpAppender extends AbstractAppender {
     private static final int DEFAULT_BUFFER_SIZE = 512;
 
     /** The SMTP Manager */
-    private final SmtpManager manager;
+    private final MailManager manager;
 
     private SmtpAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout,
-            final SmtpManager manager, final boolean ignoreExceptions, final Property[] properties) {
+            final MailManager manager, final boolean ignoreExceptions, final Property[] properties) {
         super(name, filter, layout, ignoreExceptions, properties);
         this.manager = manager;
     }
 
+    public MailManager getManager() {
+        return manager;
+    }
+
     /**
      * @since 2.13.2
      */
@@ -261,9 +273,25 @@ public final class SmtpAppender extends AbstractAppender {
             if (getFilter() == null) {
                 setFilter(ThresholdFilter.createFilter(null, null, null));
             }
-            final SmtpManager smtpManager = SmtpManager.getSmtpManager(getConfiguration(), to, cc, bcc, from, replyTo,
-                    subject, smtpProtocol, smtpHost, smtpPort, smtpUsername, smtpPassword, smtpDebug,
-                    getFilter().toString(), bufferSize, sslConfiguration);
+            if (Strings.isEmpty(smtpProtocol)) {
+                smtpProtocol = "smtp";
+            }
+            final Serializer subjectSerializer = PatternLayout.newSerializerBuilder()
+                    .setConfiguration(getConfiguration())
+                    .setPattern(subject)
+                    .build();
+            final FactoryData data = new FactoryData(to, cc, bcc, from, replyTo, subject, subjectSerializer,
+                    smtpProtocol, smtpHost, smtpPort, smtpUsername, smtpPassword, smtpDebug, bufferSize,
+                    sslConfiguration, getFilter().toString());
+            final MailManagerFactory factory = ServiceLoaderUtil.loadServices(MailManagerFactory.class, MethodHandles.lookup())
+                    .findAny()
+                    .orElseGet(() -> SmtpManager.FACTORY);
+            final MailManager smtpManager = AbstractManager.getManager(data.getManagerName(), factory, data);
+            if (smtpManager == null) {
+                LOGGER.error("Unabled to instantiate SmtpAppender named {}", getName());
+                return null;
+            }
+
             return new SmtpAppender(getName(), getFilter(), getLayout(), smtpManager, isIgnoreExceptions(), getPropertyArray());
         }
     }
@@ -305,27 +333,15 @@ public final class SmtpAppender extends AbstractAppender {
             LOGGER.error("No name provided for SmtpAppender");
             return null;
         }
-
-        final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
-        final int smtpPort = AbstractAppender.parseInt(smtpPortStr, 0);
-        final boolean isSmtpDebug = Boolean.parseBoolean(smtpDebug);
-        final int bufferSize = bufferSizeStr == null ? DEFAULT_BUFFER_SIZE : Integers.parseInt(bufferSizeStr);
-
-        if (layout == null) {
-            layout = HtmlLayout.createDefaultLayout();
-        }
-        if (filter == null) {
-            filter = ThresholdFilter.createFilter(null, null, null);
-        }
-        final Configuration configuration = config != null ? config : new DefaultConfiguration();
-
-        final SmtpManager manager = SmtpManager.getSmtpManager(configuration, to, cc, bcc, from, replyTo, subject, smtpProtocol,
-            smtpHost, smtpPort, smtpUsername, smtpPassword, isSmtpDebug, filter.toString(),  bufferSize, null);
-        if (manager == null) {
-            return null;
-        }
-
-        return new SmtpAppender(name, filter, layout, manager, ignoreExceptions, null);
+        return SmtpAppender.newBuilder()
+                .setIgnoreExceptions(Booleans.parseBoolean(ignore, true))
+                .setSmtpPort(AbstractAppender.parseInt(smtpPortStr, 0))
+                .setSmtpDebug(Boolean.parseBoolean(smtpDebug))
+                .setBufferSize(bufferSizeStr == null ? DEFAULT_BUFFER_SIZE : Integers.parseInt(bufferSizeStr))
+                .setLayout(layout)
+                .setFilter(filter)
+                .setConfiguration(config != null ? config : new DefaultConfiguration())
+                .build();
     }
 
     /**
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/MailManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/MailManager.java
new file mode 100644
index 0000000000..f8f07e346a
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/MailManager.java
@@ -0,0 +1,211 @@
+/*
+ * 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.logging.log4j.core.net;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.AbstractManager;
+import org.apache.logging.log4j.core.layout.AbstractStringLayout.Serializer;
+import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
+
+/**
+ * Parent of all managers that send e-mails.
+ *
+ */
+public abstract class MailManager extends AbstractManager {
+
+    /**
+     * Creates a unique-per-configuration name for an smtp manager using the
+     * specified the parameters.<br>
+     * Using such a name allows us to maintain singletons per unique configurations.
+     *
+     * @return smtp manager name
+     */
+    static String createManagerName(final String to, final String cc, final String bcc, final String from,
+            final String replyTo, final String subject, final String smtpProtocol, final String smtpHost,
+            final int smtpPort, final String smtpUsername, final boolean smtpDebug, final String filterName) {
+
+        final StringBuilder sb = new StringBuilder();
+
+        if (to != null) {
+            sb.append(to);
+        }
+        sb.append(':');
+        if (cc != null) {
+            sb.append(cc);
+        }
+        sb.append(':');
+        if (bcc != null) {
+            sb.append(bcc);
+        }
+        sb.append(':');
+        if (from != null) {
+            sb.append(from);
+        }
+        sb.append(':');
+        if (replyTo != null) {
+            sb.append(replyTo);
+        }
+        sb.append(':');
+        if (subject != null) {
+            sb.append(subject);
+        }
+        sb.append(':');
+        sb.append(smtpProtocol).append(':').append(smtpHost).append(':').append(smtpPort).append(':');
+        if (smtpUsername != null) {
+            sb.append(smtpUsername);
+        }
+        sb.append(smtpDebug ? ":debug:" : "::");
+        sb.append(filterName);
+
+        return "SMTP:" + sb.toString();
+    }
+
+    public static class FactoryData {
+        private final String to;
+        private final String cc;
+        private final String bcc;
+        private final String from;
+        private final String replyTo;
+        private final String subject;
+        private final Serializer subjectSerializer;
+        private final String smtpProtocol;
+        private final String smtpHost;
+        private final int smtpPort;
+        private final String smtpUsername;
+        private final String smtpPassword;
+        private final boolean smtpDebug;
+        private final int bufferSize;
+        private final SslConfiguration sslConfiguration;
+        private final String filterName;
+        private final String managerName;
+
+
+
+        public FactoryData(String to, String cc, String bcc, String from, String replyTo, String subject,
+                Serializer subjectSerializer, String smtpProtocol, String smtpHost, int smtpPort, String smtpUsername,
+                String smtpPassword, boolean smtpDebug, int bufferSize, SslConfiguration sslConfiguration,
+                String filterName) {
+            this.to = to;
+            this.cc = cc;
+            this.bcc = bcc;
+            this.from = from;
+            this.replyTo = replyTo;
+            this.subject = subject;
+            this.subjectSerializer = subjectSerializer;
+            this.smtpProtocol = smtpProtocol;
+            this.smtpHost = smtpHost;
+            this.smtpPort = smtpPort;
+            this.smtpUsername = smtpUsername;
+            this.smtpPassword = smtpPassword;
+            this.smtpDebug = smtpDebug;
+            this.bufferSize = bufferSize;
+            this.sslConfiguration = sslConfiguration;
+            this.filterName = filterName;
+            this.managerName = createManagerName(to, cc, bcc, from, replyTo, subject, smtpProtocol, smtpHost, smtpPort,
+                    smtpUsername, smtpDebug, filterName);
+        }
+
+        public String getTo() {
+            return to;
+        }
+
+        public String getCc() {
+            return cc;
+        }
+
+        public String getBcc() {
+            return bcc;
+        }
+
+        public String getFrom() {
+            return from;
+        }
+
+        public String getReplyTo() {
+            return replyTo;
+        }
+
+        public String getSubject() {
+            return subject;
+        }
+
+        public Serializer getSubjectSerializer() {
+            return subjectSerializer;
+        }
+
+        public String getSmtpProtocol() {
+            return smtpProtocol;
+        }
+
+        public String getSmtpHost() {
+            return smtpHost;
+        }
+
+        public int getSmtpPort() {
+            return smtpPort;
+        }
+
+        public String getSmtpUsername() {
+            return smtpUsername;
+        }
+
+        public String getSmtpPassword() {
+            return smtpPassword;
+        }
+
+        public boolean isSmtpDebug() {
+            return smtpDebug;
+        }
+
+        public int getBufferSize() {
+            return bufferSize;
+        }
+
+        public SslConfiguration getSslConfiguration() {
+            return sslConfiguration;
+        }
+
+        public String getFilterName() {
+            return filterName;
+        }
+
+        public String getManagerName() {
+            return managerName;
+        }
+    }
+
+    public MailManager(LoggerContext loggerContext, String name) {
+        super(loggerContext, name);
+    }
+
+    /**
+     * Adds an event to the cyclic buffer.
+     * 
+     * @param event The event to add.
+     */
+    public abstract void add(LogEvent event);
+
+    /**
+     * Send the contents of the cyclic buffer as an e-mail message.
+     * 
+     * @param layout      The layout for formatting the events.
+     * @param appendEvent The event that triggered the send.
+     */
+    public abstract void sendEvents(final Layout<?> layout, final LogEvent appendEvent);
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/MailManagerFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/MailManagerFactory.java
new file mode 100644
index 0000000000..9ef2b500d3
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/MailManagerFactory.java
@@ -0,0 +1,26 @@
+/*
+ * 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.logging.log4j.core.net;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+import org.apache.logging.log4j.core.net.MailManager.FactoryData;
+
+public interface MailManagerFactory extends ManagerFactory<MailManager, FactoryData> {
+
+    @Override
+    MailManager createManager(String name, FactoryData data);
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SmtpManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SmtpManager.java
index af16c9acb8..7e89f9d481 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SmtpManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SmtpManager.java
@@ -40,8 +40,6 @@ import javax.net.ssl.SSLSocketFactory;
 import org.apache.logging.log4j.LoggingException;
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.appender.AbstractManager;
-import org.apache.logging.log4j.core.appender.ManagerFactory;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.layout.AbstractStringLayout.Serializer;
 import org.apache.logging.log4j.core.layout.PatternLayout;
@@ -49,13 +47,12 @@ import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
 import org.apache.logging.log4j.core.util.CyclicBuffer;
 import org.apache.logging.log4j.core.util.NetUtils;
 import org.apache.logging.log4j.util.PropertiesUtil;
-import org.apache.logging.log4j.util.Strings;
 
 /**
  * Manager for sending SMTP events.
  */
-public class SmtpManager extends AbstractManager {
-    private static final SMTPManagerFactory FACTORY = new SMTPManagerFactory();
+public class SmtpManager extends MailManager {
+    public static final SMTPManagerFactory FACTORY = new SMTPManagerFactory();
 
     private final Session session;
 
@@ -67,9 +64,12 @@ public class SmtpManager extends AbstractManager {
 
     private static MimeMessage createMimeMessage(final FactoryData data, final Session session, final LogEvent appendEvent)
             throws MessagingException {
-        return new MimeMessageBuilder(session).setFrom(data.from).setReplyTo(data.replyto)
-                .setRecipients(Message.RecipientType.TO, data.to).setRecipients(Message.RecipientType.CC, data.cc)
-                .setRecipients(Message.RecipientType.BCC, data.bcc).setSubject(data.subject.toSerializable(appendEvent))
+        return new MimeMessageBuilder(session).setFrom(data.getFrom())
+                .setReplyTo(data.getReplyTo())
+                .setRecipients(Message.RecipientType.TO, data.getTo())
+                .setRecipients(Message.RecipientType.CC, data.getCc())
+                .setRecipients(Message.RecipientType.BCC, data.getBcc())
+                .setSubject(data.getSubjectSerializer().toSerializable(appendEvent))
                 .build();
     }
 
@@ -79,13 +79,15 @@ public class SmtpManager extends AbstractManager {
         this.session = session;
         this.message = message;
         this.data = data;
-        this.buffer = new CyclicBuffer<>(LogEvent.class, data.numElements);
+        this.buffer = new CyclicBuffer<>(LogEvent.class, data.getBufferSize());
     }
 
+    @Override
     public void add(LogEvent event) {
         buffer.add(event.toImmutable());
     }
 
+    @Deprecated
     public static SmtpManager getSmtpManager(
                                              final Configuration config,
                                              final String to, final String cc, final String bcc,
@@ -94,79 +96,18 @@ public class SmtpManager extends AbstractManager {
                                              final int port, final String username, final String password,
                                              final boolean isDebug, final String filterName, final int numElements,
                                              final SslConfiguration sslConfiguration) {
-        if (Strings.isEmpty(protocol)) {
-            protocol = "smtp";
-        }
-
-        final String name = createManagerName(to, cc, bcc, from, replyTo, subject, protocol, host, port, username, isDebug, filterName);
-        final Serializer subjectSerializer = PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(subject).build();
+        final Serializer subjectSerializer = PatternLayout.newSerializerBuilder()
+                .setConfiguration(config)
+                .setPattern(subject)
+                .build();
+        final FactoryData data = new FactoryData(to, cc, bcc, from, replyTo, subject, subjectSerializer, protocol, host,
+                port, username, password, isDebug, numElements, sslConfiguration, filterName);
 
-        return getManager(name, FACTORY, new FactoryData(to, cc, bcc, from, replyTo, subjectSerializer,
-            protocol, host, port, username, password, isDebug, numElements, sslConfiguration));
+        return (SmtpManager) getManager(data.getManagerName(), FACTORY, data);
 
     }
 
-    /**
-     * Creates a unique-per-configuration name for an smtp manager using the specified the parameters.<br>
-     * Using such a name allows us to maintain singletons per unique configurations.
-     *
-     * @return smtp manager name
-     */
-    static String createManagerName(
-            final String to,
-            final String cc,
-            final String bcc,
-            final String from,
-            final String replyTo,
-            final String subject,
-            final String protocol,
-            final String host,
-            final int port,
-            final String username,
-            final boolean isDebug,
-            final String filterName) {
-
-        final StringBuilder sb = new StringBuilder();
-
-        if (to != null) {
-            sb.append(to);
-        }
-        sb.append(':');
-        if (cc != null) {
-            sb.append(cc);
-        }
-        sb.append(':');
-        if (bcc != null) {
-            sb.append(bcc);
-        }
-        sb.append(':');
-        if (from != null) {
-            sb.append(from);
-        }
-        sb.append(':');
-        if (replyTo != null) {
-            sb.append(replyTo);
-        }
-        sb.append(':');
-        if (subject != null) {
-            sb.append(subject);
-        }
-        sb.append(':');
-        sb.append(protocol).append(':').append(host).append(':').append(port).append(':');
-        if (username != null) {
-            sb.append(username);
-        }
-        sb.append(isDebug ? ":debug:" : "::");
-        sb.append(filterName);
-
-        return "SMTP:" + sb.toString();
-    }
-
-    /**
-     * Send the contents of the cyclic buffer as an e-mail message.
-     * @param layout The layout for formatting the events.
-     * @param appendEvent The event that triggered the send.
-     */
+    @Override
     public void sendEvents(final Layout<?> layout, final LogEvent appendEvent) {
         if (message == null) {
             connect(appendEvent);
@@ -184,7 +125,7 @@ public class SmtpManager extends AbstractManager {
             final InternetHeaders headers = getHeaders(contentType, encoding);
             final MimeMultipart mp = getMimeMultipart(encodedBytes, headers);
 
-            final String subject = data.subject.toSerializable(appendEvent);
+            final String subject = data.getSubjectSerializer().toSerializable(appendEvent);
 
             sendMultipartMessage(message, mp, subject);
         } catch (final MessagingException | IOException | RuntimeException e) {
@@ -292,46 +233,6 @@ public class SmtpManager extends AbstractManager {
         }
     }
 
-    /**
-     * Factory data.
-     */
-    private static class FactoryData {
-        private final String to;
-        private final String cc;
-        private final String bcc;
-        private final String from;
-        private final String replyto;
-        private final Serializer subject;
-        private final String protocol;
-        private final String host;
-        private final int port;
-        private final String username;
-        private final String password;
-        private final boolean isDebug;
-        private final int numElements;
-        private final SslConfiguration sslConfiguration;
-
-        public FactoryData(final String to, final String cc, final String bcc, final String from, final String replyTo,
-                           final Serializer subjectSerializer, final String protocol, final String host, final int port,
-                           final String username, final String password, final boolean isDebug, final int numElements,
-                           final SslConfiguration sslConfiguration) {
-            this.to = to;
-            this.cc = cc;
-            this.bcc = bcc;
-            this.from = from;
-            this.replyto = replyTo;
-            this.subject = subjectSerializer;
-            this.protocol = protocol;
-            this.host = host;
-            this.port = port;
-            this.username = username;
-            this.password = password;
-            this.isDebug = isDebug;
-            this.numElements = numElements;
-            this.sslConfiguration = sslConfiguration;
-        }
-    }
-
     private synchronized void connect(final LogEvent appendEvent) {
         if (message != null) {
             return;
@@ -347,33 +248,35 @@ public class SmtpManager extends AbstractManager {
     /**
      * Factory to create the SMTP Manager.
      */
-    private static class SMTPManagerFactory implements ManagerFactory<SmtpManager, FactoryData> {
+    public static class SMTPManagerFactory implements MailManagerFactory {
 
         @Override
         public SmtpManager createManager(final String name, final FactoryData data) {
-            final String prefix = "mail." + data.protocol;
+            final String smtpProtocol = data.getSmtpProtocol();
+            final String prefix = "mail." + smtpProtocol;
 
             final Properties properties = PropertiesUtil.getSystemProperties();
-            properties.setProperty("mail.transport.protocol", data.protocol);
+            properties.setProperty("mail.transport.protocol", smtpProtocol);
             if (properties.getProperty("mail.host") == null) {
                 // Prevent an UnknownHostException in Java 7
                 properties.setProperty("mail.host", NetUtils.getLocalHostname());
             }
 
-            if (null != data.host) {
-                properties.setProperty(prefix + ".host", data.host);
+            final String smtpHost = data.getSmtpHost();
+            if (null != smtpHost) {
+                properties.setProperty(prefix + ".host", smtpHost);
             }
-            if (data.port > 0) {
-                properties.setProperty(prefix + ".port", String.valueOf(data.port));
+            if (data.getSmtpPort() > 0) {
+                properties.setProperty(prefix + ".port", String.valueOf(data.getSmtpPort()));
             }
 
-            final Authenticator authenticator = buildAuthenticator(data.username, data.password);
+            final Authenticator authenticator = buildAuthenticator(data.getSmtpUsername(), data.getSmtpPassword());
             if (null != authenticator) {
                 properties.setProperty(prefix + ".auth", "true");
             }
 
-            if (data.protocol.equals("smtps")) {
-                final SslConfiguration sslConfiguration = data.sslConfiguration;
+            if (smtpProtocol.equals("smtps")) {
+                final SslConfiguration sslConfiguration = data.getSslConfiguration();
                 if (sslConfiguration != null) {
                     final SSLSocketFactory sslSocketFactory = sslConfiguration.getSslSocketFactory();
                     properties.put(prefix + ".ssl.socketFactory", sslSocketFactory);
@@ -382,8 +285,8 @@ public class SmtpManager extends AbstractManager {
             }
 
             final Session session = Session.getInstance(properties, authenticator);
-            session.setProtocolForAddress("rfc822", data.protocol);
-            session.setDebug(data.isDebug);
+            session.setProtocolForAddress("rfc822", smtpProtocol);
+            session.setDebug(data.isSmtpDebug());
             return new SmtpManager(name, session, null, data);
         }
 
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SmtpAppenderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SmtpAppenderTest.java
index 2a4cf4cf7a..d0cf61afcf 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SmtpAppenderTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SmtpAppenderTest.java
@@ -16,7 +16,15 @@
  */
 package org.apache.logging.log4j.core.appender;
 
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
 import java.util.Iterator;
+
 import javax.mail.Address;
 import javax.mail.Message;
 import javax.mail.MessagingException;
@@ -30,12 +38,11 @@ import org.apache.logging.log4j.categories.Appenders;
 import org.apache.logging.log4j.core.Logger;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.net.MimeMessageBuilder;
+import org.apache.logging.log4j.core.net.SmtpManager;
 import org.apache.logging.log4j.test.AvailablePortFinder;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
-import static org.junit.Assert.*;
-
 @Category(Appenders.Smtp.class)
 public class SmtpAppenderTest {
 
@@ -125,6 +132,8 @@ public class SmtpAppenderTest {
                 .setSmtpPort(smtpPort)
                 .setBufferSize(3)
                 .build();
+        assertNotNull(appender);
+        assertTrue(appender.getManager() instanceof SmtpManager);
         appender.start();
 
         final LoggerContext context = LoggerContext.getContext();
@@ -174,4 +183,6 @@ public class SmtpAppenderTest {
         assertFalse(body2.contains("Error with exception"));
         assertTrue(body2.contains("Error message #2"));
     }
+
+    
 }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/net/SmtpManagerTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/net/SmtpManagerTest.java
index 5759cf7bbb..224369068d 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/net/SmtpManagerTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/net/SmtpManagerTest.java
@@ -23,6 +23,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.SmtpAppender;
 import org.apache.logging.log4j.core.async.RingBufferLogEvent;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
 import org.apache.logging.log4j.core.impl.MementoMessage;
@@ -33,20 +34,36 @@ import org.apache.logging.log4j.message.ReusableMessage;
 import org.apache.logging.log4j.message.ReusableSimpleMessage;
 import org.junit.jupiter.api.Test;
 
-/**
- * Unit tests for {@link SmtpManager}.
- */
 public class SmtpManagerTest {
 
     @Test
-    void testCreateManagerName() {
+    public void testCreateManagerName() {
         String managerName = SmtpManager.createManagerName("to", "cc", null, "from", null, "LOG4J2-3107",
                 "proto", "smtp.log4j.com", 4711, "username", false, "filter");
         assertEquals("SMTP:to:cc::from::LOG4J2-3107:proto:smtp.log4j.com:4711:username::filter", managerName);
     }
 
     private void testAdd(LogEvent event) {
-        SmtpManager smtpManager = SmtpManager.getSmtpManager(null, "to", "cc", "bcc", "from", "replyTo", "subject", "protocol", "host", 0, "username", "password", false, "filterName", 10, null);
+        final SmtpAppender appender = SmtpAppender.newBuilder()
+                .setName("smtp")
+                .setTo("to")
+                .setCc("cc")
+                .setBcc("bcc")
+                .setFrom("from")
+                .setReplyTo("replyTo")
+                .setSubject("subject")
+                .setSmtpProtocol("smtp")
+                .setSmtpHost("host")
+                .setSmtpPort(0)
+                .setSmtpUsername("username")
+                .setSmtpPassword("password")
+                .setSmtpDebug(false)
+                .setFilter(null)
+                .setBufferSize(10)
+                .build();
+        final MailManager mailManager = appender.getManager();
+        assertThat("is instance of SmtpManager", mailManager instanceof SmtpManager);
+        final SmtpManager smtpManager = (SmtpManager) mailManager;
         smtpManager.removeAllBufferedEvents(); // in case this smtpManager is reused
         smtpManager.add(event);
 
@@ -57,21 +74,21 @@ public class SmtpManagerTest {
 
     // LOG4J2-3172: make sure existing protections are not violated
     @Test
-    void testAdd_WhereLog4jLogEventWithReusableMessage() {
+    public void testAdd_WhereLog4jLogEventWithReusableMessage() {
         LogEvent event = new Log4jLogEvent.Builder().setMessage(getReusableMessage("test message")).build();
         testAdd(event);
     }
 
     // LOG4J2-3172: make sure existing protections are not violated
     @Test
-    void testAdd_WhereMutableLogEvent() {
+    public void testAdd_WhereMutableLogEvent() {
         MutableLogEvent event = new MutableLogEvent(new StringBuilder("test message"), null);
         testAdd(event);
     }
 
     // LOG4J2-3172
     @Test
-    void testAdd_WhereRingBufferLogEvent() {
+    public void testAdd_WhereRingBufferLogEvent() {
         RingBufferLogEvent event = new RingBufferLogEvent();
         event.setValues(null, null, null, null, null, getReusableMessage("test message"), null, null, null, 0, null, 0, null, ClockFactory.getClock(), new DummyNanoClock());
         testAdd(event);
diff --git a/log4j-jakarta-smtp/pom.xml b/log4j-jakarta-smtp/pom.xml
new file mode 100644
index 0000000000..a1c8cfd0df
--- /dev/null
+++ b/log4j-jakarta-smtp/pom.xml
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements. See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache license, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License. You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the license for the specific language governing permissions and
+  ~ limitations under the license.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.logging.log4j</groupId>
+    <artifactId>log4j</artifactId>
+    <version>2.17.3-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>log4j-smtp</artifactId>
+  <name>Apache Log4j SMTP</name>
+  <description>
+    Apache Log4j Simple Mail Transfer Protocol (SMTP) Appender, version for Jakarta EE 9.
+  </description>
+  <properties>
+    <log4jParentDir>${basedir}/..</log4jParentDir>
+    <docLabel>Log4j SMTP Appender Documentation</docLabel>
+    <projectDir>/log4j-jakarta-smtp</projectDir>
+    <maven.doap.skip>true</maven.doap.skip>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+    </dependency>
+    <!-- Required for SMTPAppender -->
+    <dependency>
+      <groupId>jakarta.activation</groupId>
+      <artifactId>jakarta.activation-api</artifactId>
+      <!-- the implementation jar contains the API -->
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>com.sun.activation</groupId>
+      <artifactId>jakarta.activation</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>jakarta.mail</groupId>
+      <artifactId>jakarta.mail-api</artifactId>
+      <!-- the implementation jar contains the API -->
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>com.sun.mail</groupId>
+      <artifactId>smtp</artifactId>
+    </dependency>
+    <!-- Test Dependencies -->
+    <dependency>
+      <groupId>org.junit.vintage</groupId>
+      <artifactId>junit-vintage-engine</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-engine</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <type>test-jar</type>
+    </dependency>
+    <dependency>
+      <groupId>com.lmax</groupId>
+      <artifactId>disruptor</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <configuration>
+          <instructions>
+            <Fragment-Host>org.apache.logging.log4j.core.appender.mom.jeromq</Fragment-Host>
+            <Export-Package>*</Export-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-changes-plugin</artifactId>
+        <version>${changes.plugin.version}</version>
+        <reportSets>
+          <reportSet>
+            <reports>
+              <report>changes-report</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+        <configuration>
+          <issueLinkTemplate>%URL%/show_bug.cgi?id=%ISSUE%</issueLinkTemplate>
+          <useJql>true</useJql>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <version>${checkstyle.plugin.version}</version>
+        <configuration>
+          <!--<propertiesLocation>${vfs.parent.dir}/checkstyle.properties</propertiesLocation> -->
+          <configLocation>${log4jParentDir}/checkstyle.xml</configLocation>
+          <suppressionsLocation>${log4jParentDir}/checkstyle-suppressions.xml</suppressionsLocation>
+          <enableRulesSummary>false</enableRulesSummary>
+          <propertyExpansion>basedir=${basedir}</propertyExpansion>
+          <propertyExpansion>licensedir=${log4jParentDir}/checkstyle-header.txt</propertyExpansion>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <version>${javadoc.plugin.version}</version>
+        <configuration>
+          <bottom><![CDATA[<p align="center">Copyright &#169; {inceptionYear}-{currentYear} {organizationName}. All Rights Reserved.<br />
+            Apache Logging, Apache Log4j, Log4j, Apache, the Apache feather logo, the Apache Logging project logo,
+            and the Apache Log4j logo are trademarks of The Apache Software Foundation.</p>]]></bottom>
+          <!-- module link generation is completely broken in the javadoc plugin for a multi-module non-aggregating project -->
+          <detectOfflineLinks>false</detectOfflineLinks>
+          <linksource>true</linksource>
+          <source>8</source>
+        </configuration>
+        <reportSets>
+          <reportSet>
+            <id>non-aggregate</id>
+            <reports>
+              <report>javadoc</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+      <plugin>
+        <groupId>com.github.spotbugs</groupId>
+        <artifactId>spotbugs-maven-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jxr-plugin</artifactId>
+        <version>${jxr.plugin.version}</version>
+        <reportSets>
+          <reportSet>
+            <id>non-aggregate</id>
+            <reports>
+              <report>jxr</report>
+            </reports>
+          </reportSet>
+          <reportSet>
+            <id>aggregate</id>
+            <reports>
+              <report>aggregate</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-pmd-plugin</artifactId>
+        <version>${pmd.plugin.version}</version>
+        <configuration>
+          <targetJdk>${maven.compiler.target}</targetJdk>
+        </configuration>
+      </plugin>
+    </plugins>
+  </reporting>
+</project>
\ No newline at end of file
diff --git a/log4j-jakarta-smtp/src/main/java/org/apache/logging/log4j/smtp/MimeMessageBuilder.java b/log4j-jakarta-smtp/src/main/java/org/apache/logging/log4j/smtp/MimeMessageBuilder.java
new file mode 100644
index 0000000000..4e99326a9b
--- /dev/null
+++ b/log4j-jakarta-smtp/src/main/java/org/apache/logging/log4j/smtp/MimeMessageBuilder.java
@@ -0,0 +1,93 @@
+/*
+ * 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.logging.log4j.smtp;
+
+import java.nio.charset.StandardCharsets;
+
+import jakarta.mail.Message;
+import jakarta.mail.MessagingException;
+import jakarta.mail.Session;
+import jakarta.mail.internet.AddressException;
+import jakarta.mail.internet.InternetAddress;
+import jakarta.mail.internet.MimeMessage;
+
+import org.apache.logging.log4j.core.util.Builder;
+
+/**
+ * Builder for {@link MimeMessage} instances.
+ */
+public class MimeMessageBuilder implements Builder<MimeMessage> {
+    private final MimeMessage message;
+
+    public MimeMessageBuilder(final Session session) {
+        message = new MimeMessage(session);
+    }
+
+    public MimeMessageBuilder setFrom(final String from) throws MessagingException {
+        final InternetAddress address = parseAddress(from);
+
+        if (null != address) {
+            message.setFrom(address);
+        } else {
+            try {
+                message.setFrom();
+            } catch (final Exception ex) {
+                message.setFrom((InternetAddress) null);
+            }
+        }
+        return this;
+    }
+
+    public MimeMessageBuilder setReplyTo(final String replyTo) throws MessagingException {
+        final InternetAddress[] addresses = parseAddresses(replyTo);
+
+        if (null != addresses) {
+            message.setReplyTo(addresses);
+        }
+        return this;
+    }
+
+    public MimeMessageBuilder setRecipients(final Message.RecipientType recipientType, final String recipients)
+        throws MessagingException {
+        final InternetAddress[] addresses = parseAddresses(recipients);
+
+        if (null != addresses) {
+            message.setRecipients(recipientType, addresses);
+        }
+        return this;
+    }
+
+    public MimeMessageBuilder setSubject(final String subject) throws MessagingException {
+        if (subject != null) {
+            message.setSubject(subject, StandardCharsets.UTF_8.name());
+        }
+        return this;
+    }
+
+    @Override
+    public MimeMessage build() {
+        return message;
+    }
+
+    private static InternetAddress parseAddress(final String address) throws AddressException {
+        return address == null ? null : new InternetAddress(address);
+    }
+
+    private static InternetAddress[] parseAddresses(final String addresses) throws AddressException {
+        return addresses == null ? null : InternetAddress.parse(addresses, true);
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SmtpManager.java b/log4j-jakarta-smtp/src/main/java/org/apache/logging/log4j/smtp/SmtpManager.java
similarity index 58%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/net/SmtpManager.java
copy to log4j-jakarta-smtp/src/main/java/org/apache/logging/log4j/smtp/SmtpManager.java
index af16c9acb8..4c838b5b8f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SmtpManager.java
+++ b/log4j-jakarta-smtp/src/main/java/org/apache/logging/log4j/smtp/SmtpManager.java
@@ -14,7 +14,7 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.net;
+package org.apache.logging.log4j.smtp;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -22,40 +22,36 @@ import java.io.OutputStream;
 import java.util.Date;
 import java.util.Properties;
 
-import javax.activation.DataSource;
-import javax.mail.Authenticator;
-import javax.mail.Message;
-import javax.mail.MessagingException;
-import javax.mail.PasswordAuthentication;
-import javax.mail.Session;
-import javax.mail.Transport;
-import javax.mail.internet.InternetHeaders;
-import javax.mail.internet.MimeBodyPart;
-import javax.mail.internet.MimeMessage;
-import javax.mail.internet.MimeMultipart;
-import javax.mail.internet.MimeUtility;
-import javax.mail.util.ByteArrayDataSource;
 import javax.net.ssl.SSLSocketFactory;
 
+import jakarta.activation.DataSource;
+import jakarta.mail.Authenticator;
+import jakarta.mail.Message;
+import jakarta.mail.MessagingException;
+import jakarta.mail.PasswordAuthentication;
+import jakarta.mail.Session;
+import jakarta.mail.Transport;
+import jakarta.mail.internet.InternetHeaders;
+import jakarta.mail.internet.MimeBodyPart;
+import jakarta.mail.internet.MimeMessage;
+import jakarta.mail.internet.MimeMultipart;
+import jakarta.mail.internet.MimeUtility;
+import jakarta.mail.util.ByteArrayDataSource;
+
 import org.apache.logging.log4j.LoggingException;
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.appender.AbstractManager;
-import org.apache.logging.log4j.core.appender.ManagerFactory;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.layout.AbstractStringLayout.Serializer;
-import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.net.MailManager;
+import org.apache.logging.log4j.core.net.MailManagerFactory;
 import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
 import org.apache.logging.log4j.core.util.CyclicBuffer;
 import org.apache.logging.log4j.core.util.NetUtils;
 import org.apache.logging.log4j.util.PropertiesUtil;
-import org.apache.logging.log4j.util.Strings;
 
 /**
  * Manager for sending SMTP events.
  */
-public class SmtpManager extends AbstractManager {
-    private static final SMTPManagerFactory FACTORY = new SMTPManagerFactory();
+public class SmtpManager extends MailManager {
 
     private final Session session;
 
@@ -67,9 +63,12 @@ public class SmtpManager extends AbstractManager {
 
     private static MimeMessage createMimeMessage(final FactoryData data, final Session session, final LogEvent appendEvent)
             throws MessagingException {
-        return new MimeMessageBuilder(session).setFrom(data.from).setReplyTo(data.replyto)
-                .setRecipients(Message.RecipientType.TO, data.to).setRecipients(Message.RecipientType.CC, data.cc)
-                .setRecipients(Message.RecipientType.BCC, data.bcc).setSubject(data.subject.toSerializable(appendEvent))
+        return new MimeMessageBuilder(session).setFrom(data.getFrom())
+                .setReplyTo(data.getReplyTo())
+                .setRecipients(Message.RecipientType.TO, data.getTo())
+                .setRecipients(Message.RecipientType.CC, data.getCc())
+                .setRecipients(Message.RecipientType.BCC, data.getBcc())
+                .setSubject(data.getSubjectSerializer().toSerializable(appendEvent))
                 .build();
     }
 
@@ -79,94 +78,15 @@ public class SmtpManager extends AbstractManager {
         this.session = session;
         this.message = message;
         this.data = data;
-        this.buffer = new CyclicBuffer<>(LogEvent.class, data.numElements);
+        this.buffer = new CyclicBuffer<>(LogEvent.class, data.getBufferSize());
     }
 
+    @Override
     public void add(LogEvent event) {
         buffer.add(event.toImmutable());
     }
 
-    public static SmtpManager getSmtpManager(
-                                             final Configuration config,
-                                             final String to, final String cc, final String bcc,
-                                             final String from, final String replyTo,
-                                             final String subject, String protocol, final String host,
-                                             final int port, final String username, final String password,
-                                             final boolean isDebug, final String filterName, final int numElements,
-                                             final SslConfiguration sslConfiguration) {
-        if (Strings.isEmpty(protocol)) {
-            protocol = "smtp";
-        }
-
-        final String name = createManagerName(to, cc, bcc, from, replyTo, subject, protocol, host, port, username, isDebug, filterName);
-        final Serializer subjectSerializer = PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(subject).build();
-
-        return getManager(name, FACTORY, new FactoryData(to, cc, bcc, from, replyTo, subjectSerializer,
-            protocol, host, port, username, password, isDebug, numElements, sslConfiguration));
-
-    }
-
-    /**
-     * Creates a unique-per-configuration name for an smtp manager using the specified the parameters.<br>
-     * Using such a name allows us to maintain singletons per unique configurations.
-     *
-     * @return smtp manager name
-     */
-    static String createManagerName(
-            final String to,
-            final String cc,
-            final String bcc,
-            final String from,
-            final String replyTo,
-            final String subject,
-            final String protocol,
-            final String host,
-            final int port,
-            final String username,
-            final boolean isDebug,
-            final String filterName) {
-
-        final StringBuilder sb = new StringBuilder();
-
-        if (to != null) {
-            sb.append(to);
-        }
-        sb.append(':');
-        if (cc != null) {
-            sb.append(cc);
-        }
-        sb.append(':');
-        if (bcc != null) {
-            sb.append(bcc);
-        }
-        sb.append(':');
-        if (from != null) {
-            sb.append(from);
-        }
-        sb.append(':');
-        if (replyTo != null) {
-            sb.append(replyTo);
-        }
-        sb.append(':');
-        if (subject != null) {
-            sb.append(subject);
-        }
-        sb.append(':');
-        sb.append(protocol).append(':').append(host).append(':').append(port).append(':');
-        if (username != null) {
-            sb.append(username);
-        }
-        sb.append(isDebug ? ":debug:" : "::");
-        sb.append(filterName);
-
-        return "SMTP:" + sb.toString();
-    }
-
-    /**
-     * Send the contents of the cyclic buffer as an e-mail message.
-     * @param layout The layout for formatting the events.
-     * @param appendEvent The event that triggered the send.
-     */
+    @Override
     public void sendEvents(final Layout<?> layout, final LogEvent appendEvent) {
         if (message == null) {
             connect(appendEvent);
@@ -184,7 +104,7 @@ public class SmtpManager extends AbstractManager {
             final InternetHeaders headers = getHeaders(contentType, encoding);
             final MimeMultipart mp = getMimeMultipart(encodedBytes, headers);
 
-            final String subject = data.subject.toSerializable(appendEvent);
+            final String subject = data.getSubjectSerializer().toSerializable(appendEvent);
 
             sendMultipartMessage(message, mp, subject);
         } catch (final MessagingException | IOException | RuntimeException e) {
@@ -292,46 +212,6 @@ public class SmtpManager extends AbstractManager {
         }
     }
 
-    /**
-     * Factory data.
-     */
-    private static class FactoryData {
-        private final String to;
-        private final String cc;
-        private final String bcc;
-        private final String from;
-        private final String replyto;
-        private final Serializer subject;
-        private final String protocol;
-        private final String host;
-        private final int port;
-        private final String username;
-        private final String password;
-        private final boolean isDebug;
-        private final int numElements;
-        private final SslConfiguration sslConfiguration;
-
-        public FactoryData(final String to, final String cc, final String bcc, final String from, final String replyTo,
-                           final Serializer subjectSerializer, final String protocol, final String host, final int port,
-                           final String username, final String password, final boolean isDebug, final int numElements,
-                           final SslConfiguration sslConfiguration) {
-            this.to = to;
-            this.cc = cc;
-            this.bcc = bcc;
-            this.from = from;
-            this.replyto = replyTo;
-            this.subject = subjectSerializer;
-            this.protocol = protocol;
-            this.host = host;
-            this.port = port;
-            this.username = username;
-            this.password = password;
-            this.isDebug = isDebug;
-            this.numElements = numElements;
-            this.sslConfiguration = sslConfiguration;
-        }
-    }
-
     private synchronized void connect(final LogEvent appendEvent) {
         if (message != null) {
             return;
@@ -347,33 +227,35 @@ public class SmtpManager extends AbstractManager {
     /**
      * Factory to create the SMTP Manager.
      */
-    private static class SMTPManagerFactory implements ManagerFactory<SmtpManager, FactoryData> {
+    public static class SMTPManagerFactory implements MailManagerFactory {
 
         @Override
         public SmtpManager createManager(final String name, final FactoryData data) {
-            final String prefix = "mail." + data.protocol;
+            final String smtpProtocol = data.getSmtpProtocol();
+            final String prefix = "mail." + smtpProtocol;
 
             final Properties properties = PropertiesUtil.getSystemProperties();
-            properties.setProperty("mail.transport.protocol", data.protocol);
+            properties.setProperty("mail.transport.protocol", smtpProtocol);
             if (properties.getProperty("mail.host") == null) {
                 // Prevent an UnknownHostException in Java 7
                 properties.setProperty("mail.host", NetUtils.getLocalHostname());
             }
 
-            if (null != data.host) {
-                properties.setProperty(prefix + ".host", data.host);
+            final String smtpHost = data.getSmtpHost();
+            if (null != smtpHost) {
+                properties.setProperty(prefix + ".host", smtpHost);
             }
-            if (data.port > 0) {
-                properties.setProperty(prefix + ".port", String.valueOf(data.port));
+            if (data.getSmtpPort() > 0) {
+                properties.setProperty(prefix + ".port", String.valueOf(data.getSmtpPort()));
             }
 
-            final Authenticator authenticator = buildAuthenticator(data.username, data.password);
+            final Authenticator authenticator = buildAuthenticator(data.getSmtpUsername(), data.getSmtpPassword());
             if (null != authenticator) {
                 properties.setProperty(prefix + ".auth", "true");
             }
 
-            if (data.protocol.equals("smtps")) {
-                final SslConfiguration sslConfiguration = data.sslConfiguration;
+            if (smtpProtocol.equals("smtps")) {
+                final SslConfiguration sslConfiguration = data.getSslConfiguration();
                 if (sslConfiguration != null) {
                     final SSLSocketFactory sslSocketFactory = sslConfiguration.getSslSocketFactory();
                     properties.put(prefix + ".ssl.socketFactory", sslSocketFactory);
@@ -382,8 +264,8 @@ public class SmtpManager extends AbstractManager {
             }
 
             final Session session = Session.getInstance(properties, authenticator);
-            session.setProtocolForAddress("rfc822", data.protocol);
-            session.setDebug(data.isDebug);
+            session.setProtocolForAddress("rfc822", smtpProtocol);
+            session.setDebug(data.isSmtpDebug());
             return new SmtpManager(name, session, null, data);
         }
 
diff --git a/log4j-jakarta-smtp/src/main/resources/META-INF/services/org.apache.logging.log4j.core.net.MailManagerFactory b/log4j-jakarta-smtp/src/main/resources/META-INF/services/org.apache.logging.log4j.core.net.MailManagerFactory
new file mode 100644
index 0000000000..8b89a565e9
--- /dev/null
+++ b/log4j-jakarta-smtp/src/main/resources/META-INF/services/org.apache.logging.log4j.core.net.MailManagerFactory
@@ -0,0 +1 @@
+org.apache.logging.log4j.smtp.SmtpManager$SMTPManagerFactory
\ No newline at end of file
diff --git a/log4j-jakarta-smtp/src/test/java/org/apache/logging/log4j/smtp/SmtpAppenderAsyncTest.java b/log4j-jakarta-smtp/src/test/java/org/apache/logging/log4j/smtp/SmtpAppenderAsyncTest.java
new file mode 100644
index 0000000000..3a18b26615
--- /dev/null
+++ b/log4j-jakarta-smtp/src/test/java/org/apache/logging/log4j/smtp/SmtpAppenderAsyncTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.logging.log4j.smtp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Iterator;
+
+import org.apache.logging.dumbster.smtp.SimpleSmtpServer;
+import org.apache.logging.dumbster.smtp.SmtpMessage;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.apache.logging.log4j.test.AvailablePortFinder;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class SmtpAppenderAsyncTest {
+
+    private static int PORT;
+
+    private SimpleSmtpServer smtpServer;
+
+    @BeforeClass
+    public static void setupClass() {
+        PORT = AvailablePortFinder.getNextAvailable();
+        System.setProperty("smtp.port", String.valueOf(PORT));
+    }
+
+    @Before
+    public void setup() {
+        smtpServer = SimpleSmtpServer.start(PORT);
+    }
+
+    @Rule
+    public LoggerContextRule ctx = new LoggerContextRule("SmtpAppenderAsyncTest.xml");
+
+    @Test
+    public void testSync() {
+        testSmtpAppender(ctx.getLogger("sync"));
+    }
+
+    @Test
+    public void testAsync() {
+        testSmtpAppender(ctx.getLogger("async"));
+    }
+
+    private void testSmtpAppender(final Logger logger) {
+        ThreadContext.put("MDC1", "mdc1");
+        logger.error("the message");
+        ctx.getLoggerContext().stop();
+        smtpServer.stop();
+
+        assertEquals(1, smtpServer.getReceivedEmailSize());
+        final Iterator<SmtpMessage> messages = smtpServer.getReceivedEmail();
+        final SmtpMessage email = messages.next();
+
+        assertEquals("to@example.com", email.getHeaderValue("To"));
+        assertEquals("from@example.com", email.getHeaderValue("From"));
+        assertEquals("[mdc1]", email.getHeaderValue("Subject"));
+
+        final String body = email.getBody();
+        if (!body.contains("Body:[mdc1]")) {
+            fail(body);
+        }
+    }
+
+    @After
+    public void teardown() {
+        if (smtpServer != null) {
+            smtpServer.stop();
+        }
+    }
+
+    @AfterClass
+    public static void teardownClass() {
+        System.clearProperty("smtp.port");
+    }
+}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SmtpAppenderTest.java b/log4j-jakarta-smtp/src/test/java/org/apache/logging/log4j/smtp/SmtpAppenderTest.java
similarity index 91%
copy from log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SmtpAppenderTest.java
copy to log4j-jakarta-smtp/src/test/java/org/apache/logging/log4j/smtp/SmtpAppenderTest.java
index 2a4cf4cf7a..8360c18cc5 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SmtpAppenderTest.java
+++ b/log4j-jakarta-smtp/src/test/java/org/apache/logging/log4j/smtp/SmtpAppenderTest.java
@@ -14,13 +14,21 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core.appender;
+package org.apache.logging.log4j.smtp;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import java.util.Iterator;
-import javax.mail.Address;
-import javax.mail.Message;
-import javax.mail.MessagingException;
-import javax.mail.internet.InternetAddress;
+
+import jakarta.mail.Address;
+import jakarta.mail.Message;
+import jakarta.mail.MessagingException;
+import jakarta.mail.internet.InternetAddress;
 
 import org.apache.logging.dumbster.smtp.SimpleSmtpServer;
 import org.apache.logging.dumbster.smtp.SmtpMessage;
@@ -29,13 +37,11 @@ import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.categories.Appenders;
 import org.apache.logging.log4j.core.Logger;
 import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.net.MimeMessageBuilder;
+import org.apache.logging.log4j.core.appender.SmtpAppender;
 import org.apache.logging.log4j.test.AvailablePortFinder;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
-import static org.junit.Assert.*;
-
 @Category(Appenders.Smtp.class)
 public class SmtpAppenderTest {
 
@@ -125,6 +131,8 @@ public class SmtpAppenderTest {
                 .setSmtpPort(smtpPort)
                 .setBufferSize(3)
                 .build();
+        assertNotNull(appender);
+        assertTrue(appender.getManager() instanceof SmtpManager);
         appender.start();
 
         final LoggerContext context = LoggerContext.getContext();
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/net/SmtpManagerTest.java b/log4j-jakarta-smtp/src/test/java/org/apache/logging/log4j/smtp/SmtpManagerTest.java
similarity index 70%
copy from log4j-core/src/test/java/org/apache/logging/log4j/core/net/SmtpManagerTest.java
copy to log4j-jakarta-smtp/src/test/java/org/apache/logging/log4j/smtp/SmtpManagerTest.java
index 5759cf7bbb..1ddd7b01aa 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/net/SmtpManagerTest.java
+++ b/log4j-jakarta-smtp/src/test/java/org/apache/logging/log4j/smtp/SmtpManagerTest.java
@@ -15,38 +15,48 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.net;
+package org.apache.logging.log4j.smtp;
 
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.assertEquals;
 
 import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.SmtpAppender;
 import org.apache.logging.log4j.core.async.RingBufferLogEvent;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
 import org.apache.logging.log4j.core.impl.MementoMessage;
 import org.apache.logging.log4j.core.impl.MutableLogEvent;
+import org.apache.logging.log4j.core.net.MailManager;
 import org.apache.logging.log4j.core.util.ClockFactory;
 import org.apache.logging.log4j.core.util.DummyNanoClock;
 import org.apache.logging.log4j.message.ReusableMessage;
 import org.apache.logging.log4j.message.ReusableSimpleMessage;
-import org.junit.jupiter.api.Test;
+import org.junit.Test;
 
-/**
- * Unit tests for {@link SmtpManager}.
- */
 public class SmtpManagerTest {
 
-    @Test
-    void testCreateManagerName() {
-        String managerName = SmtpManager.createManagerName("to", "cc", null, "from", null, "LOG4J2-3107",
-                "proto", "smtp.log4j.com", 4711, "username", false, "filter");
-        assertEquals("SMTP:to:cc::from::LOG4J2-3107:proto:smtp.log4j.com:4711:username::filter", managerName);
-    }
-
     private void testAdd(LogEvent event) {
-        SmtpManager smtpManager = SmtpManager.getSmtpManager(null, "to", "cc", "bcc", "from", "replyTo", "subject", "protocol", "host", 0, "username", "password", false, "filterName", 10, null);
+        final SmtpAppender appender = SmtpAppender.newBuilder()
+                .setName("smtp")
+                .setTo("to")
+                .setCc("cc")
+                .setBcc("bcc")
+                .setFrom("from")
+                .setReplyTo("replyTo")
+                .setSubject("subject")
+                .setSmtpProtocol("smtp")
+                .setSmtpHost("host")
+                .setSmtpPort(0)
+                .setSmtpUsername("username")
+                .setSmtpPassword("password")
+                .setSmtpDebug(false)
+                .setFilter(null)
+                .setBufferSize(10)
+                .build();
+        final MailManager mailManager = appender.getManager();
+        assertThat("is instance of SmtpManager", mailManager instanceof SmtpManager);
+        final SmtpManager smtpManager = (SmtpManager) mailManager;
         smtpManager.removeAllBufferedEvents(); // in case this smtpManager is reused
         smtpManager.add(event);
 
@@ -57,21 +67,21 @@ public class SmtpManagerTest {
 
     // LOG4J2-3172: make sure existing protections are not violated
     @Test
-    void testAdd_WhereLog4jLogEventWithReusableMessage() {
+    public void testAdd_WhereLog4jLogEventWithReusableMessage() {
         LogEvent event = new Log4jLogEvent.Builder().setMessage(getReusableMessage("test message")).build();
         testAdd(event);
     }
 
     // LOG4J2-3172: make sure existing protections are not violated
     @Test
-    void testAdd_WhereMutableLogEvent() {
+    public void testAdd_WhereMutableLogEvent() {
         MutableLogEvent event = new MutableLogEvent(new StringBuilder("test message"), null);
         testAdd(event);
     }
 
     // LOG4J2-3172
     @Test
-    void testAdd_WhereRingBufferLogEvent() {
+    public void testAdd_WhereRingBufferLogEvent() {
         RingBufferLogEvent event = new RingBufferLogEvent();
         event.setValues(null, null, null, null, null, getReusableMessage("test message"), null, null, null, 0, null, 0, null, ClockFactory.getClock(), new DummyNanoClock());
         testAdd(event);
diff --git a/log4j-jakarta-smtp/src/test/resources/SmtpAppenderAsyncTest.xml b/log4j-jakarta-smtp/src/test/resources/SmtpAppenderAsyncTest.xml
new file mode 100644
index 0000000000..1939ddd2e9
--- /dev/null
+++ b/log4j-jakarta-smtp/src/test/resources/SmtpAppenderAsyncTest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements. See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache license, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License. You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the license for the specific language governing permissions and
+  ~ limitations under the license.
+  -->
+<Configuration name="SmtpAppenderAsyncTest" status="WARN">
+    <Appenders>
+        <SMTP name="mail-sync" to="to@example.com" from="from@example.com" smtpHost="localhost"
+              smtpPort="${sys:smtp.port}" ignoreExceptions="false" subject="[%X{MDC1}]">
+            <PatternLayout pattern="Body:[%X{MDC1}]" />
+        </SMTP>
+        <Async name="mail-async">
+            <AppenderRef ref="mail-sync"/>
+        </Async>
+        <Console name="Console" target="SYSTEM_OUT">
+            <PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable" />
+        </Console>
+    </Appenders>
+    <Loggers>
+        <Root level="FATAL">
+          <AppenderRef ref="Console"/>
+        </Root>
+        <Logger name="sync" level="INFO">
+            <AppenderRef ref="mail-sync"/>
+        </Logger>
+        <Logger name="async" level="INFO">
+            <AppenderRef ref="mail-async"/>
+        </Logger>
+    </Loggers>
+</Configuration>
diff --git a/pom.xml b/pom.xml
index f28e4717bb..6cc3ec117c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -303,6 +303,10 @@
     <junitPioneerVersion>1.6.2</junitPioneerVersion>
     <mockitoVersion>4.4.0</mockitoVersion>
     <xmlunitVersion>2.9.0</xmlunitVersion>
+    <javax.activation.version>1.2.0</javax.activation.version>
+    <javax.mail.version>1.6.2</javax.mail.version>
+    <jakarta.activation.version>2.0.1</jakarta.activation.version>
+    <jakarta.mail.version>2.0.1</jakarta.mail.version>
     <argLine>-Xms256m -Xmx1024m</argLine>
     <javaTargetVersion>1.8</javaTargetVersion>
     <module.name />
@@ -697,10 +701,43 @@
         <optional>true</optional>
       </dependency>
       <!-- Jackson 2 end -->
+      <dependency>
+        <groupId>javax.mail</groupId>
+        <artifactId>javax.mail-api</artifactId>
+        <version>${javax.mail.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>javax.activation</groupId>
+        <artifactId>javax.activation-api</artifactId>
+        <version>${javax.activation.version}</version>
+      </dependency>
       <dependency>
         <groupId>com.sun.mail</groupId>
         <artifactId>javax.mail</artifactId>
-        <version>1.6.2</version>
+        <version>${javax.mail.version}</version>
+        <scope>runtime</scope>
+      </dependency>
+      <dependency>
+        <groupId>jakarta.mail</groupId>
+        <artifactId>jakarta.mail-api</artifactId>
+        <version>${jakarta.mail.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>jakarta.activation</groupId>
+        <artifactId>jakarta.activation-api</artifactId>
+        <version>${jakarta.activation.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>com.sun.mail</groupId>
+        <artifactId>smtp</artifactId>
+        <version>${jakarta.mail.version}</version>
+        <scope>runtime</scope>
+      </dependency>
+      <dependency>
+        <groupId>com.sun.activation</groupId>
+        <artifactId>jakarta.activation</artifactId>
+        <version>${jakarta.activation.version}</version>
+        <scope>runtime</scope>
       </dependency>
       <dependency>
         <groupId>org.jboss.spec.javax.jms</groupId>
@@ -1651,6 +1688,7 @@
     <module>log4j-kubernetes</module>
     <module>log4j-spring-boot</module>
     <module>log4j-spring-cloud-config</module>
+    <module>log4j-jakarta-smtp</module>
   </modules>
   <profiles>
     <profile>