public class SmtpAuthEmailSender extends java.lang.Object implements EmailSender
Loggers. See LogManager on how
to configure these kinds of loggers, in case you need fine-grained control over logging. In general the log output
of the Java Mail API is helpful for development and debugging. For such purposes debug-level log output can be
obtained by setting the mail.debug property to true. See the
Java Mail API FAQ for details on how to
debug the Java Mail API and other related technologies like SSL that are employed by the Java Mail API.
The JavaDoc of the
javax.mail,
javax.mail.internet,
com.sun.mail.smtp,
and other packages provide details on log levels.| Constructor and Description |
|---|
SmtpAuthEmailSender(SmtpConfiguration smtpConfiguration,
java.util.concurrent.Executor executor,
int timeoutMs)
Create a new instance using the given executor and configuration.
|
| Modifier and Type | Method and Description |
|---|---|
protected javax.mail.internet.MimeMessage |
createAndFillMessage(MessageEditor messageEditor)
Creates a new
MimeMessage and passes it to the given MessageEditor that fills the message. |
protected javax.mail.Session |
createSession(java.util.Properties properties,
SmtpConfiguration smtpConfiguration)
Creates a new
Session with an Authenticator that will be used to log into the SMTP server. |
protected void |
properties(java.util.Properties properties)
This method does nothing by default but may be overridden to customize the configuration of the Java Mail API
used by this e-mail sender.
|
java.util.concurrent.CompletionStage<java.lang.String> |
send(MessageEditor messageEditor)
Create a completion stage that asynchronously sends an e-mail using the configuration of this e-mail
service.
|
protected void |
sendMessage(javax.mail.internet.MimeMessage message)
Sends the given message to the configured SMTP server.
|
public SmtpAuthEmailSender(@Nonnull
SmtpConfiguration smtpConfiguration,
@Nonnull
java.util.concurrent.Executor executor,
int timeoutMs)
final String yourHost = ...;
final int yourPort = ...;
// You may need to choose another transport security available for your server.
final SmptConfiguration.TransportSecurity security = SmtpConfiguration.TransportSecurity.STARTTLS;
final String username = ...;
final String password = ...;
final SmtpConfiguration smtpConfiguration = new SmtpConfiguration(yourHost, yourPort, security, username, password);
final int threeSeconds = 3*1000;
final SmtpAuthEmailSender sender = new SmtpAuthEmailSender(smtpConfiguration, ForkJoinPool.commonPool(), threeSeconds);
Instances of SmtpAuthEmailSender might be used as exemplified in EmailSender.send(MessageEditor).
Executor passed to the constructor enables the sender to send e-mails asynchronously. You may use
ForkJoinPool.commonPool(), which uses a pool of N threads, where N is equal to the number
of available processors. You may also create a custom ForkJoinPool instance, or another kind of
Executor. Note that blocked I/O while sending e-mails will block a pool thread. A fork join pool
will also queue tasks while all threads are busy or blocked and offers methods like
ForkJoinPool.getQueuedTaskCount() that may be used to monitor the pool at run-time.
The constructor also requires a timeout to be specified in milliseconds. If this timeout elapses while the e-mail
sender waits to be connected to the SMTP server, waits to read data from the SMTP server, or waits to write data
to the SMTP server, an EmailDeliveryException will be raised and sending of the e-mail that caused the
issue will be aborted. If this happens before the e-mail has been received by the SMTP server, the e-mail will
not be sent, as users of this service will not typically implement error-handling in such cases. In general,
shorter timeouts may lead to more messages being lost in the abscence of further error handling, longer timeouts
may lead to more tasks being queued if a ForkJoinPool is used as Executor; run-time monitoring of
the ForkJoinPool might help choose a suitable timeout value.
send(MessageEditor)
method fills messages before sending is attempted within the Executor, it is theoretically possible that
a large queue of messages waiting to be send consumes all available memory. Users of this API should therefore
ensure that messages do not exceed a certain size.
Executor passed to this constructor should be
shut down, too, in a way that ensures that all messages submitted to send(MessageEditor) have been sent
via SMTP - or a timeout expires in the case too many messages are waiting. If a ForkJoinPool is used as
Executor, ForkJoinPool.awaitQuiescence(long, TimeUnit) may be used for this purpose.smtpConfiguration - how to connect to the SMTP serverexecutor - the executor to use, e.g. ForkJoinPool.commonPool() may be used, but see abovetimeoutMs - the timeout for creating, reading from and writing to SMTP connections in
milliseconds. To try out SmtpAuthEmailSender for a low to moderate traffic site
with an SMTP server on the local network, the choice of the timeout might not be too
relevant and e.g. 3000ms might fit. But see above on what to consider when choosing a
timeout for use in a production environment.protected javax.mail.Session createSession(@Nonnull
java.util.Properties properties,
@Nonnull
SmtpConfiguration smtpConfiguration)
Session with an Authenticator that will be used to log into the SMTP server.
This method may be overridden to customize session creation. It is invoked by the
SmtpAuthEmailSender(SmtpConfiguration, Executor, int) constructor.
properties - the configuration to be applied in the sessionsmtpConfiguration - how to connect to the SMTP server, incl. the username and password to authenticate withprotected void properties(@Nonnull
java.util.Properties properties)
SmtpAuthEmailSender(SmtpConfiguration, Executor, int).
Consult the source code of this class before you override this method. The code will help you understand how
SmtpAuthEmailSender(SmtpConfiguration, Executor, int)
creates the properties that are passed to this method.
This method may be overridden like in the following example that disables the check of the server identity if
SmtpConfiguration.TransportSecurity.SSL_TLS or SmtpConfiguration.TransportSecurity.STARTTLS are
used for this SmtpAuthEmailSender. Because the example relaxes security constraints it might only be
applied in test setups. Note that properties are always set as strings.
protected void properties(final Properties properties) {
properties.setProperty("mail.smtp.ssl.checkserveridentity", "" + false);
}
The Java Mail API offers many configuration properties. The following packages define relevant properties: javax.mail, javax.mail.internet, and com.sun.mail.smtp.
properties - the properties for the Java Mail API created from the arguments passed to
SmtpAuthEmailSender(SmtpConfiguration, Executor, int)@Nonnull
public java.util.concurrent.CompletionStage<java.lang.String> send(@Nonnull
MessageEditor messageEditor)
EmailSenderMessageEditor passed to this method is invoked with an empty
MimeMessage that the EmailSender created. The MessageEditor shall prepare the message
so it can be sent. Sending will happen asynchronously, though.
MimeMessage. The following code shows a simple example.
CompletionStage<String> completionStage = emailSender.send(msg -> {
msg.setFrom("foo@domain.com");
msg.setSubject("Subject, "UTF-8");
msg.setText("Text", "UTF-8");
msg.setSentDate(new Date());
msg.setRecipients(Message.RecipientType.TO, "bar@domain.com");
});
The CompletionStage returned by this method may be combined with further completion stages. To directly
send the email and get its message ID, obtain a completable future as in the following example.
String messageID = completionStage.toCompletableFuture().join();
EmailDeliveryException that are contained
in the CompletionStage returned by this method. (Note that it is possible that other
RuntimeExceptions or Errors besides EmailDeliveryException may be contained in the
CompletionStage.) If CompletableFuture.join() is invoked like in
above snippet, any EmailDeliveryException raised while sending the email is wrapped in a
CompletionException thrown by CompletableFuture.join().
Exceptions that occur while creating an email are instances of EmailCreationException that are thrown
by EmailSender.send(MessageEditor).
If desired, both kinds of exceptions can be handled like in the following
snippet, without requiring the use CompletableFuture.join().
try {
emailSender.send(...)
.exceptionally(throwable -> {
if (throwable instanceof EmailDeliveryException) {
// handle the EmailDeliveryException
}
...
});
} catch (EmailCreationException e) {
// handle the EmailCreationException
}
EmailSender
instance. The timeouts avoid denial of service by too many connections waiting for stalled I/O.send in interface EmailSendermessageEditor - the email sender passes an empty message to the message editor that the editor shall fill.
Note that MimeMessage instances are not immutable. Messages passed to the editor
must only be used by that editor instance and must not be passed elsewhere.EmailDeliveryException if the email could not be sent successfully. (Note that it is possible that other
RuntimeExceptions or Errors besides EmailDeliveryException may be contained in the
CompletionStage.)protected javax.mail.internet.MimeMessage createAndFillMessage(@Nonnull
MessageEditor messageEditor)
MimeMessage and passes it to the given MessageEditor that fills the message.
This method may be overridden to customize message creation; it is invoked by send(MessageEditor).
messageEditor - the editor that will be used to fill the empty message created by this methodEmailCreationException - if there was an error while creating or filling the messageprotected void sendMessage(@Nonnull
javax.mail.internet.MimeMessage message)
throws javax.mail.MessagingException
This method may be overridden to customize message sending; it is invoked by send(MessageEditor).
The implementation of this method uses Transport.send(Message), which uses the Session
configuration from the given message and utilizes one SMTP connection per message. This approach avoids tracking
connection state.
message - the edited message that is ready for being sentjavax.mail.MessagingException - may be raised while sending the message. This method does not handle exceptions.
Exceptions are handled by the invoking sendMessage(MimeMessage) method.