com.enterprisemath.utils.messaging.SmtpMessenger Maven / Gradle / Ivy
package com.enterprisemath.utils.messaging;
import java.net.ConnectException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.mail.Address;
import javax.mail.Authenticator;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.velocity.app.VelocityEngine;
import com.enterprisemath.utils.DomainUtils;
import com.enterprisemath.utils.ValidationUtils;
import java.io.StringWriter;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.util.ByteArrayDataSource;
import org.apache.commons.codec.binary.Base64;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.exception.VelocityException;
/**
* Messenger implementation which sends messages through SMTP channel.
*
* @author radek.hecl
*
*/
public class SmtpMessenger implements Messenger {
/**
* SMTP server.
*/
private String smtpHost;
/**
* SMTP server port.
*/
private Integer smtpPort;
/**
* Security which is used to transport email.
*/
private TransportSecurity transportSecurity;
/**
* User name for login to SMTP server.
*/
private String username;
/**
* Password for login to SMTP server.
*/
private String password;
/**
* Port for the socket.
*/
private Integer socketFactoryPort;
/**
* Provider for templates.
*/
private MessageTemplateProvider templateProvider;
/**
* Model provided by default to the
*/
private Map defaultModel;
/**
* Velocity engine for formatting messages.
*/
private VelocityEngine velocityEngine;
/**
* Creates new instance.
*/
private SmtpMessenger() {
}
/**
* Guards this object to be consistent. Throws exception if this is not the case.
*/
@PostConstruct
private void guardInvariants() {
ValidationUtils.guardNotEmpty(smtpHost, "smtpHost cannot be empty");
ValidationUtils.guardNotNull(smtpPort, "smtpPort cannot be null");
ValidationUtils.guardNotNull(transportSecurity, "transportSecurity cannot be null");
if (transportSecurity.equals(TransportSecurity.NONE)) {
ValidationUtils.guardEmpty(username, "username must be empty for non secured transport");
ValidationUtils.guardEmpty(password, "password must be empty for non secured transport");
}
else {
ValidationUtils.guardNotEmpty(username, "username cannot be empty for secured transport");
ValidationUtils.guardNotEmpty(password, "password cannot be empty for secured transport");
}
if (transportSecurity.equals(TransportSecurity.SSL)) {
ValidationUtils.guardNotNull(socketFactoryPort, "socketFactoryPort cannot be null for SSL transport");
}
else {
ValidationUtils.guardNull(socketFactoryPort, "socketFactoryPort must be null for non SSL transport");
}
ValidationUtils.guardNotNull(templateProvider, "templateProvider cannot be null");
ValidationUtils.guardNotEmptyNullMap(defaultModel, "defaultModel cannot have empty key or null value");
ValidationUtils.guardNotNull(velocityEngine, "velocityEngine cannot be null");
}
@Override
@SuppressWarnings("ThrowableResultIgnored")
public void send(String target, Message message) {
// format
Map model = new HashMap();
model.putAll(defaultModel);
model.putAll(DomainUtils.convertPropertyMapIntoVelocityModel(message.getParameters()));
String subject = applyTemplate(templateProvider.getTemplate(message.getType(), "subject"), model);
String text = applyTemplate(templateProvider.getTemplate(message.getType(), "message"), model);
// send
try {
boolean end = false;
int count = 0;
while (end == false) {
try {
sendOneTry(target, subject, text, message.getAttachments());
end = true;
} catch (MessagingException e) {
if (count >= 5 || !(e.getCause() instanceof ConnectException)) {
// kick out if retry was already enough or non retry able reason
throw e;
}
++count;
try {
Thread.sleep(30000);
} catch (InterruptedException e1) {
throw new RuntimeException(e1);
}
}
}
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
/**
*
* Sends single message. Makes only one try and throws exception if this fails.
*
* @param target target
* @param subject subject
* @param text message text
* @param attchements attachments
* @throws MessagingException in case of error during transport
*/
private void sendOneTry(String target, String subject, String text, List attachments) throws MessagingException {
// now send it
Session session = createSession();
// check whether there is a base64 encoded image and if yes, update the message and embed the image
Map imageCids2Attachemnts = new HashMap();
Pattern pattern = Pattern.compile(".*img.*src=\"(data:image/(png|gif);base64,([^\"]*)\").*", Pattern.DOTALL);
Matcher matcher = pattern.matcher(text);
int i = 0;
while (matcher.matches()) {
String cid = "embeddedImage" + i;
Attachment att = new Attachment.Builder().
setName(cid + "." + matcher.group(2)).
setMime("image/" + matcher.group(2)).
setBuf(Base64.decodeBase64(matcher.group(3))).
build();
imageCids2Attachemnts.put(cid, att);
text = text.substring(0, matcher.start(1)) + "cid:" + cid + text.substring(matcher.end(1) - 1);
matcher = pattern.matcher(text);
++i;
}
// now create the message
MimeMultipart multipart = new MimeMultipart();
MimeBodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setText(text, "utf-8", "html");
multipart.addBodyPart(messageBodyPart);
// include standard attachments
for (Attachment att : attachments) {
MimeBodyPart attachmentBodyPart = new MimeBodyPart();
DataSource source = new ByteArrayDataSource(att.getBuf(), att.getMime());
attachmentBodyPart.setDataHandler(new DataHandler(source));
attachmentBodyPart.setFileName(att.getName());
multipart.addBodyPart(attachmentBodyPart);
}
// include embedded images as attachments
for (Map.Entry emdImg : imageCids2Attachemnts.entrySet()) {
MimeBodyPart attachmentBodyPart = new MimeBodyPart();
DataSource source = new ByteArrayDataSource(emdImg.getValue().getBuf(), emdImg.getValue().getMime());
attachmentBodyPart.setDataHandler(new DataHandler(source));
attachmentBodyPart.setFileName(emdImg.getValue().getName());
attachmentBodyPart.setContentID("<" + emdImg.getKey() + ">");
attachmentBodyPart.setDisposition("inline");
multipart.addBodyPart(attachmentBodyPart);
}
MimeMessage mimeMsg = new MimeMessage(session);
mimeMsg.setSentDate(new Date());
mimeMsg.setFrom(new InternetAddress("[email protected]"));
mimeMsg.setReplyTo(new Address[]{new InternetAddress("[email protected]")});
mimeMsg.setRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(target));
mimeMsg.setSubject(subject);
mimeMsg.setContent(multipart);
mimeMsg.saveChanges();
Transport.send(mimeMsg);
}
/**
* Creates mail session.
*
* @return created session
*/
private Session createSession() {
Properties props = null;
Session session = null;
switch (transportSecurity) {
case NONE:
props = new Properties();
props.put("mail.smtp.host", smtpHost);
props.put("mail.smtp.port", Integer.toString(smtpPort));
props.put("mail.from", "[email protected]");
session = Session.getInstance(props, null);
break;
case SSL:
props = new Properties();
props.put("mail.smtp.host", smtpHost);
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", Integer.toString(smtpPort));
props.put("mail.smtp.socketFactory.port", Integer.toString(socketFactoryPort));
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
session = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
break;
case TLS:
props = new Properties();
props.put("mail.smtp.host", smtpHost);
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", Integer.toString(smtpPort));
props.put("mail.smtp.starttls.enable", "true");
session = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
break;
default:
throw new IllegalStateException("unsupported transport security - implement me if you need");
}
return session;
}
/**
* Applies template to the model and returns result
*
* @param templateLocation template location
* @param model model
* @return result after operation is done
*/
public String applyTemplate(String templateLocation, Map, ?> model) {
StringWriter result = new StringWriter();
try {
VelocityContext velocityContext = new VelocityContext(model);
velocityEngine.mergeTemplate(templateLocation, "utf-8", velocityContext, result);
} catch (VelocityException e) {
throw new RuntimeException(e);
}
return result.toString();
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
/**
* Creates new instance.
*
* @param smtpHost SMTP server
* @param smtpPort SMTP server port
* @param transportSecurity security which is used to transport email
* @param username user name for login to SMTP server
* @param password password for login to SMTP server
* @param socketFactoryPort port for the socket
* @param templateProvider provider for message templates
* @param defaultModel model accessible always in all templates
* @param velocityEngine velocity engine
* @return created instance
*/
public static SmtpMessenger create(String smtpHost, Integer smtpPort, TransportSecurity transportSecurity,
String username, String password, Integer socketFactoryPort, MessageTemplateProvider templateProvider,
Map defaultModel, VelocityEngine velocityEngine) {
SmtpMessenger res = new SmtpMessenger();
res.smtpHost = smtpHost;
res.smtpPort = smtpPort;
res.transportSecurity = transportSecurity;
res.username = username;
res.password = password;
res.socketFactoryPort = socketFactoryPort;
res.templateProvider = templateProvider;
res.defaultModel = DomainUtils.softCopyUnmodifiableMap(defaultModel);
res.velocityEngine = velocityEngine;
res.guardInvariants();
return res;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy