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.Iterator;
    10  import java.util.List;
    11  import java.util.Map;
    12
    13  import org.apache.commons.logging.Log;
    14  import org.apache.commons.logging.LogFactory;
    15  import org.apache.velocity.VelocityContext;
    16  import org.apache.velocity.app.Velocity;
    17  import org.apache.velocity.exception.MethodInvocationException;
    18  import org.apache.velocity.exception.ParseErrorException;
    19  import org.apache.velocity.exception.ResourceNotFoundException;
    20  import org.apache.velocity.runtime.log.LogSystem;
    21  import org.jdom.Document;
    22  import org.jdom.Element;
    23  import org.jdom.JDOMException;
    24  import org.jdom.input.SAXBuilder;
    25  import org.jdom.output.XMLOutputter;
    26
    27  import com.ozacc.mail.Mail;
    28  import com.ozacc.mail.MailBuildException;
    29  import com.ozacc.mail.MultipleMailBuilder;
    30  import com.ozacc.mail.VelocityMultipleMailBuilder;
    31
    32  /***
    33   * <a href="http://www.jdom.org/">JDOM</a>を利用してXMLファイルからMailインスタンスを生成するクラス。
    34   * <p>
    35   * ソースXMLを読み込む際に、DTDバリデーションが実行されますので妥当なXMLデータ(Valid XML Document)でなければいけません。
    36   *
    37   * @since 1.0
    38   *
    39   * @author Tomohiro Otsuka
    40   * @version $Id: JDomXMLMailBuilder.java,v 1.10.2.5 2005/02/01 20:37:49 otsuka Exp $
    41   */
    42  public class JDomXMLMailBuilder implements MultipleMailBuilder, VelocityMultipleMailBuilder {
    43
    44  	private static Log log = LogFactory.getLog(JDomXMLMailBuilder.class);
    45
    46  	private static String CACHE_KEY_SEPARATOR = "#";
    47
    48  	private static String DEFAULT_MAIL_ID = "DEFAULT";
    49
    50  	protected LogSystem velocityLogSystem = new VelocityLogSystem();
    51
    52  	private boolean cacheEnabled = false;
    53
    54  	protected Map templateCache = new HashMap();
    55
    56  	/***
    57  	 * コンストラクタ。
    58  	 */
    59  	public JDomXMLMailBuilder() {}
    60
    61  	/***
    62  	 * 指定されたクラスパス上のXMLファイルからMailインスタンスを生成します。
    63  	 *
    64  	 * @param classPath メール内容を記述したXMLファイルのパス
    65  	 * @return 生成されたMailインスタンス
    66  	 * @throws MailBuildException Mailインスタンスの生成に失敗した場合
    67  	 */
    68  	public Mail buildMail(String classPath) throws MailBuildException {
    69  		Document doc = getDocumentFromClassPath(classPath);
    70  		return build(doc.getRootElement());
    71  	}
    72
    73  	/***
    74  	 * 指定されたクラスパス上のXMLファイルからMailインスタンスを生成します。
    75  	 * 指定されたVelocityContextを使って、XMLファイルの内容を動的に生成できます。
    76  	 *
    77  	 * @param classPath メール内容を記述したXMLファイルのパス
    78  	 * @param context VelocityContext
    79  	 * @return 生成されたMailインスタンス
    80  	 * @throws MailBuildException Mailインスタンスの生成に失敗した場合
    81  	 */
    82  	public Mail buildMail(String classPath, VelocityContext context) throws MailBuildException {
    83  		String cacheKey = classPath + CACHE_KEY_SEPARATOR + DEFAULT_MAIL_ID;
    84  		String templateXmlText;
    85  		if (!hasTemplateCache(cacheKey)) {
    86  			Document doc = getDocumentFromClassPath(classPath);
    87  			templateXmlText = cacheTemplateText(doc, cacheKey);
    88  		} else {
    89  			templateXmlText = getTemplateCache(cacheKey);
    90  		}
    91  		try {
    92  			return build(templateXmlText, context);
    93  		} catch (Exception e) {
    94  			throw new MailBuildException("メールの生成に失敗しました。", e);
    95  		}
    96  	}
    97
    98  	/***
    99  	 * 指定されたXMLファイルからMailインスタンスを生成します。
    100 	 *
    101 	 * @param file メール内容を記述したXMLファイル
    102 	 * @return 生成されたMailインスタンス
    103 	 * @throws MailBuildException Mailインスタンスの生成に失敗した場合
    104 	 */
    105 	public Mail buildMail(File file) throws MailBuildException {
    106 		Document doc = getDocumentFromFile(file);
    107 		return build(doc.getRootElement());
    108 	}
    109
    110 	/***
    111 	 * 指定されたXMLファイルからMailインスタンスを生成します。
    112 	 * 指定されたVelocityContextを使って、XMLファイルの内容を動的に生成できます。
    113 	 *
    114 	 * @param file メール内容を記述したXMLファイル
    115 	 * @param context VelocityContext
    116 	 * @return 生成されたMailインスタンス
    117 	 * @throws MailBuildException Mailインスタンスの生成に失敗した場合
    118 	 */
    119 	public Mail buildMail(File file, VelocityContext context) throws MailBuildException {
    120 		String cacheKey = file.getAbsolutePath() + CACHE_KEY_SEPARATOR + DEFAULT_MAIL_ID;
    121 		String templateXmlText;
    122 		if (!hasTemplateCache(cacheKey)) {
    123 			Document doc = getDocumentFromFile(file);
    124 			templateXmlText = cacheTemplateText(doc, cacheKey);
    125 		} else {
    126 			templateXmlText = getTemplateCache(cacheKey);
    127 		}
    128 		try {
    129 			return build(templateXmlText, context);
    130 		} catch (Exception e) {
    131 			throw new MailBuildException("メールの生成に失敗しました。", e);
    132 		}
    133 	}
    134
    135 	private String cacheTemplateText(Document doc, String cacheKey) {
    136 		XMLOutputter output = new XMLOutputter();
    137 		String templateXmlText = "<!DOCTYPE mail PUBLIC \"" + Mail.DOCTYPE_PUBLIC + "\" \""
    138 				+ Mail.DOCTYPE_SYSTEM + "\">\n" + output.outputString(doc.getRootElement());
    139 		log.debug("以下のXMLデータをキャッシュします。\n" + templateXmlText);
    140 		putTemplateCache(cacheKey, templateXmlText);
    141 		return templateXmlText;
    142 	}
    143
    144 	/***
    145 	 * 指定されたクラスパス上のファイルを読み込んで、XMLドキュメントを生成します。
    146 	 *
    147 	 * @param classPath
    148 	 * @return JDOM Document
    149 	 */
    150 	protected Document getDocumentFromClassPath(String classPath) throws MailBuildException {
    151 		InputStream is = getClass().getResourceAsStream(classPath);
    152 		SAXBuilder builder = new SAXBuilder(true);
    153 		builder.setEntityResolver(new DTDEntityResolver());
    154 		Document doc;
    155 		try {
    156 			doc = builder.build(is);
    157 		} catch (JDOMException e) {
    158 			throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);
    159 		} catch (IOException e) {
    160 			throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);
    161 		} finally {
    162 			if (is != null) {
    163 				try {
    164 					is.close();
    165 				} catch (IOException e) {
    166 					// ignore
    167 				}
    168 			}
    169 		}
    170 		return doc;
    171 	}
    172
    173 	/***
    174 	 * 指定されたファイルを読み込んで、XMLドキュメントを生成します。
    175 	 *
    176 	 * @param file
    177 	 * @return JDOM Document
    178 	 */
    179 	protected Document getDocumentFromFile(File file) {
    180 		SAXBuilder builder = new SAXBuilder(true);
    181 		builder.setEntityResolver(new DTDEntityResolver());
    182 		Document doc;
    183 		try {
    184 			doc = builder.build(file);
    185 		} catch (JDOMException e) {
    186 			throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);
    187 		} catch (IOException e) {
    188 			throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);
    189 		}
    190 		return doc;
    191 	}
    192
    193 	/***
    194 	 * XMLのmailルートエレメントからMailインスタンスを生成します。
    195 	 *
    196 	 * @param mailElement mail要素を示すElementインスタンス
    197 	 * @return Mail 生成されたMail
    198 	 */
    199 	protected Mail build(Element mailElement) {
    200 		Mail mail = new Mail();
    201 		setFrom(mailElement, mail);
    202 		setRecipients(mailElement, mail);
    203 		setSubject(mailElement, mail);
    204 		setBody(mailElement, mail);
    205 		setReplyTo(mailElement, mail);
    206 		setReturnPath(mailElement, mail);
    207
    208 		setHtml(mailElement, mail);
    209
    210 		return mail;
    211 	}
    212
    213 	/***
    214 	 * VelocityContextとXMLテンプレートをマージさせ、Mailインスタンスを生成します。
    215 	 *
    216 	 * @param templateText マージするXMLテンプレートの文字列
    217 	 * @param context マージするVelocityContext
    218 	 * @return Mail
    219 	 *
    220 	 * @throws Exception
    221 	 * @throws ParseErrorException
    222 	 * @throws MethodInvocationException
    223 	 * @throws ResourceNotFoundException
    224 	 * @throws IOException
    225 	 * @throws JDOMException
    226 	 */
    227 	protected Mail build(String templateText, VelocityContext context) throws Exception,
    228 																		ParseErrorException,
    229 																		MethodInvocationException,
    230 																		ResourceNotFoundException,
    231 																		IOException, JDOMException {
    232 		if (log.isDebugEnabled()) {
    233 			log.debug("ソースXMLデータ\n" + templateText);
    234 		}
    235
    236 		Velocity.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM, velocityLogSystem);
    237 		Velocity.init();
    238 		StringWriter w = new StringWriter();
    239 		Velocity.evaluate(context, w, "XML Mail Data", templateText);
    240
    241 		if (log.isDebugEnabled()) {
    242 			log.debug("VelocityContextとマージ後のXMLデータ\n" + w.toString());
    243 		}
    244
    245 		StringReader reader = new StringReader(w.toString());
    246 		SAXBuilder builder = new SAXBuilder(true);
    247 		builder.setEntityResolver(new DTDEntityResolver());
    248 		Document mergedDoc = builder.build(reader);
    249
    250 		return build(mergedDoc.getRootElement());
    251 	}
    252
    253 	/***
    254 	 * @param root
    255 	 * @param mail
    256 	 */
    257 	protected void setReturnPath(Element root, Mail mail) {
    258 		Element returnPathElem = root.getChild("returnPath");
    259 		if (returnPathElem != null && returnPathElem.getAttributeValue("email") != null) {
    260 			mail.setReturnPath(returnPathElem.getAttributeValue("email"));
    261 		}
    262 	}
    263
    264 	/***
    265 	 * @param root
    266 	 * @param mail
    267 	 */
    268 	protected void setReplyTo(Element root, Mail mail) {
    269 		Element replyToElem = root.getChild("replyTo");
    270 		if (replyToElem != null && replyToElem.getAttributeValue("email") != null) {
    271 			mail.setReplyTo(replyToElem.getAttributeValue("email"));
    272 		}
    273 	}
    274
    275 	/***
    276 	 * @param root
    277 	 * @param mail
    278 	 */
    279 	protected void setBody(Element root, Mail mail) {
    280 		Element bodyElem = root.getChild("body");
    281 		if (bodyElem != null) {
    282 			mail.setText(bodyElem.getTextTrim());
    283 		}
    284 	}
    285
    286 	/***
    287 	 * @param root
    288 	 * @param mail
    289 	 */
    290 	protected void setHtml(Element root, Mail mail) {
    291 		Element htmlElem = root.getChild("html");
    292 		if (htmlElem != null) {
    293 			mail.setHtmlText(htmlElem.getTextTrim());
    294 		}
    295 	}
    296
    297 	/***
    298 	 * @param root
    299 	 * @param mail
    300 	 */
    301 	protected void setSubject(Element root, Mail mail) {
    302 		Element subjectElem = root.getChild("subject");
    303 		if (subjectElem != null) {
    304 			mail.setSubject(subjectElem.getTextTrim());
    305 		}
    306 	}
    307
    308 	/***
    309 	 * @param root
    310 	 * @param mail
    311 	 */
    312 	protected void setRecipients(Element root, Mail mail) {
    313 		Element recipientsElem = root.getChild("recipients");
    314 		if (recipientsElem == null) {
    315 			return;
    316 		}
    317
    318 		List recipientElemList = recipientsElem.getChildren();
    319 		for (int i = 0, max = recipientElemList.size(); i < max; i++) {
    320 			Element e = (Element)recipientElemList.get(i);
    321 			if ("to".equals(e.getName())) { // to
    322 				if (e.getAttributeValue("email") != null) {
    323 					if (e.getAttributeValue("name") != null) {
    324 						mail.addTo(e.getAttributeValue("email"), e.getAttributeValue("name"));
    325 					} else {
    326 						mail.addTo(e.getAttributeValue("email"));
    327 					}
    328 				}
    329 			} else if ("cc".equals(e.getName())) { // cc
    330 				if (e.getAttributeValue("email") != null) {
    331 					if (e.getAttributeValue("name") != null) {
    332 						mail.addCc(e.getAttributeValue("email"), e.getAttributeValue("name"));
    333 					} else {
    334 						mail.addCc(e.getAttributeValue("email"));
    335 					}
    336 				}
    337 			} else {
    338 				if (e.getAttributeValue("email") != null) { // bcc
    339 					mail.addBcc(e.getAttributeValue("email"));
    340 				}
    341 			}
    342 		}
    343 	}
    344
    345 	/***
    346 	 * @param root
    347 	 * @param mail
    348 	 */
    349 	protected void setFrom(Element root, Mail mail) {
    350 		Element fromElem = root.getChild("from");
    351 		if (fromElem != null && fromElem.getAttributeValue("email") != null) {
    352 			if (fromElem.getAttributeValue("name") != null) {
    353 				mail.setFrom(fromElem.getAttributeValue("email"), fromElem
    354 						.getAttributeValue("name"));
    355 			} else {
    356 				mail.setFrom(fromElem.getAttributeValue("email"));
    357 			}
    358 		}
    359 	}
    360
    361 	/***
    362 	 * @see com.ozacc.mail.VelocityMailBuilder#clearCache()
    363 	 */
    364 	public synchronized void clearCache() {
    365 		log.debug("テンプレートキャッシュをクリアします。");
    366 		templateCache.clear();
    367 	}
    368
    369 	/***
    370 	 * @see com.ozacc.mail.VelocityMailBuilder#isCacheEnabled()
    371 	 */
    372 	public boolean isCacheEnabled() {
    373 		return cacheEnabled;
    374 	}
    375
    376 	/***
    377 	 * @see com.ozacc.mail.VelocityMailBuilder#setCacheEnabled(boolean)
    378 	 */
    379 	public void setCacheEnabled(boolean cacheEnabled) {
    380 		if (!cacheEnabled) {
    381 			clearCache();
    382 		}
    383 		this.cacheEnabled = cacheEnabled;
    384 	}
    385
    386 	protected boolean hasTemplateCache(String key) {
    387 		if (cacheEnabled) {
    388 			return templateCache.containsKey(key);
    389 		}
    390 		return false;
    391 	}
    392
    393 	protected void putTemplateCache(String key, String templateXmlText) {
    394 		if (cacheEnabled) {
    395 			log.debug("テンプレートをキャッシュします。[key='" + key + "']");
    396 			templateCache.put(key, templateXmlText);
    397 		}
    398 	}
    399
    400 	protected String getTemplateCache(String key) {
    401 		if (hasTemplateCache(key)) {
    402 			log.debug("テンプレートキャッシュを返します。[key='" + key + "']");
    403 			return (String)templateCache.get(key);
    404 		}
    405 		return null;
    406 	}
    407
    408 	/***
    409 	 * @see com.ozacc.mail.VelocityMultipleMailBuilder#buildMail(java.lang.String, org.apache.velocity.VelocityContext, java.lang.String)
    410 	 */
    411 	public Mail buildMail(String classPath, VelocityContext context, String mailId)
    412 																					throws MailBuildException {
    413 		if (mailId == null || "".equals(mailId)) {
    414 			throw new IllegalArgumentException("メールIDが指定されていません。");
    415 		}
    416
    417 		String cacheKey = classPath + CACHE_KEY_SEPARATOR + mailId;
    418 		String templateXmlText;
    419 		if (!hasTemplateCache(cacheKey)) {
    420 			Document doc = getDocumentFromClassPath(classPath);
    421 			templateXmlText = getAndCacheTemplateText(doc, mailId, cacheKey);
    422 		} else {
    423 			templateXmlText = getTemplateCache(cacheKey);
    424 		}
    425 		try {
    426 			return build(templateXmlText, context);
    427 		} catch (Exception e) {
    428 			throw new MailBuildException("メールの生成に失敗しました。", e);
    429 		}
    430 	}
    431
    432 	private String getAndCacheTemplateText(Document doc, String mailId, String cacheKey)
    433 																						throws MailBuildException {
    434 		Element mailElem = getElementById(doc, mailId);
    435 		XMLOutputter output = new XMLOutputter();
    436 		String templateXmlText = output.outputString(mailElem);
    437
    438 		putTemplateCache(cacheKey, templateXmlText);
    439 		return templateXmlText;
    440 	}
    441
    442 	/***
    443 	 * @see com.ozacc.mail.VelocityMultipleMailBuilder#buildMail(java.io.File, org.apache.velocity.VelocityContext, java.lang.String)
    444 	 */
    445 	public Mail buildMail(File file, VelocityContext context, String mailId)
    446 																			throws MailBuildException {
    447 		if (mailId == null || "".equals(mailId)) {
    448 			throw new IllegalArgumentException("メールIDが指定されていません。");
    449 		}
    450
    451 		String cacheKey = file.getAbsolutePath() + CACHE_KEY_SEPARATOR + mailId;
    452 		String templateXmlText;
    453 		if (!hasTemplateCache(cacheKey)) {
    454 			Document doc = getDocumentFromFile(file);
    455 			templateXmlText = getAndCacheTemplateText(doc, mailId, cacheKey);
    456 		} else {
    457 			templateXmlText = getTemplateCache(cacheKey);
    458 		}
    459 		try {
    460 			return build(templateXmlText, context);
    461 		} catch (Exception e) {
    462 			throw new MailBuildException("メールの生成に失敗しました。", e);
    463 		}
    464 	}
    465
    466 	/***
    467 	 * @see com.ozacc.mail.MultipleMailBuilder#buildMail(java.lang.String, java.lang.String)
    468 	 */
    469 	public Mail buildMail(String classPath, String mailId) throws MailBuildException {
    470 		Document doc = getDocumentFromClassPath(classPath);
    471 		Element mailElem = getElementById(doc, mailId);
    472 		return build(mailElem);
    473 	}
    474
    475 	/***
    476 	 * @see com.ozacc.mail.MultipleMailBuilder#buildMail(java.io.File, java.lang.String)
    477 	 */
    478 	public Mail buildMail(File file, String mailId) throws MailBuildException {
    479 		Document doc = getDocumentFromFile(file);
    480 		Element mailElem = getElementById(doc, mailId);
    481 		return build(mailElem);
    482 	}
    483
    484 	/***
    485 	 * 指定されたXMLドキュメントの中から、指定されたid属性がセットされている要素を取得します。
    486 	 *
    487 	 * @param doc XMLドキュメント
    488 	 * @param id 抽出する要素のid属性値
    489 	 * @return XMLドキュメントで見つかったid属性を持つ要素
    490 	 */
    491 	private Element getElementById(Document doc, String id) {
    492 		Element mailsElem = doc.getRootElement(); // <mails>
    493 		List mailElemList = mailsElem.getChildren("mail");
    494 		for (Iterator itr = mailElemList.iterator(); itr.hasNext();) {
    495 			Element mailElem = (Element)itr.next();
    496 			String mailId = mailElem.getAttributeValue("id");
    497 			if (mailId.equals(id)) {
    498 				return mailElem;
    499 			}
    500 		}
    501 		throw new MailBuildException("指定されたID[" + id + "]のメールデータは見つかりませんでした。");
    502 	}
    503
    504 }