View Javadoc

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.10.2.3 2005/01/17 15:44:21 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())) { // to
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())) { // cc
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) { // bcc
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 }