Skip to content

Commit

Permalink
#121: added support for a user provided custom mailer implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
bbottema committed Oct 27, 2019
1 parent e5feda3 commit beea36e
Show file tree
Hide file tree
Showing 12 changed files with 300 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.simplejavamail.api.mailer;

import org.simplejavamail.api.email.Email;
import org.simplejavamail.api.mailer.config.OperationalConfig;

import javax.annotation.Nonnull;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;

/**
* By default, Simple Java Mail handles the ultimate connection and sending of emails. However, it is possible to replace this last step
* by a custom implementation.
* <p>
* The benefit of this is that Simple Java Mail acts as an accelarator, providing thread pool, applying email content-validation, address validations,
* configuring a {@code Session} instance, producing a {@code MimeMessage}, all with full S/MIME, DKIM support and everything else.
*
* @see MailerGenericBuilder#withCustomMailer(CustomMailer)
*/
public interface CustomMailer {
void testConnection(@Nonnull OperationalConfig operationalConfig, @Nonnull Session session);
void sendMessage(@Nonnull OperationalConfig operationalConfig, @Nonnull Session session, final Email email, @Nonnull MimeMessage message);
}
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,11 @@ public interface MailerGenericBuilder<T extends MailerGenericBuilder<?>> {
*/
T withProperty(@Nonnull String propertyName, @Nullable Object propertyValue);

/**
* @see CustomMailer
*/
T withCustomMailer(@Nonnull CustomMailer customMailer);

/**
* Resets session time to its default ({@value DEFAULT_SESSION_TIMEOUT_MILLIS}).
*
Expand Down Expand Up @@ -705,4 +710,10 @@ public interface MailerGenericBuilder<T extends MailerGenericBuilder<?>> {
*/
@Nullable
Properties getProperties();

/**
* @see #withCustomMailer(CustomMailer)
*/
@Nullable
CustomMailer getCustomMailer();
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package org.simplejavamail.api.mailer.config;

import org.simplejavamail.api.mailer.CustomMailer;
import org.simplejavamail.api.mailer.Mailer;
import org.simplejavamail.api.mailer.MailerGenericBuilder;
import org.simplejavamail.api.mailer.MailerRegularBuilder;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
Expand Down Expand Up @@ -95,15 +97,21 @@ public interface OperationalConfig {
@Nonnull
Properties getProperties();

/**
* @see MailerGenericBuilder#withClusterKey(UUID)
*/
@Nonnull
UUID getClusterKey();

/**
* @see MailerGenericBuilder#withExecutorService(ExecutorService)
*/
@Nonnull
ExecutorService getExecutorService();

/**
* @see MailerGenericBuilder#withClusterKey(UUID)
* @see MailerGenericBuilder#withCustomMailer(CustomMailer)
*/
@Nonnull
UUID getClusterKey();
@Nullable
CustomMailer getCustomMailer();
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package org.simplejavamail.mailer.internal;

import org.hazlewood.connor.bottema.emailaddress.EmailAddressCriteria;
import org.simplejavamail.api.mailer.CustomMailer;
import org.simplejavamail.api.mailer.MailerGenericBuilder;
import org.simplejavamail.api.mailer.config.LoadBalancingStrategy;
import org.simplejavamail.api.mailer.config.OperationalConfig;
import org.simplejavamail.api.mailer.config.ProxyConfig;
import org.simplejavamail.config.ConfigLoader.Property;
import org.simplejavamail.internal.modules.ModuleLoader;
import org.simplejavamail.internal.util.SimpleOptional;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
Expand Down Expand Up @@ -169,6 +169,12 @@ abstract class MailerGenericBuilderImpl<T extends MailerGenericBuilderImpl<?>> i
* @see MailerGenericBuilder#withTransportModeLoggingOnly(Boolean)
*/
private boolean transportModeLoggingOnly;

/**
* @see MailerGenericBuilder#withCustomMailer(CustomMailer)
*/
@Nullable
private CustomMailer customMailer;

/**
* Sets defaults configured for proxy host, proxy port, proxy username, proxy password and proxy bridge port (used in authenticated proxy).
Expand Down Expand Up @@ -259,7 +265,8 @@ OperationalConfig buildOperationalConfig() {
getSslHostsToTrust(),
isTrustAllSSLHost(),
isVerifyingServerIdentity(),
getExecutorService());
getExecutorService(),
getCustomMailer());
}

/**
Expand Down Expand Up @@ -515,6 +522,15 @@ public T withProperty(@Nonnull final String propertyName, @Nullable final Object
return (T) this;
}

/**
* @see MailerGenericBuilder#withCustomMailer(CustomMailer)
*/
@Override
public T withCustomMailer(@Nonnull CustomMailer customMailer) {
this.customMailer = customMailer;
return (T) this;
}

/**
* @see MailerGenericBuilder#resetSessionTimeout()
*/
Expand Down Expand Up @@ -870,4 +886,13 @@ public boolean isTransportModeLoggingOnly() {
public Properties getProperties() {
return properties;
}

/**
* @see MailerGenericBuilder#getCustomMailer()
*/
@Override
@Nullable
public CustomMailer getCustomMailer() {
return customMailer;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public class MailerImpl implements Mailer {
this.session = session;
this.operationalConfig = operationalConfig;
final TransportStrategy effectiveTransportStrategy = ofNullable(transportStrategy).orMaybe(findStrategyForSession(session));
this.proxyServer = configureSessionWithProxy(proxyConfig, session, effectiveTransportStrategy);
this.proxyServer = configureSessionWithProxy(proxyConfig, operationalConfig, session, effectiveTransportStrategy);
initSession(session, operationalConfig, effectiveTransportStrategy);
initCluster(session, operationalConfig);
}
Expand Down Expand Up @@ -227,10 +227,14 @@ private void configureServerIdentityVerification(@Nonnull final Session session,
* @return null in case of no proxy or anonymous proxy, or a AnonymousSocks5Server proxy bridging server instance in case of authenticated proxy.
*/
@Nullable
private static AnonymousSocks5Server configureSessionWithProxy(@Nonnull final ProxyConfig proxyConfig,
private static AnonymousSocks5Server configureSessionWithProxy(
@Nonnull final ProxyConfig proxyConfig,
@Nonnull final OperationalConfig operationalConfig,
@Nonnull final Session session,
@Nullable final TransportStrategy transportStrategy) {
if (!proxyConfig.requiresProxy()) {
if (operationalConfig.getCustomMailer() != null) {
LOGGER.trace("CustomMailer provided by user, skipping proxy.");
} else if (!proxyConfig.requiresProxy()) {
LOGGER.trace("No proxy set, skipping proxy.");
} else {
if (transportStrategy == TransportStrategy.SMTPS) {
Expand Down Expand Up @@ -260,7 +264,7 @@ private static AnonymousSocks5Server configureSessionWithProxy(@Nonnull final Pr
}

private void initCluster(@Nonnull final Session session, @Nonnull final OperationalConfig operationalConfig) {
if (ModuleLoader.batchModuleAvailable()) {
if (operationalConfig.getCustomMailer() == null && ModuleLoader.batchModuleAvailable()) {
ModuleLoader.loadBatchModule().registerToCluster(operationalConfig, operationalConfig.getClusterKey(), session);
}
}
Expand All @@ -278,7 +282,7 @@ public void testConnection() {
*/
@Nullable
public synchronized AsyncResponse testConnection(boolean async) {
TestConnectionClosure testConnectionClosure = new TestConnectionClosure(operationalConfig.getClusterKey(), session, proxyServer, async, smtpConnectionCounter);
TestConnectionClosure testConnectionClosure = new TestConnectionClosure(operationalConfig, session, proxyServer, async, smtpConnectionCounter);

if (!async) {
testConnectionClosure.run();
Expand All @@ -304,7 +308,7 @@ public final void sendMail(final Email email) {
@Nullable
public final AsyncResponse sendMail(final Email email, @SuppressWarnings("SameParameterValue") final boolean async) {
if (validate(email)) {
SendMailClosure sendMailClosure = new SendMailClosure(operationalConfig.getClusterKey(), session, email, proxyServer, async, operationalConfig.isTransportModeLoggingOnly(),
SendMailClosure sendMailClosure = new SendMailClosure(operationalConfig, session, email, proxyServer, async, operationalConfig.isTransportModeLoggingOnly(),
smtpConnectionCounter);

if (!async) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package org.simplejavamail.mailer.internal;

import org.simplejavamail.api.mailer.CustomMailer;
import org.simplejavamail.api.mailer.config.LoadBalancingStrategy;
import org.simplejavamail.api.mailer.config.OperationalConfig;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
Expand Down Expand Up @@ -104,9 +106,15 @@ class OperationalConfigImpl implements OperationalConfig {
*/
@Nonnull
private final ExecutorService executorService;

/**
* @see org.simplejavamail.api.mailer.MailerGenericBuilder#withCustomMailer(CustomMailer)
*/
@Nullable
private final CustomMailer customMailer;

OperationalConfigImpl(final boolean async,
final Properties properties,
@Nullable final Properties properties,
final int sessionTimeout,
final int threadPoolSize,
final int threadPoolKeepAliveTime,
Expand All @@ -121,7 +129,8 @@ class OperationalConfigImpl implements OperationalConfig {
@Nonnull final List<String> sslHostsToTrust,
final boolean trustAllSSLHost,
final boolean verifyingServerIdentity,
@Nonnull final ExecutorService executorService) {
@Nonnull final ExecutorService executorService,
@Nullable final CustomMailer customMailer) {
this.async = async; // can be overridden when calling {@code mailer.send(async = true)}
this.properties = properties;
this.sessionTimeout = sessionTimeout;
Expand All @@ -139,6 +148,30 @@ class OperationalConfigImpl implements OperationalConfig {
this.trustAllSSLHost = trustAllSSLHost;
this.verifyingServerIdentity = verifyingServerIdentity;
this.executorService = executorService;
this.customMailer = customMailer;
}

@Override
public String toString() {
return "OperationalConfigImpl{" + "async=" + async
+ ", properties=" + properties
+ ", sessionTimeout=" + sessionTimeout
+ ", threadPoolSize=" + threadPoolSize
+ ", threadPoolKeepAliveTime=" + threadPoolKeepAliveTime
+ ", clusterKey=" + clusterKey
+ ", connectionPoolCoreSize=" + connectionPoolCoreSize
+ ", connectionPoolMaxSize=" + connectionPoolMaxSize
+ ", connectionPoolClaimTimeoutMillis=" + connectionPoolClaimTimeoutMillis
+ ", connectionPoolExpireAfterMillis=" + connectionPoolExpireAfterMillis
+ ", connectionPoolLoadBalancingStrategy=" + connectionPoolLoadBalancingStrategy
+ ", transportModeLoggingOnly=" + transportModeLoggingOnly
+ ", debugLogging=" + debugLogging
+ ", sslHostsToTrust=" + sslHostsToTrust
+ ", trustAllSSLHost=" + trustAllSSLHost
+ ", verifyingServerIdentity=" + verifyingServerIdentity
+ ", executorService=" + executorService
+ ", customMailer=" + customMailer
+ '}';
}

/**
Expand Down Expand Up @@ -264,15 +297,21 @@ public Properties getProperties() {
return properties;
}

@Nonnull
@Override
public UUID getClusterKey() {
return clusterKey;
}

@Nonnull
@Override
public ExecutorService getExecutorService() {
return executorService;
}

@Nonnull
@Nullable
@Override
public UUID getClusterKey() {
return clusterKey;
public CustomMailer getCustomMailer() {
return customMailer;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.simplejavamail.api.email.Email;
import org.simplejavamail.api.internal.authenticatedsockssupport.socks5server.AnonymousSocks5Server;
import org.simplejavamail.api.mailer.config.OperationalConfig;
import org.simplejavamail.converter.internal.mimemessage.MimeMessageProducerHelper;
import org.simplejavamail.mailer.internal.util.SessionLogger;
import org.simplejavamail.mailer.internal.util.TransportRunner;
Expand All @@ -12,7 +13,6 @@
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
import java.io.UnsupportedEncodingException;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;

import static org.simplejavamail.converter.EmailConverter.mimeMessageToEML;
Expand All @@ -25,17 +25,18 @@
*/
class SendMailClosure extends AbstractProxyServerSyncingClosure {

@Nonnull private final UUID clusterKey;
@Nonnull private final OperationalConfig operationalConfig;
@Nonnull private final Session session;
@Nonnull private final Email email;
private final boolean asyncForLoggingPurpose;
private final boolean transportModeLoggingOnly;

SendMailClosure(@Nonnull UUID clusterKey, @Nonnull Session session, @Nonnull Email email, @Nullable AnonymousSocks5Server proxyServer, boolean asyncForLoggingPurpose, boolean transportModeLoggingOnly, @Nonnull AtomicInteger smtpConnectionCounter) {
SendMailClosure(@Nonnull OperationalConfig operationalConfig, @Nonnull Session session, @Nonnull Email email, @Nullable AnonymousSocks5Server proxyServer, boolean asyncForLoggingPurpose,
boolean transportModeLoggingOnly, @Nonnull AtomicInteger smtpConnectionCounter) {
super(smtpConnectionCounter, proxyServer);
this.operationalConfig = operationalConfig;
this.session = session;
this.email = email;
this.clusterKey = clusterKey;
this.asyncForLoggingPurpose = asyncForLoggingPurpose;
this.transportModeLoggingOnly = transportModeLoggingOnly;
}
Expand All @@ -56,8 +57,10 @@ public void executeClosure() {

if (transportModeLoggingOnly) {
LOGGER.info("TRANSPORT_MODE_LOGGING_ONLY: skipping actual sending...");
} else if (operationalConfig.getCustomMailer() != null) {
operationalConfig.getCustomMailer().sendMessage(operationalConfig, session, email, message);
} else {
TransportRunner.sendMessage(clusterKey, session, message, message.getAllRecipients());
TransportRunner.sendMessage(operationalConfig.getClusterKey(), session, message, message.getAllRecipients());
}
} catch (final UnsupportedEncodingException e) {
LOGGER.error("Failed to send email:\n{}", email);
Expand Down
Loading

0 comments on commit beea36e

Please sign in to comment.