Java tutorial
/* * Copyright (c) 2008-2011, Martijn Brinkers, Djigzo. * * This file is part of Djigzo email encryption. * * Djigzo is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3, 19 November 2007 as published by the Free Software * Foundation. * * Djigzo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public * License along with Djigzo. If not, see <http://www.gnu.org/licenses/> * * Additional permission under GNU AGPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, * wsdl4j-1.6.1.jar (or modified versions of these libraries), * containing parts covered by the terms of Eclipse Public License, * tyrex license, freemarker license, dom4j license, mx4j license, * Spice Software License, Common Development and Distribution License * (CDDL), Common Public License (CPL) the licensors of this Program grant * you additional permission to convey the resulting work. */ package mitm.application.djigzo.james.mailets; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.Collection; import java.util.Date; import java.util.LinkedList; import java.util.UUID; import javax.mail.Address; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMessage.RecipientType; import mitm.application.djigzo.james.MailAddressUtils; import mitm.application.djigzo.james.MailetUtils; import mitm.application.djigzo.james.SystemMailAttributes; import mitm.application.djigzo.service.SystemServices; import mitm.common.mail.MailUtils; import mitm.common.template.TemplateBuilder; import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.text.StrBuilder; import org.apache.james.core.MailImpl; import org.apache.mailet.Mail; import org.apache.mailet.MailAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import freemarker.template.SimpleHash; import freemarker.template.Template; import freemarker.template.TemplateException; import freemarker.template.TemplateModelException; /** * SendMailTemplate mailet sends an email using a template (a Freemarker template) as the base for the email to all recipients. * * The following template parameters are supported: * * subject : Set to subject of source mail (Address) * from : Set to from of source mail (only the first from is used) (Address) * replyTo : Set to replyTo of source mail (only the first replyTo is used) (Address) * to : Set to 'to' of the source mail (collection of Address) * cc : Set to 'cc' of the source mail (collection of Address) * sender : Set to the sender of the mail (MailAddress) * recipients : Set to recipients of the source mail (collection of MailAddress) * body : Set to null * date : Current date (Date) * mail : The source mail (Mail) * * Supported mailet parameters: * * template : Path to the Freemarker template file. The file must exist (no default, must be specified) * processor : The next processor for the new mail (default: root) * passThrough : If false the source message will be removed (ghost'ed) * passThroughProcessor : If passThrough is true the pass through message next state will be set to passThroughProcessor * catchRuntimeExceptions : If true all RunTimeExceptions are caught (default: true) * catchErrors : If true all Errors are caught (default: true) * * @author Martijn Brinkers * */ public class SendMailTemplate extends AbstractDjigzoMailet { private final static Logger logger = LoggerFactory.getLogger(SendMailTemplate.class); /* * General SendMailTemplate Freemarker template parameter names */ protected static final String SUBJECT_TEMPLATE_PARAM = "subject"; protected static final String FROM_TEMPLATE_PARAM = "from"; protected static final String REPLYTO_TEMPLATE_PARAM = "replyTo"; protected static final String TO_TEMPLATE_PARAM = "to"; protected static final String CC_TEMPLATE_PARAM = "cc"; protected static final String SENDER_TEMPLATE_PARAM = "sender"; protected static final String RECIPIENTS_TEMPLATE_PARAM = "recipients"; protected static final String BODY_TEMPLATE_PARAM = "body"; protected static final String DATE_TEMPLATE_PARAM = "date"; protected static final String MAIL_TEMPLATE_PARAM = "mail"; protected static final String BOUNDARY_TEMPLATE_PARAM = "boundary"; /* * For building Freemarker templates */ private TemplateBuilder templateBuilder; /* * The Freemarker template for the message. */ private String templateFile; /* * The processor for the notification message. */ private String processor = Mail.DEFAULT; /* * Should the SMTP extensions attributes be removed from the Mail after cloning. */ private boolean removeSMTPExtensions; /* * If true, missing recipients are not considered to be an error */ private boolean ignoreMissingRecipients; /* * If true the source message will continue to be used. If false the source message will * be ghost'ed. */ private boolean passThrough = true; /* * If passThrough is true the pass through message next state will be set * to passThroughProcessor. If null next state will not be changed. */ private String passThroughProcessor; /* * Thrown when some internal method needs to report that there are no recipients. */ protected static class MissingRecipientsException extends MessagingException { private static final long serialVersionUID = 765760228227617458L; public MissingRecipientsException(String message) { super(message); } } /* * The mailet initialization parameters used by this mailet. */ protected enum Parameter { TEMPLATE("template"), PROCESSOR("processor"), REMOVE_SMTP_EXTENSIONS( "removeSMTPExtensions"), IGNORE_MISSING_RECIPIENTS("ignoreMissingRecipients"), PASS_THROUGH( "passThrough"), PASS_THROUGH_PROCESSOR("passThroughProcessor"); private String name; private Parameter(String name) { this.name = name; } public static Parameter fromName(String name) { for (Parameter parameter : Parameter.values()) { if (parameter.name.equals(name)) { return parameter; } } return null; } public String getName() { return name; } }; @Override protected Logger getLogger() { return logger; } private void loadTemplate() { templateFile = getInitParameter(Parameter.TEMPLATE.name); if (templateFile == null) { throw new IllegalArgumentException("template must be specified."); } /* * Validate the template (if it can be found) */ try { templateBuilder.getTemplate(templateFile); } catch (IOException e) { throw new IllegalArgumentException("Error loading the template.", e); } } protected boolean getInitRemoveSMTPExtensions() { return getBooleanInitParameter(Parameter.REMOVE_SMTP_EXTENSIONS.name, false); } protected boolean getIgnoreMissingRecipients() { return getBooleanInitParameter(Parameter.IGNORE_MISSING_RECIPIENTS.name, false); } @Override public void initMailet() throws MessagingException { getLogger().info("Initializing mailet: " + getMailetName()); templateBuilder = SystemServices.getTemplateBuilder(); loadTemplate(); String param = getInitParameter(Parameter.PROCESSOR.name); if (param != null) { processor = param; } removeSMTPExtensions = getInitRemoveSMTPExtensions(); ignoreMissingRecipients = getIgnoreMissingRecipients(); param = getInitParameter(Parameter.PASS_THROUGH.name); if (param != null) { Boolean bool = BooleanUtils.toBooleanObject(param); if (bool == null) { throw new IllegalArgumentException(param + " is not a valid boolean."); } passThrough = bool; } passThroughProcessor = getInitParameter(Parameter.PASS_THROUGH_PROCESSOR.name); StrBuilder sb = new StrBuilder(); sb.append("template: "); sb.append(templateFile); sb.appendSeparator("; "); sb.appendSeparator("; "); sb.append("processor: "); sb.append(processor); sb.appendSeparator("; "); sb.append("removeSMTPExtensions: "); sb.append(removeSMTPExtensions); sb.appendSeparator("; "); sb.append("ignoreMissingRecipients: "); sb.append(ignoreMissingRecipients); sb.appendSeparator("; "); sb.append("passThrough"); sb.append(passThrough); sb.appendSeparator("; "); sb.append("passThroughProcessor"); sb.append(passThroughProcessor); getLogger().info(sb.toString()); } protected Address getFrom(Mail mail) throws MessagingException { Address from = null; try { Address[] froms = mail.getMessage().getFrom(); from = (froms != null && froms.length > 0) ? froms[0] : null; } catch (MessagingException e) { logger.warn("From address is not a valid email address."); } return from; } protected Collection<MailAddress> getRecipients(Mail mail) throws MessagingException, MissingRecipientsException { return MailAddressUtils.getRecipients(mail); } protected Address getReplyTo(Mail mail) throws MessagingException { Address replyTo = null; try { Address[] replies = mail.getMessage().getReplyTo(); replyTo = (replies != null && replies.length > 0) ? replies[0] : null; } catch (MessagingException e) { logger.warn("Reply-To address is not a valid email address"); } return replyTo; } protected MailAddress getSender(Mail mail) throws MessagingException { return mail.getSender(); } protected String getSubject(Mail mail) throws MessagingException { return mail.getMessage().getSubject(); } protected Collection<Address> getTo(Mail mail) throws MessagingException { Collection<Address> to = new LinkedList<Address>(); try { Address[] addresses = mail.getMessage().getRecipients(RecipientType.TO); if (addresses != null) { for (Address address : addresses) { to.add(address); } } } catch (MessagingException e) { logger.warn("To is not a valid email address."); } return to; } protected Collection<Address> getCC(Mail mail) throws MessagingException { Collection<Address> cc = new LinkedList<Address>(); try { Address[] addresses = mail.getMessage().getRecipients(RecipientType.CC); if (addresses != null) { for (Address address : addresses) { cc.add(address); } } } catch (MessagingException e) { logger.warn("CC is not a valid email address."); } return cc; } protected String getBody(Mail mail) throws MessagingException { return null; } protected Date getDate(Mail mail) { return new Date(); } /* * Creates a new unique message boundary which will be used for multipart messages. */ private String createBoundary() { String boundary = "=-" + UUID.randomUUID().toString(); return boundary; } /* * Duplicates the source mail */ protected MailImpl duplicateMail(Mail source) throws MessagingException { /* * We need to create a new Mail object this way to make sure that all attributes are cloned. */ return new MailImpl(source, MailetUtils.createUniqueMailName()); } private void sendMail(Mail mail, MimeMessage notificationMessage, MailAddress sender, Collection<MailAddress> recipients) throws MessagingException, MissingRecipientsException { MailImpl newMail = duplicateMail(mail); if (removeSMTPExtensions) { SystemMailAttributes.removeSMTPExtensions(newMail); } try { newMail.setSender(sender); newMail.setRecipients(recipients); newMail.setMessage(notificationMessage); newMail.setState(processor); getMailetContext().sendMail(newMail); } finally { newMail.dispose(); } } protected void sendMail(Mail mail) throws MessagingException, MissingRecipientsException, TemplateException, IOException { MimeMessage message = createMessage(mail, new SimpleHash()); MailAddress sender = getSender(mail); Collection<MailAddress> recipients = getRecipients(mail); sendMail(mail, message, sender, recipients); } protected MimeMessage createMessage(Mail mail, SimpleHash root) throws MessagingException, MissingRecipientsException, TemplateException, IOException { String subject = getSubject(mail); Address from = getFrom(mail); Address replyTo = getReplyTo(mail); Collection<Address> to = getTo(mail); Collection<Address> cc = getCC(mail); MailAddress sender = getSender(mail); Collection<MailAddress> recipients = getRecipients(mail); String body = getBody(mail); Date date = getDate(mail); return createMessage(mail, subject, from, replyTo, to, cc, sender, recipients, body, date, root); } protected Template getTemplate(Mail mail, SimpleHash root) throws IOException, MessagingException { return templateBuilder.getTemplate(templateFile); } /* * Sets the root parameter if a value for key is not already available */ private void setFreemarkerParam(SimpleHash root, String key, Object value) throws TemplateModelException { if (root.get(key) == null) { root.put(key, value); } } private MimeMessage createMessage(Mail mail, String subject, Address from, Address replyTo, Collection<Address> to, Collection<Address> cc, MailAddress sender, Collection<MailAddress> recipients, String body, Date date, SimpleHash root) throws MessagingException, MissingRecipientsException, TemplateException, IOException { setFreemarkerParam(root, SUBJECT_TEMPLATE_PARAM, subject); setFreemarkerParam(root, FROM_TEMPLATE_PARAM, from); setFreemarkerParam(root, REPLYTO_TEMPLATE_PARAM, replyTo); setFreemarkerParam(root, TO_TEMPLATE_PARAM, to); setFreemarkerParam(root, CC_TEMPLATE_PARAM, cc); setFreemarkerParam(root, SENDER_TEMPLATE_PARAM, sender); setFreemarkerParam(root, RECIPIENTS_TEMPLATE_PARAM, recipients); setFreemarkerParam(root, BODY_TEMPLATE_PARAM, body); setFreemarkerParam(root, DATE_TEMPLATE_PARAM, date); setFreemarkerParam(root, MAIL_TEMPLATE_PARAM, mail); setFreemarkerParam(root, BOUNDARY_TEMPLATE_PARAM, createBoundary()); ByteArrayOutputStream bos = new ByteArrayOutputStream(); Writer writer = new OutputStreamWriter(bos); getTemplate(mail, root).process(root, writer); MimeMessage templatedMessage = MailUtils.byteArrayToMessage(bos.toByteArray()); /* * Make sure the message has a message-ID */ templatedMessage.saveChanges(); return templatedMessage; } protected void internalServiceMail(Mail mail) throws MessagingException, MissingRecipientsException, TemplateException, IOException { sendMail(mail); } @Override public void serviceMail(Mail mail) { try { try { internalServiceMail(mail); } catch (MissingRecipientsException e) { if (!ignoreMissingRecipients) { throw e; } getLogger().debug("Ignoring MissingRecipientsException.", e); } if (!passThrough) { mail.setState(Mail.GHOST); } else if (StringUtils.isNotEmpty(passThroughProcessor)) { mail.setState(passThroughProcessor); } } catch (MissingRecipientsException e) { getLogger().error("Missing recipients.", e); } catch (MessagingException e) { getLogger().error("Unhandled exception.", e); } catch (TemplateException e) { getLogger().error("Error processing template.", e); } catch (IOException e) { getLogger().error("Error processing template.", e); } } /** * Returns the template builder service. */ protected TemplateBuilder getTemplateBuilder() { return templateBuilder; } }