001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu 018 */ 019package org.nuxeo.ecm.automation.core.mail; 020 021import static org.nuxeo.mail.MailConstants.CONFIGURATION_JNDI_JAVA_MAIL; 022import static org.nuxeo.mail.MailConstants.DEFAULT_MAIL_JNDI_NAME; 023 024import java.io.File; 025import java.io.FileInputStream; 026import java.io.IOException; 027import java.io.StringReader; 028import java.io.StringWriter; 029import java.io.Writer; 030import java.net.URL; 031import java.util.List; 032import java.util.Properties; 033import java.util.concurrent.ConcurrentHashMap; 034import java.util.concurrent.ConcurrentMap; 035 036import javax.activation.DataHandler; 037import javax.mail.MessagingException; 038import javax.mail.internet.MimeBodyPart; 039import javax.mail.internet.MimeMultipart; 040import javax.validation.constraints.NotNull; 041 042import org.apache.commons.logging.Log; 043import org.apache.commons.logging.LogFactory; 044import org.nuxeo.ecm.core.api.Blob; 045import org.nuxeo.ecm.platform.rendering.api.RenderingException; 046import org.nuxeo.ecm.platform.rendering.api.ResourceLocator; 047import org.nuxeo.ecm.platform.rendering.fm.FreemarkerEngine; 048import org.nuxeo.runtime.api.Framework; 049 050import freemarker.core.Environment; 051import freemarker.template.Template; 052import freemarker.template.TemplateException; 053 054/** 055 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 056 */ 057public class Composer { 058 059 private static final Log log = LogFactory.getLog(Composer.class); 060 061 /** Mail properties read from mail.properties. */ 062 protected static final Properties MAIL_PROPERTIES = new Properties(); 063 064 protected final FreemarkerEngine engine; 065 066 protected Mailer mailer; 067 068 // FIXME: don't put URLs in Maps, this is a serious performance issue. 069 protected final ConcurrentMap<String, URL> urls; 070 071 public Composer() { 072 this(null); 073 } 074 075 public Composer(Mailer mailer) { 076 urls = new ConcurrentHashMap<>(); 077 if (mailer == null) { 078 this.mailer = createMailer(); 079 } else { 080 this.mailer = mailer; 081 } 082 engine = new FreemarkerEngine(); 083 engine.setResourceLocator(new ResourceLocator() { 084 @Override 085 public URL getResourceURL(String key) { 086 return urls.get(key); 087 } 088 089 @Override 090 public File getResourceFile(String key) { 091 return null; 092 } 093 }); 094 } 095 096 protected Mailer createMailer() { 097 // first try the local configuration 098 // it was used by FakeSmtpMailServerFeature / EmbeddedAutomationClientTest#testSendMail - no runtime usage found 099 var properties = getMailProperties(); 100 if (!properties.isEmpty()) { 101 mailer = new Mailer(properties); 102 } 103 // second try using JNDI 104 if (mailer == null) { 105 String name = Framework.getProperty(CONFIGURATION_JNDI_JAVA_MAIL, DEFAULT_MAIL_JNDI_NAME); 106 mailer = new Mailer(name); 107 } 108 return mailer; 109 } 110 111 @NotNull 112 protected static Properties getMailProperties() { 113 File mailFile = getMailPropertiesFile(); 114 if ((mailFile != null && MAIL_PROPERTIES.isEmpty()) || Framework.isTestModeSet()) { 115 synchronized (MAIL_PROPERTIES) { 116 if ((mailFile != null && MAIL_PROPERTIES.isEmpty()) || Framework.isTestModeSet()) { 117 MAIL_PROPERTIES.clear(); 118 if (mailFile != null) { 119 try (FileInputStream in = new FileInputStream(mailFile)) { 120 MAIL_PROPERTIES.load(in); 121 } catch (IOException e) { 122 log.error("Failed to load mail properties", e); 123 } 124 } 125 } 126 } 127 } 128 return MAIL_PROPERTIES; 129 } 130 131 protected static File getMailPropertiesFile() { 132 org.nuxeo.common.Environment env = org.nuxeo.common.Environment.getDefault(); 133 if (env != null) { 134 File file = new File(env.getConfig(), "mail.properties"); 135 if (file.isFile()) { 136 return file; 137 } 138 } 139 return null; 140 } 141 142 public void registerTemplate(URL url) { 143 urls.put(url.toExternalForm(), url); 144 } 145 146 public void unregisterTemplate(URL url) { 147 urls.remove(url.toExternalForm()); 148 } 149 150 public void unregisterAllTemplates() { 151 urls.clear(); 152 } 153 154 public Mailer getMailer() { 155 return mailer; 156 } 157 158 public FreemarkerEngine getEngine() { 159 return engine; 160 } 161 162 public void render(String template, Object ctx, Writer writer) throws RenderingException { 163 engine.render(template, ctx, writer); 164 } 165 166 public void render(URL template, Object ctx, Writer writer) throws RenderingException { 167 String key = template.toExternalForm(); 168 urls.putIfAbsent(key, template); 169 engine.render(key, ctx, writer); 170 } 171 172 public String render(URL template, Object ctx) throws RenderingException { 173 String key = template.toExternalForm(); 174 urls.putIfAbsent(key, template); 175 StringWriter writer = new StringWriter(); 176 engine.render(key, ctx, writer); 177 return writer.toString(); 178 } 179 180 public String render(String templateContent, Object ctx) throws TemplateException, IOException { 181 StringReader reader = new StringReader(templateContent); 182 Template temp = new Template("@inline", reader, engine.getConfiguration(), "UTF-8"); 183 StringWriter writer = new StringWriter(); 184 Environment env = temp.createProcessingEnvironment(ctx, writer, engine.getObjectWrapper()); 185 env.process(); 186 return writer.toString(); 187 } 188 189 public Mailer.Message newMessage() { 190 return mailer.newMessage(); 191 } 192 193 public Mailer.Message newTextMessage(URL template, Object ctx) throws RenderingException, MessagingException { 194 Mailer.Message msg = mailer.newMessage(); 195 msg.setText(render(template, ctx), "UTF-8"); 196 return msg; 197 } 198 199 public Mailer.Message newTextMessage(String templateContent, Object ctx) throws RenderingException, 200 MessagingException, TemplateException, IOException { 201 Mailer.Message msg = mailer.newMessage(); 202 msg.setText(render(templateContent, ctx), "UTF-8"); 203 return msg; 204 } 205 206 public Mailer.Message newHtmlMessage(URL template, Object ctx) throws RenderingException, MessagingException { 207 Mailer.Message msg = mailer.newMessage(); 208 msg.setContent(render(template, ctx), "text/html; charset=utf-8"); 209 return msg; 210 } 211 212 public Mailer.Message newHtmlMessage(String templateContent, Object ctx) throws MessagingException, 213 TemplateException, IOException { 214 Mailer.Message msg = mailer.newMessage(); 215 msg.setContent(render(templateContent, ctx), "text/html; charset=utf-8"); 216 return msg; 217 } 218 219 public Mailer.Message newMixedMessage(String templateContent, Object ctx, String textType, List<Blob> attachments) 220 throws TemplateException, IOException, MessagingException { 221 if (textType == null) { 222 textType = "plain"; 223 } 224 Mailer.Message msg = mailer.newMessage(); 225 MimeMultipart mp = new MimeMultipart(); 226 MimeBodyPart body = new MimeBodyPart(); 227 String result = render(templateContent, ctx); 228 body.setText(result, "UTF-8", textType); 229 mp.addBodyPart(body); 230 for (Blob blob : attachments) { 231 MimeBodyPart a = new MimeBodyPart(); 232 a.setDataHandler(new DataHandler(new BlobDataSource(blob))); 233 a.setFileName(blob.getFilename()); 234 mp.addBodyPart(a); 235 } 236 msg.setContent(mp); 237 return msg; 238 } 239 240}