[Major] Refactored MailHandler to send attachments, removed whitelisting
This commit is contained in:
parent
cea5ed9e25
commit
2b4707bf6a
|
@ -27,7 +27,21 @@ public abstract class MailHandler extends StrolchComponent {
|
|||
public abstract void sendMail(String subject, String text, String recipients);
|
||||
|
||||
/**
|
||||
* Sends an e-mail to given recipient asynchronously
|
||||
* Sends an e-mail with an attachment to the given recipients
|
||||
*
|
||||
* @param subject the subject of the e-mail
|
||||
* @param text the test of the e-mail
|
||||
* @param recipients the comma separated list of addresses to whom to send the e-mail see
|
||||
* {@link InternetAddress#parse(String)}
|
||||
* @param attachment the attachment as a string
|
||||
* @param fileName the file name of the attachment
|
||||
* @param type the mime type of the attachment
|
||||
*/
|
||||
public abstract void sendMail(String subject, String text, String recipients, String attachment, String fileName,
|
||||
String type);
|
||||
|
||||
/**
|
||||
* Sends an e-mail to the given recipients asynchronously
|
||||
*
|
||||
* @param subject the subject of the e-mail
|
||||
* @param text the test of the e-mail
|
||||
|
@ -35,4 +49,18 @@ public abstract class MailHandler extends StrolchComponent {
|
|||
* {@link InternetAddress#parse(String)}
|
||||
*/
|
||||
public abstract void sendMailAsync(String subject, String text, String recipients);
|
||||
|
||||
/**
|
||||
* Sends an e-mail with an attachment to the given recipients asynchronously
|
||||
*
|
||||
* @param subject the subject of the e-mail
|
||||
* @param text the test of the e-mail
|
||||
* @param recipients the comma separated list of addresses to whom to send the e-mail see
|
||||
* {@link InternetAddress#parse(String)}
|
||||
* @param attachment the attachment as a string
|
||||
* @param fileName the file name of the attachment
|
||||
* @param type the mime type of the attachment
|
||||
*/
|
||||
public abstract void sendMailAsync(String subject, String text, String recipients, String attachment,
|
||||
String fileName, String type);
|
||||
}
|
||||
|
|
|
@ -45,11 +45,23 @@ public class SmtpMailHandler extends MailHandler {
|
|||
SmtpMailer.getInstance().sendMail(subject, text, recipients);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMail(String subject, String text, String recipients, String attachment, String fileName,
|
||||
String type) {
|
||||
SmtpMailer.getInstance().sendMail(subject, text, recipients, attachment, fileName, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMailAsync(String subject, String text, String recipients) {
|
||||
getExecutorService("Mail").submit(() -> doSendMail(subject, text, recipients));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMailAsync(String subject, String text, String recipients, String attachment, String fileName,
|
||||
String type) {
|
||||
getExecutorService("Mail").submit(() -> doSendMail(subject, text, recipients, attachment, fileName, type));
|
||||
}
|
||||
|
||||
private void doSendMail(String subject, String text, String recipients) {
|
||||
try {
|
||||
SmtpMailer.getInstance().sendMail(subject, text, recipients);
|
||||
|
@ -65,4 +77,23 @@ public class SmtpMailHandler extends MailHandler {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doSendMail(String subject, String text, String recipients, String attachment, String fileName,
|
||||
String type) {
|
||||
try {
|
||||
SmtpMailer.getInstance().sendMail(subject, text, recipients, attachment, fileName, type);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to send mail \"" + subject + "\" to " + recipients + " with attachment " + fileName,
|
||||
e);
|
||||
|
||||
if (hasComponent(OperationsLog.class)) {
|
||||
LogMessage message = new LogMessage(this.realm, SYSTEM_USER_AGENT, getLocator(), LogSeverity.Exception,
|
||||
LogMessageState.Information, ResourceBundle.getBundle("strolch-service"),
|
||||
"mail.failedToSendWithAttachment").withException(e).value("reason", e)
|
||||
.value("fileName", fileName).value("host", this.host).value("subject", subject)
|
||||
.value("recipients", recipients);
|
||||
getComponent(OperationsLog.class).addMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
mail.failedToSend=Failed to send email to host {host} with subject {subject} to {recipients} due to {reason}
|
||||
mail.failedToSendWithAttachment=Failed to send email to host {host} with subject {subject} with attachment {fileName} to {recipients} due to {reason}
|
||||
execution.handler.invalidState=ExecutionHandler has state {state}, can not start new jobs!
|
||||
execution.handler.failed.execution=Failed to set to execution due to {reason}
|
||||
execution.handler.failed.executed=Failed to set to executed due to {reason}
|
||||
|
|
|
@ -1,27 +1,25 @@
|
|||
package li.strolch.utils;
|
||||
|
||||
import jakarta.mail.*;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.mail.internet.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Properties;
|
||||
|
||||
import static java.text.MessageFormat.format;
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
/**
|
||||
* A simple helper class to send e-mails. Uses javax.mail and is built as a singleton, so configuration has to be done
|
||||
* A simple helper class to send e-mails. Uses jakarta.mail and is built as a singleton, so configuration has to be done
|
||||
* only once.
|
||||
* <p>
|
||||
* The {@link Properties} required are as follows:
|
||||
* <ul>
|
||||
* <li><code>fromAddr</code> and <code>fromName</code> - defines the address from which the e-mail comes from</li>
|
||||
* <li><code>overrideRecipients</code> - if defined, overrides any recipients - useful for testng purposes</li>
|
||||
* <li><code>recipientWhitelist</code> - if defined allows those e-mail addresses to not be overridden</li>
|
||||
* <li>username - the username to authenticate at the SMTP Server</li>
|
||||
* <li>password - the password to authenticate at the SMTP Server</li>
|
||||
* <li>auth - boolean to define if auth is to be done</li>
|
||||
|
@ -66,7 +64,6 @@ public class SmtpMailer {
|
|||
|
||||
private final InternetAddress from;
|
||||
private final InternetAddress[] overrideRecipients;
|
||||
private final Set<InternetAddress> recipientWhitelist;
|
||||
private final String username;
|
||||
private final String password;
|
||||
private final Properties props;
|
||||
|
@ -92,17 +89,6 @@ public class SmtpMailer {
|
|||
this.overrideRecipients = InternetAddress.parse(addr);
|
||||
}
|
||||
|
||||
if (!properties.containsKey("recipientWhitelist")) {
|
||||
this.recipientWhitelist = null;
|
||||
} else {
|
||||
String whiteListProp = properties.getProperty("recipientWhitelist");
|
||||
this.recipientWhitelist = Arrays.stream(whiteListProp.split(",")) //
|
||||
.map(String::trim) //
|
||||
.map(s -> Arrays.asList(parseAddress(s))) //
|
||||
.flatMap(List::stream) //
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
this.username = properties.getProperty("username", null);
|
||||
this.password = properties.getProperty("password", null);
|
||||
|
||||
|
@ -113,74 +99,21 @@ public class SmtpMailer {
|
|||
this.props.put("mail.smtp.port", properties.getProperty("port", null));
|
||||
}
|
||||
|
||||
private InternetAddress[] parseAddress(String s) {
|
||||
try {
|
||||
return InternetAddress.parse(s);
|
||||
} catch (AddressException e) {
|
||||
throw new IllegalArgumentException("Failed to parse address: " + s, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an e-mail to given recipient (unless override address defined).
|
||||
* Sends an e-mail to the given recipients (unless override address defined).
|
||||
*
|
||||
* @param subject the subject of the e-mail
|
||||
* @param text the test of the e-mail
|
||||
* @param recipients the addresses to whom to send the e-mail. See {@link InternetAddress#parse(String)}
|
||||
*/
|
||||
public void sendMail(String subject, String text, String recipients) {
|
||||
|
||||
try {
|
||||
InternetAddress[] recipientAddresses = evaluateRecipients(subject, recipients);
|
||||
Message message = buildMessage(subject, recipientAddresses);
|
||||
message.setText(text);
|
||||
|
||||
if (this.overrideRecipients == null) {
|
||||
|
||||
// no override, just send
|
||||
send(subject, text, parseAddress(recipients));
|
||||
logger.info(MessageFormat.format("Sent E-mail to {0}: {1}", recipients, subject));
|
||||
|
||||
} else if (this.recipientWhitelist == null) {
|
||||
|
||||
// override, with no white list, so send to override
|
||||
text = "Override recipient. Original recipients: " + recipients + ".\n\n" + text;
|
||||
send(subject, text, this.overrideRecipients);
|
||||
logger.info(MessageFormat.format("Sent E-mail to override recipient {0}: {1}",
|
||||
Arrays.stream(this.overrideRecipients) //
|
||||
.map(Object::toString) //
|
||||
.collect(Collectors.joining(",")), subject));
|
||||
|
||||
} else {
|
||||
|
||||
// override with whitelist, so we have to perhaps send to override and white list
|
||||
|
||||
Set<InternetAddress> allowList = new HashSet<>();
|
||||
boolean needsOverrides = false;
|
||||
|
||||
for (InternetAddress recipient : parseAddress(recipients)) {
|
||||
if (this.recipientWhitelist.contains(recipient))
|
||||
allowList.add(recipient);
|
||||
else
|
||||
needsOverrides = true;
|
||||
}
|
||||
|
||||
if (!allowList.isEmpty()) {
|
||||
InternetAddress[] arr = new InternetAddress[allowList.size()];
|
||||
allowList.toArray(arr);
|
||||
send(subject, text, arr);
|
||||
logger.info(
|
||||
MessageFormat.format("Sent E-mail to white list recipient {0}: {1}", recipients, subject));
|
||||
}
|
||||
|
||||
if (needsOverrides) {
|
||||
|
||||
// override, with no white list, so send to override
|
||||
text = "Override recipient. Original recipients: " + recipients + ".\n\n" + text;
|
||||
send(subject, text, this.overrideRecipients);
|
||||
logger.info(MessageFormat.format("Sent E-mail to override recipient {0}: {1}",
|
||||
Arrays.stream(this.overrideRecipients) //
|
||||
.map(Object::toString) //
|
||||
.collect(Collectors.joining(",")), subject));
|
||||
}
|
||||
}
|
||||
Transport.send(message);
|
||||
logger.info(format("Sent E-mail with subject {0} to {1}", subject, addressesToString(recipientAddresses)));
|
||||
|
||||
} catch (MessagingException e) {
|
||||
logger.error("Failed to send the following e-mail:\nSubject: " + subject + "\nRecipients: " + recipients +
|
||||
|
@ -189,20 +122,83 @@ public class SmtpMailer {
|
|||
}
|
||||
}
|
||||
|
||||
private void send(String subject, String text, InternetAddress[] recipients) throws MessagingException {
|
||||
/**
|
||||
* Sends an e-mail with an attachment to the given recipients (unless override address defined).
|
||||
*
|
||||
* @param subject the subject of the e-mail
|
||||
* @param text the test of the e-mail
|
||||
* @param recipients the addresses to whom to send the e-mail. See {@link InternetAddress#parse(String)}
|
||||
*/
|
||||
public void sendMail(String subject, String text, String recipients, String attachment, String fileName,
|
||||
String type) {
|
||||
try {
|
||||
InternetAddress[] recipientAddresses = evaluateRecipients(subject, recipients);
|
||||
Message message = buildMessage(subject, recipientAddresses);
|
||||
|
||||
Session session = Session.getInstance(this.props, new jakarta.mail.Authenticator() {
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(SmtpMailer.this.username, SmtpMailer.this.password);
|
||||
}
|
||||
});
|
||||
MimeBodyPart messageBodyPart = new MimeBodyPart();
|
||||
messageBodyPart.setContent(text, "text/html; charset=utf-8");
|
||||
|
||||
MimeBodyPart attachmentPart = new MimeBodyPart();
|
||||
attachmentPart.setContent(attachment, type);
|
||||
attachmentPart.setFileName(fileName);
|
||||
|
||||
Multipart multipart = new MimeMultipart();
|
||||
multipart.addBodyPart(messageBodyPart);
|
||||
multipart.addBodyPart(attachmentPart);
|
||||
|
||||
message.setContent(multipart);
|
||||
|
||||
Transport.send(message);
|
||||
} catch (MessagingException e) {
|
||||
logger.error("Failed to send the following e-mail:\nSubject: " + subject + "\nAttachment: " + fileName +
|
||||
"\nRecipients: " + recipients + "\n\nBody:\n" + text);
|
||||
throw new RuntimeException("Failed to send e-mail due to " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private Message buildMessage(String subject, InternetAddress[] recipients) throws MessagingException {
|
||||
Session session = Session.getInstance(this.props, getAuthenticator());
|
||||
|
||||
Message message = new MimeMessage(session);
|
||||
message.setFrom(this.from);
|
||||
message.addRecipients(Message.RecipientType.TO, recipients);
|
||||
message.setSubject(subject);
|
||||
message.setText(text);
|
||||
return message;
|
||||
}
|
||||
|
||||
Transport.send(message);
|
||||
private Authenticator getAuthenticator() {
|
||||
return new Authenticator() {
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(SmtpMailer.this.username, SmtpMailer.this.password);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private InternetAddress[] parseAddress(String s) {
|
||||
try {
|
||||
return InternetAddress.parse(s);
|
||||
} catch (AddressException e) {
|
||||
throw new IllegalArgumentException("Failed to parse address: " + s, e);
|
||||
}
|
||||
}
|
||||
|
||||
private InternetAddress[] evaluateRecipients(String subject, String recipients) {
|
||||
InternetAddress[] recipientAddresses;
|
||||
if (this.overrideRecipients == null) {
|
||||
recipientAddresses = parseAddress(recipients);
|
||||
logger.info(
|
||||
format("Sending e-mail with subject {0} to {1}", subject, addressesToString(recipientAddresses)));
|
||||
} else {
|
||||
recipientAddresses = this.overrideRecipients;
|
||||
logger.info(format("Sending e-mail with subject {0} to override recipient {1}", subject,
|
||||
addressesToString(recipientAddresses)));
|
||||
}
|
||||
return recipientAddresses;
|
||||
}
|
||||
|
||||
private String addressesToString(InternetAddress[] recipientAddresses) {
|
||||
return stream(recipientAddresses) //
|
||||
.map(Object::toString) //
|
||||
.collect(joining(","));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue