1 package com.ozacc.mail.impl;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.StringReader;
7 import java.io.StringWriter;
8 import java.util.HashMap;
9 import java.util.List;
10 import java.util.Map;
11
12 import org.apache.commons.logging.Log;
13 import org.apache.commons.logging.LogFactory;
14 import org.apache.velocity.VelocityContext;
15 import org.apache.velocity.app.Velocity;
16 import org.apache.velocity.exception.MethodInvocationException;
17 import org.apache.velocity.exception.ParseErrorException;
18 import org.apache.velocity.exception.ResourceNotFoundException;
19 import org.apache.velocity.runtime.log.LogSystem;
20 import org.jdom.Document;
21 import org.jdom.Element;
22 import org.jdom.JDOMException;
23 import org.jdom.input.SAXBuilder;
24 import org.jdom.output.XMLOutputter;
25
26 import com.ozacc.mail.Mail;
27 import com.ozacc.mail.MailBuildException;
28 import com.ozacc.mail.VelocityMailBuilder;
29
30 /***
31 * <a href="http://www.jdom.org/">JDOM</a>を利用してXMLファイルからMailインスタンスを生成するクラス。
32 * <p>
33 * ソースXMLを読み込む際に、DTDバリデーションが実行されますので妥当なXMLデータ(Valid XML Document)でなければいけません。
34 *
35 * @since 1.0
36 *
37 * @author Tomohiro Otsuka
38 * @version $Id: JDomXMLMailBuilder.java,v 1.13 2005/01/17 15:32:35 otsuka Exp $
39 */
40 public class JDomXMLMailBuilder implements VelocityMailBuilder {
41
42 private static Log log = LogFactory.getLog(JDomXMLMailBuilder.class);
43
44 protected LogSystem velocityLogSystem = new VelocityLogSystem();
45
46 private boolean cacheEnabled = false;
47
48 protected Map templateCache = new HashMap();
49
50 /***
51 * コンストラクタ。
52 */
53 public JDomXMLMailBuilder() {}
54
55 /***
56 * 指定されたクラスパス上のXMLファイルからMailインスタンスを生成します。
57 *
58 * @param classPath メール内容を記述したXMLファイルのパス
59 * @return 生成されたMailインスタンス
60 * @throws MailBuildException Mailインスタンスの生成に失敗した場合
61 */
62 public Mail buildMail(String classPath) throws MailBuildException {
63 Document doc = getDocumentFromClassPath(classPath);
64 return build(doc);
65 }
66
67 /***
68 * 指定されたクラスパス上のXMLファイルからMailインスタンスを生成します。
69 * 指定されたVelocityContextを使って、XMLファイルの内容を動的に生成できます。
70 *
71 * @param classPath メール内容を記述したXMLファイルのパス
72 * @param context VelocityContext
73 * @return 生成されたMailインスタンス
74 * @throws MailBuildException Mailインスタンスの生成に失敗した場合
75 */
76 public Mail buildMail(String classPath, VelocityContext context) throws MailBuildException {
77 String templateXmlText;
78 if (!hasTemplateCache(classPath)) {
79 Document doc = getDocumentFromClassPath(classPath);
80 XMLOutputter output = new XMLOutputter();
81 templateXmlText = output.outputString(doc);
82 putTemplateCache(classPath, templateXmlText);
83 } else {
84 templateXmlText = getTemplateCache(classPath);
85 }
86
87 try {
88 return build(templateXmlText, context);
89 } catch (Exception e) {
90 throw new MailBuildException("メールの生成に失敗しました。", e);
91 }
92 }
93
94 /***
95 * 指定されたXMLファイルからMailインスタンスを生成します。
96 *
97 * @param file メール内容を記述したXMLファイル
98 * @return 生成されたMailインスタンス
99 * @throws MailBuildException Mailインスタンスの生成に失敗した場合
100 */
101 public Mail buildMail(File file) throws MailBuildException {
102 Document doc = getDocumentFromFile(file);
103 return build(doc);
104 }
105
106 /***
107 * 指定されたXMLファイルからMailインスタンスを生成します。
108 * 指定されたVelocityContextを使って、XMLファイルの内容を動的に生成できます。
109 *
110 * @param file メール内容を記述したXMLファイル
111 * @param context VelocityContext
112 * @return 生成されたMailインスタンス
113 * @throws MailBuildException Mailインスタンスの生成に失敗した場合
114 */
115 public Mail buildMail(File file, VelocityContext context) throws MailBuildException {
116 String templateXmlText;
117 if (!hasTemplateCache(file.getAbsolutePath())) {
118 Document doc = getDocumentFromFile(file);
119 XMLOutputter output = new XMLOutputter();
120 templateXmlText = output.outputString(doc);
121 putTemplateCache(file.getAbsolutePath(), templateXmlText);
122 } else {
123 templateXmlText = getTemplateCache(file.getAbsolutePath());
124 }
125
126 try {
127 return build(templateXmlText, context);
128 } catch (Exception e) {
129 throw new MailBuildException("メールの生成に失敗しました。", e);
130 }
131 }
132
133 /***
134 * 指定されたクラスパス上のファイルを読み込んで、XMLドキュメントを生成します。
135 *
136 * @param classPath
137 * @return JDOM Document
138 */
139 protected Document getDocumentFromClassPath(String classPath) throws MailBuildException {
140 InputStream is = getClass().getResourceAsStream(classPath);
141 SAXBuilder builder = new SAXBuilder(true);
142 builder.setEntityResolver(new DTDEntityResolver());
143 Document doc;
144 try {
145 doc = builder.build(is);
146 is.close();
147 } catch (JDOMException e) {
148 throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);
149 } catch (IOException e) {
150 throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);
151 }
152 return doc;
153 }
154
155 /***
156 * 指定されたファイルを読み込んで、XMLドキュメントを生成します。
157 *
158 * @param file
159 * @return JDOM Document
160 */
161 protected Document getDocumentFromFile(File file) {
162 SAXBuilder builder = new SAXBuilder(true);
163 builder.setEntityResolver(new DTDEntityResolver());
164 Document doc;
165 try {
166 doc = builder.build(file);
167 } catch (JDOMException e) {
168 throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);
169 } catch (IOException e) {
170 throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);
171 }
172 return doc;
173 }
174
175 /***
176 * XMLドキュメントからMailインスタンスを生成します。
177 *
178 * @param doc
179 * @return Mail
180 */
181 protected Mail build(Document doc) {
182 Element root = doc.getRootElement();
183
184 Mail mail = new Mail();
185 setFrom(root, mail);
186 setRecipients(root, mail);
187 setSubject(root, mail);
188 setBody(root, mail);
189 setReplyTo(root, mail);
190 setReturnPath(root, mail);
191
192 setHtml(root, mail);
193
194 return mail;
195 }
196
197 /***
198 * VelocityContextとXMLテンプレートをマージさせ、Mailインスタンスを生成します。
199 *
200 * @param templateText マージするXMLテンプレートの文字列
201 * @param context マージするVelocityContext
202 * @return Mail
203 *
204 * @throws Exception
205 * @throws ParseErrorException
206 * @throws MethodInvocationException
207 * @throws ResourceNotFoundException
208 * @throws IOException
209 * @throws JDOMException
210 */
211 protected Mail build(String templateText, VelocityContext context) throws Exception,
212 ParseErrorException,
213 MethodInvocationException,
214 ResourceNotFoundException,
215 IOException, JDOMException {
216 if (log.isDebugEnabled()) {
217 log.debug("Source XML Mail Data\n" + templateText);
218 }
219
220 Velocity.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM, velocityLogSystem);
221 Velocity.init();
222 StringWriter w = new StringWriter();
223 Velocity.evaluate(context, w, "XML Mail Data", templateText);
224
225 if (log.isDebugEnabled()) {
226 String newXmlContent = w.toString();
227 log.debug("VelocityContext-merged XML Mail Data\n" + newXmlContent);
228 }
229
230 StringReader reader = new StringReader(w.toString());
231 SAXBuilder builder = new SAXBuilder(true);
232 builder.setEntityResolver(new DTDEntityResolver());
233 Document doc2 = builder.build(reader);
234
235 return build(doc2);
236 }
237
238 /***
239 * @param root
240 * @param mail
241 */
242 protected void setReturnPath(Element root, Mail mail) {
243 Element returnPathElem = root.getChild("returnPath");
244 if (returnPathElem != null && returnPathElem.getAttributeValue("email") != null) {
245 mail.setReturnPath(returnPathElem.getAttributeValue("email"));
246 }
247 }
248
249 /***
250 * @param root
251 * @param mail
252 */
253 protected void setReplyTo(Element root, Mail mail) {
254 Element replyToElem = root.getChild("replyTo");
255 if (replyToElem != null && replyToElem.getAttributeValue("email") != null) {
256 mail.setReplyTo(replyToElem.getAttributeValue("email"));
257 }
258 }
259
260 /***
261 * @param root
262 * @param mail
263 */
264 protected void setBody(Element root, Mail mail) {
265 Element bodyElem = root.getChild("body");
266 if (bodyElem != null) {
267 mail.setText(bodyElem.getTextTrim());
268 }
269 }
270
271 /***
272 * @param root
273 * @param mail
274 */
275 protected void setHtml(Element root, Mail mail) {
276 Element htmlElem = root.getChild("html");
277 if (htmlElem != null) {
278 mail.setHtmlText(htmlElem.getTextTrim());
279 }
280 }
281
282 /***
283 * @param root
284 * @param mail
285 */
286 protected void setSubject(Element root, Mail mail) {
287 Element subjectElem = root.getChild("subject");
288 if (subjectElem != null) {
289 mail.setSubject(subjectElem.getTextTrim());
290 }
291 }
292
293 /***
294 * @param root
295 * @param mail
296 */
297 protected void setRecipients(Element root, Mail mail) {
298 Element recipientsElem = root.getChild("recipients");
299 if (recipientsElem == null) {
300 return;
301 }
302
303 List recipientElemList = recipientsElem.getChildren();
304 for (int i = 0, max = recipientElemList.size(); i < max; i++) {
305 Element e = (Element)recipientElemList.get(i);
306 if ("to".equals(e.getName())) {
307 if (e.getAttributeValue("email") != null) {
308 if (e.getAttributeValue("name") != null) {
309 mail.addTo(e.getAttributeValue("email"), e.getAttributeValue("name"));
310 } else {
311 mail.addTo(e.getAttributeValue("email"));
312 }
313 }
314 } else if ("cc".equals(e.getName())) {
315 if (e.getAttributeValue("email") != null) {
316 if (e.getAttributeValue("name") != null) {
317 mail.addCc(e.getAttributeValue("email"), e.getAttributeValue("name"));
318 } else {
319 mail.addCc(e.getAttributeValue("email"));
320 }
321 }
322 } else {
323 if (e.getAttributeValue("email") != null) {
324 mail.addBcc(e.getAttributeValue("email"));
325 }
326 }
327 }
328 }
329
330 /***
331 * @param root
332 * @param mail
333 */
334 protected void setFrom(Element root, Mail mail) {
335 Element fromElem = root.getChild("from");
336 if (fromElem != null && fromElem.getAttributeValue("email") != null) {
337 if (fromElem.getAttributeValue("name") != null) {
338 mail.setFrom(fromElem.getAttributeValue("email"), fromElem
339 .getAttributeValue("name"));
340 } else {
341 mail.setFrom(fromElem.getAttributeValue("email"));
342 }
343 }
344 }
345
346 /***
347 * @see com.ozacc.mail.VelocityMailBuilder#clearCache()
348 */
349 public synchronized void clearCache() {
350 log.debug("テンプレートキャッシュをクリアします。");
351 templateCache.clear();
352 }
353
354 /***
355 * @see com.ozacc.mail.VelocityMailBuilder#isCacheEnabled()
356 */
357 public boolean isCacheEnabled() {
358 return cacheEnabled;
359 }
360
361 /***
362 * @see com.ozacc.mail.VelocityMailBuilder#setCacheEnabled(boolean)
363 */
364 public void setCacheEnabled(boolean cacheEnabled) {
365 if (!cacheEnabled) {
366 clearCache();
367 }
368 this.cacheEnabled = cacheEnabled;
369 }
370
371 protected boolean hasTemplateCache(String key) {
372 if (cacheEnabled) {
373 return templateCache.containsKey(key);
374 }
375 return false;
376 }
377
378 protected void putTemplateCache(String key, String templateXmlText) {
379 if (cacheEnabled) {
380 log.debug("テンプレートをキャッシュします。[key='" + key + "']");
381 templateCache.put(key, templateXmlText);
382 }
383 }
384
385 protected String getTemplateCache(String key) {
386 if (hasTemplateCache(key)) {
387 log.debug("テンプレートキャッシュを返します。[key='" + key + "']");
388 return (String)templateCache.get(key);
389 }
390 return null;
391 }
392
393 }