package cn.bran.play;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.mail.Email;
import org.apache.commons.mail.EmailException;
import play.Logger;
import play.Play;
import cn.bran.play.exceptions.MailException;
/**
* Mail utils
*/
public class Mail {
public static Session session;
public static boolean asynchronousSend = true;
/**
* Send an email
*/
public static Future<Boolean> send(Email email) {
try {
email = buildMessage(email);
String string = getConfig("mail.smtp.host"); // has to use this since mail.smtp is a tree in play2
if (string != null)
if (string.equals("mock") && Play.isDev()) {
Mock.send(email);
return new Future<Boolean>() {
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
public boolean isCancelled() {
return false;
}
public boolean isDone() {
return true;
}
public Boolean get() throws InterruptedException,
ExecutionException {
return true;
}
public Boolean get(long timeout, TimeUnit unit)
throws InterruptedException,
ExecutionException, TimeoutException {
return true;
}
};
}
email.setMailSession(getSession());
return sendMessage(email);
} catch (EmailException ex) {
throw new MailException("Cannot send email", ex);
}
}
/**
*
*/
public static Email buildMessage(Email email) throws EmailException {
String from = Play.application().configuration()
.getString("mail.smtp.from");
if (email.getFromAddress() == null && !StringUtils.isEmpty(from)) {
email.setFrom(from);
} else if (email.getFromAddress() == null) {
throw new MailException("Please define a 'from' email address",
new NullPointerException());
}
if ((email.getToAddresses() == null || email.getToAddresses().size() == 0)
&& (email.getCcAddresses() == null || email.getCcAddresses()
.size() == 0)
&& (email.getBccAddresses() == null || email.getBccAddresses()
.size() == 0)) {
throw new MailException("Please define a recipient email address",
new NullPointerException());
}
if (email.getSubject() == null) {
throw new MailException("Please define a subject",
new NullPointerException());
}
if (email.getReplyToAddresses() == null
|| email.getReplyToAddresses().size() == 0) {
email.addReplyTo(email.getFromAddress().getAddress());
}
return email;
}
public static Session getSession() {
if (session == null) {
Properties props = new Properties();
// Put a bogus value even if we are on dev mode, otherwise JavaMail
// will complain
props.put("mail.smtp.host", getConfigOr(
"mail.smtp.host", "localhost"));
String channelEncryption;
if (getConfigOr("mail.smtp.protocol", "smtp").equals("smtps")) {
// Backward compatibility before stable5
channelEncryption = "starttls";
} else {
channelEncryption = getConfigOr("mail.smtp.channel", "clear");
}
if (channelEncryption.equals("clear")) {
props.put("mail.smtp.port", "25");
} else if (channelEncryption.equals("ssl")) {
// port 465 + setup yes ssl socket factory (won't verify that
// the server certificate is signed with a root ca.)
props.put("mail.smtp.port", "465");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class",
"play.utils.YesSSLSocketFactory");
props.put("mail.smtp.socketFactory.fallback", "false");
} else if (channelEncryption.equals("starttls")) {
// port 25 + enable starttls + ssl socket factory
props.put("mail.smtp.port", "25");
props.put("mail.smtp.starttls.enable", "true");
// can't install our socket factory. will work only with server
// that has a signed certificate
// story to be continued in javamail 1.4.2 :
// https://glassfish.dev.java.net/issues/show_bug.cgi?id=5189
}
if (getConfig("mail.smtp.localhost") != null) {
props.put("mail.smtp.localhost",
getConfig("mail.smtp.localhost")); // override
// defaults
}
String sockfac = getConfig("mail.smtp.socketFactory.class");
if (sockfac != null) {
props.put("mail.smtp.socketFactory.class", sockfac);
}
String port = getConfig("mail.smtp.port");
if (port != null) {
props.put("mail.smtp.port", port);
}
String user = getConfigOr("mail.smtp.user", null);
String password = getConfigOr("mail.smtp.pass", null);
if (password == null) {
// Fallback to old convention
password = getConfigOr("mail.smtp.password", null);
}
String authenticator = getConfig("mail.smtp.authenticator");
session = null;
if (authenticator != null) {
props.put("mail.smtp.auth", "true");
try {
session = Session.getInstance(
props,
(Authenticator) Play.application().classloader().loadClass(authenticator).newInstance());
} catch (Exception e) {
Logger.error(
"Cannot instanciate custom SMTP authenticator: " + authenticator,
e);
}
}
if (session == null) {
if (user != null && password != null) {
props.put("mail.smtp.auth", "true");
session = Session.getInstance(props, new SMTPAuthenticator(
user, password));
} else {
props.remove("mail.smtp.auth");
session = Session.getInstance(props);
}
}
if (Boolean.parseBoolean(getConfigOr("mail.debug", "false"))) {
session.setDebug(true);
}
}
return session;
}
/**
* @author Bing Ran (bing.ran@hotmail.com)
* @param string
* @return
*/
private static String getConfig(String string) {
return getConfigOr(string, null);
}
private static String getConfigOr(String key, String sub) {
String p = Play.application().configuration().getString(
key);
if (p == null)
p = sub;
return p;
}
/**
* Send a JavaMail message
*
* @param msg
* An Email message
*/
public static Future<Boolean> sendMessage(final Email msg) {
if (asynchronousSend) {
return executor.submit(new Callable<Boolean>() {
public Boolean call() {
try {
msg.setSentDate(new Date());
msg.send();
return true;
} catch (Throwable e) {
MailException me = new MailException(
"Error while sending email", e);
Logger.error("The email has not been sent", me);
return false;
}
}
});
} else {
final StringBuffer result = new StringBuffer();
try {
msg.setSentDate(new Date());
msg.send();
} catch (Throwable e) {
MailException me = new MailException(
"Error while sending email", e);
Logger.error("The email has not been sent", me);
result.append("oops");
}
return new Future<Boolean>() {
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
public boolean isCancelled() {
return false;
}
public boolean isDone() {
return true;
}
public Boolean get() throws InterruptedException,
ExecutionException {
return result.length() == 0;
}
public Boolean get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException,
TimeoutException {
return result.length() == 0;
}
};
}
}
static ExecutorService executor = Executors.newCachedThreadPool();
public static class SMTPAuthenticator extends Authenticator {
private String user;
private String password;
public SMTPAuthenticator(String user, String password) {
this.user = user;
this.password = password;
}
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, password);
}
}
public static class Mock {
static Map<String, String> emails = new HashMap<String, String>();
public static String getContent(Part message)
throws MessagingException, IOException {
if (message.getContent() instanceof String) {
return message.getContentType() + ": " + message.getContent()
+ " \n\t";
} else if (message.getContent() != null
&& message.getContent() instanceof Multipart) {
Multipart part = (Multipart) message.getContent();
String text = "";
for (int i = 0; i < part.getCount(); i++) {
BodyPart bodyPart = part.getBodyPart(i);
if (!Message.ATTACHMENT.equals(bodyPart.getDisposition())) {
text += getContent(bodyPart);
} else {
text += "attachment: \n"
+ "\t\t name: "
+ (StringUtils.isEmpty(bodyPart.getFileName()) ? "none"
: bodyPart.getFileName())
+ "\n"
+ "\t\t disposition: "
+ bodyPart.getDisposition()
+ "\n"
+ "\t\t description: "
+ (StringUtils.isEmpty(bodyPart
.getDescription()) ? "none" : bodyPart
.getDescription()) + "\n\t";
}
}
return text;
}
if (message.getContent() != null
&& message.getContent() instanceof Part) {
if (!Message.ATTACHMENT.equals(message.getDisposition())) {
return getContent((Part) message.getContent());
} else {
return "attachment: \n"
+ "\t\t name: "
+ (StringUtils.isEmpty(message.getFileName()) ? "none"
: message.getFileName())
+ "\n"
+ "\t\t disposition: "
+ message.getDisposition()
+ "\n"
+ "\t\t description: "
+ (StringUtils.isEmpty(message.getDescription()) ? "none"
: message.getDescription()) + "\n\t";
}
}
return "";
}
static void send(Email email) {
try {
final StringBuffer content = new StringBuffer();
Properties props = new Properties();
props.put("mail.smtp.host", "myfakesmtpserver.com");
Session session = Session.getInstance(props);
email.setMailSession(session);
email.buildMimeMessage();
MimeMessage msg = email.getMimeMessage();
msg.saveChanges();
String body = getContent(msg);
content.append("From Mock Mailer\n\tNew email received by");
content.append("\n\tFrom: "
+ email.getFromAddress().getAddress());
content.append("\n\tReplyTo: "
+ ((InternetAddress) email.getReplyToAddresses().get(0))
.getAddress());
content.append("\n\tTo: ");
for (Object add : email.getToAddresses()) {
content.append(add.toString() + ", ");
}
// remove the last ,
content.delete(content.length() - 2, content.length());
if (email.getCcAddresses() != null
&& !email.getCcAddresses().isEmpty()) {
content.append("\n\tCc: ");
for (Object add : email.getCcAddresses()) {
content.append(add.toString() + ", ");
}
// remove the last ,
content.delete(content.length() - 2, content.length());
}
if (email.getBccAddresses() != null
&& !email.getBccAddresses().isEmpty()) {
content.append("\n\tBcc: ");
for (Object add : email.getBccAddresses()) {
content.append(add.toString() + ", ");
}
// remove the last ,
content.delete(content.length() - 2, content.length());
}
content.append("\n\tSubject: " + email.getSubject());
content.append("\n\t" + body);
content.append("\n");
Logger.info(content.toString());
for (Object add : email.getToAddresses()) {
content.append(", " + add.toString());
emails.put(((InternetAddress) add).getAddress(),
content.toString());
}
} catch (Exception e) {
Logger.error("error sending mock email", e);
}
}
public static String getLastMessageReceivedBy(String email) {
return emails.get(email);
}
public static void reset() {
emails.clear();
}
}
}