package cn.bran.play;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.mail.internet.InternetAddress;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.mail.Email;
import org.apache.commons.mail.EmailAttachment;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import org.apache.commons.mail.MultiPartEmail;
import org.apache.commons.mail.SimpleEmail;
import play.Logger;
import play.Play;
import play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer;
import play.exceptions.MailException;
import play.exceptions.UnexpectedException;
import play.libs.Mail;
import cn.bran.japid.template.JapidTemplateBase;
import cn.bran.japid.template.RenderResult;
import cn.bran.japid.util.StackTraceUtils;
/**
* Application mailer support, based on the Mail class in the Play distribution.
*
* Only html format is support for now.
*
* The default template path is : /app/japidviews/_notifiers/{custom mailer package}/{custom mailer class}/{method name}.html
*/
public class JapidMailer {
private static final String TEXT_HTML = "text/html";
private static final String TEXT_PLAIN = "text/plain";
private static final String CONTROLLERS = "controllers.";
private static final String NOTIFIERS = "notifiers.";
private static final String METHOD = "method";
private static final String HEADERS = "headers";
private static final String SUBJECT = "subject";
private static final String CHARSET = "charset";
private static final String REPLY_TO = "replyTo";
private static final String FROM = "from";
private static final String CONTENT_TYPE = "contentType";
private static final String ATTACHMENTS = "attachments";
private static final String CCS = "ccs";
private static final String BCCS = "bccs";
private static final String RECIPIENTS = "recipients";
protected static ThreadLocal<HashMap<String, Object>> infos = new ThreadLocal<HashMap<String, Object>>() {
@Override
protected HashMap<String, Object> initialValue() {
return new HashMap<String, Object>();
}
};
public static void setSubject(String subject, Object... args) {
HashMap<String, Object> map = getInfoMap();
map.put(SUBJECT, String.format(subject, args));
infos.set(map);
}
/**
* @return
*/
private static HashMap<String, Object> getInfoMap() {
HashMap<String, Object> map = infos.get();
if (map == null) {
throw new UnexpectedException("Mailer not instrumented ?");
}
return map;
}
@SuppressWarnings("unchecked")
public static void addRecipient(Object... recipients) {
HashMap<String, Object> map = getInfoMap();
List recipientsList = (List<String>) map.get(RECIPIENTS);
if (recipientsList == null) {
recipientsList = new ArrayList<String>();
map.put(RECIPIENTS, recipientsList);
}
recipientsList.addAll(Arrays.asList(recipients));
infos.set(map);
}
@SuppressWarnings("unchecked")
public static void addBcc(Object... bccs) {
HashMap<String, Object> map = getInfoMap();
List bccsList = (List<String>) map.get(BCCS);
if (bccsList == null) {
bccsList = new ArrayList<String>();
map.put(BCCS, bccsList);
}
bccsList.addAll(Arrays.asList(bccs));
infos.set(map);
}
@SuppressWarnings("unchecked")
public static void addCc(Object... ccs) {
HashMap<String, Object> map = getInfoMap();
List ccsList = (List<String>) map.get(CCS);
if (ccsList == null) {
ccsList = new ArrayList<String>();
map.put(CCS, ccsList);
}
ccsList.addAll(Arrays.asList(ccs));
infos.set(map);
}
@SuppressWarnings("unchecked")
public static void addAttachment(EmailAttachment... attachments) {
HashMap<String, Object> map = getInfoMap();
List<EmailAttachment> attachmentsList = (List<EmailAttachment>) map.get(ATTACHMENTS);
if (attachmentsList == null) {
attachmentsList = new ArrayList<EmailAttachment>();
map.put(ATTACHMENTS, attachmentsList);
}
attachmentsList.addAll(Arrays.asList(attachments));
infos.set(map);
}
public static void setContentType(String contentType) {
HashMap<String, Object> map = getInfoMap();
map.put(CONTENT_TYPE, contentType);
infos.set(map);
}
/**
* Can be of the form xxx <m@m.com>
*
* @param from
*/
public static void setFrom(Object from) {
HashMap<String, Object> map = getInfoMap();
map.put(FROM, from);
infos.set(map);
}
/**
* Can be of the form xxx <m@m.com>
*
* @param replyTo
*/
public static void setReplyTo(Object replyTo) {
HashMap<String, Object> map = getInfoMap();
map.put(REPLY_TO, replyTo);
infos.set(map);
}
public static void setCharset(String bodyCharset) {
HashMap<String, Object> map = getInfoMap();
map.put(CHARSET, bodyCharset);
infos.set(map);
}
@SuppressWarnings("unchecked")
public static void addHeader(String key, String value) {
HashMap<String, Object> map = getInfoMap();
HashMap<String, String> headers = (HashMap<String, String>) map.get(HEADERS);
if (headers == null) {
headers = new HashMap<String, String>();
}
headers.put(key, value);
map.put(HEADERS, headers);
infos.set(map);
}
@SuppressWarnings("unchecked")
public static Future<Boolean> send(Object... args) {
try {
final HashMap<String, Object> infoMap = getInfoMap();
// Body character set
final String charset = (String) infoMap.get(CHARSET);
// Headers
final Map<String, String> headers = (Map<String, String>) infoMap.get(HEADERS);
// Subject
final String subject = (String) infoMap.get(SUBJECT);
// xxx how to determine the method name???
// String templateName = (String) infoMap.get(METHOD);
String templateNameBase = StackTraceUtils.getCaller();
if (!templateNameBase.startsWith("notifiers")) {
throw new RuntimeException("The emailers must be put in the \"notifiers\" package.");
}
// if (templateNameBase.startsWith(NOTIFIERS)) {
// templateNameBase = templateNameBase.substring(NOTIFIERS.length());
// }
// if (templateNameBase.startsWith(CONTROLLERS)) {
// templateNameBase = templateNameBase.substring(CONTROLLERS.length());
// }
// templateNameBase = templateNameBase.substring(0, templateNameBase.indexOf("("));
// templateNameBase = templateNameBase.replace(".", "/");
// final Map<String, Object> templateHtmlBinding = new HashMap<String, Object>();
// final Map<String, Object> templateTextBinding = new HashMap<String, Object>();
// for (Object o : args) {
// List<String> names = LocalVariablesNamesTracer.getAllLocalVariableNames(o);
// for (String name : names) {
// templateHtmlBinding.put(name, o);
// templateTextBinding.put(name, o);
// }
// }
String templateClassName = JapidPlugin.JAPIDVIEWS_ROOT + "._" + templateNameBase;
String bodyHtml = null;
Class tClass = Play.classloader.getClassIgnoreCase(templateClassName);
if (tClass == null) {
String templateFileName = templateClassName.replace('.', '/') + ".html";
throw new RuntimeException("Japid Emailer: could not find a Japid template with the name of: " + templateFileName);
} else if (JapidTemplateBase.class.isAssignableFrom(tClass)) {
try {
JapidController.render(tClass, args);
} catch (JapidResult jr) {
RenderResult rr = jr.getRenderResult();
bodyHtml = rr.getContent().toString();
}
} else {
throw new RuntimeException("The found class is not a Japid template class: " + templateClassName);
}
// System.out.println("email body: " + bodyHtml);
// The rule is as follow: If we ask for text/plain, we don't care about the HTML
// If we ask for HTML and there is a text/plain we add it as an alternative.
// If contentType is not specified look at the template available:
// - .txt only -> text/plain
// else
// - -> text/html
// String contentType = (String) infoMap.get(CONTENT_TYPE);
// String bodyText = "";
// try {
// Template templateHtml = TemplateLoader.load(templateNameBase + ".html");
// bodyHtml = templateHtml.render(templateHtmlBinding);
// } catch (TemplateNotFoundException e) {
// if (contentType != null && !contentType.startsWith(TEXT_PLAIN)) {
// throw e;
// }
// }
////
// try {
// Template templateText = TemplateLoader.load(templateName + ".txt");
// bodyText = templateText.render(templateTextBinding);
// } catch (TemplateNotFoundException e) {
// if (bodyHtml == null && (contentType == null || contentType.startsWith(TEXT_PLAIN))) {
// throw e;
// }
// }
// Content type
// bran html for now
// if (contentType == null) {
// if (bodyHtml != null) {
// contentType = TEXT_HTML;
// } else {
// contentType = TEXT_PLAIN;
// }
// }
// Recipients
final List<Object> recipientList = (List<Object>) infoMap.get(RECIPIENTS);
// From
final Object from = infoMap.get(FROM);
final Object replyTo = infoMap.get(REPLY_TO);
Email email = null;
if (infoMap.get(ATTACHMENTS) == null) {
// if (StringUtils.isEmpty(bodyHtml)) {
// email = new SimpleEmail();
// email.setMsg(bodyText);
// } else {
HtmlEmail htmlEmail = new HtmlEmail();
htmlEmail.setHtmlMsg(bodyHtml);
// if (!StringUtils.isEmpty(bodyText)) {
// htmlEmail.setTextMsg(bodyText);
// }
email = htmlEmail;
// }
} else {
// if (StringUtils.isEmpty(bodyHtml)) {
// email = new MultiPartEmail();
// email.setMsg(bodyText);
// } else {
HtmlEmail htmlEmail = new HtmlEmail();
htmlEmail.setHtmlMsg(bodyHtml);
// if (!StringUtils.isEmpty(bodyText)) {
// htmlEmail.setTextMsg(bodyText);
// }
email = htmlEmail;
// }
MultiPartEmail multiPartEmail = (MultiPartEmail) email;
List<EmailAttachment> objectList = (List<EmailAttachment>) infoMap.get(ATTACHMENTS);
for (EmailAttachment object : objectList) {
multiPartEmail.attach(object);
}
}
if (from != null) {
try {
InternetAddress iAddress = new InternetAddress(from.toString());
email.setFrom(iAddress.getAddress(), iAddress.getPersonal());
} catch (Exception e) {
email.setFrom(from.toString());
}
}
if (replyTo != null) {
try {
InternetAddress iAddress = new InternetAddress(replyTo.toString());
email.addReplyTo(iAddress.getAddress(), iAddress.getPersonal());
} catch (Exception e) {
email.addReplyTo(replyTo.toString());
}
}
if (recipientList != null) {
for (Object recipient : recipientList) {
try {
InternetAddress iAddress = new InternetAddress(recipient.toString());
email.addTo(iAddress.getAddress(), iAddress.getPersonal());
} catch (Exception e) {
email.addTo(recipient.toString());
}
}
} else {
throw new MailException("You must specify at least one recipient.");
}
List<Object> ccsList = (List<Object>) infoMap.get(CCS);
if (ccsList != null) {
for (Object cc : ccsList) {
email.addCc(cc.toString());
}
}
List<Object> bccsList = (List<Object>) infoMap.get(BCCS);
if (bccsList != null) {
for (Object bcc : bccsList) {
try {
InternetAddress iAddress = new InternetAddress(bcc.toString());
email.addBcc(iAddress.getAddress(), iAddress.getPersonal());
} catch (Exception e) {
email.addBcc(bcc.toString());
}
}
}
if (!StringUtils.isEmpty(charset)) {
email.setCharset(charset);
}
email.setSubject(subject);
email.updateContentType(TEXT_HTML);
if (headers != null) {
for (String key : headers.keySet()) {
email.addHeader(key, headers.get(key));
}
}
// reset the infomap
infos.remove();
return Mail.send(email);
} catch (EmailException ex) {
throw new MailException("Cannot send email", ex);
}
}
public static boolean sendAndWait(Object... args) {
try {
Future<Boolean> result = send(args);
return result.get();
} catch (InterruptedException e) {
Logger.error(e, "Error while waiting Mail.send result");
} catch (ExecutionException e) {
Logger.error(e, "Error while waiting Mail.send result");
}
return false;
}
}