1 package com.ozacc.mail.impl;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.StringReader;
6 import java.io.StringWriter;
7 import java.util.HashMap;
8 import java.util.Map;
9 import java.util.Properties;
10
11 import javax.xml.parsers.DocumentBuilder;
12 import javax.xml.transform.OutputKeys;
13 import javax.xml.transform.Transformer;
14 import javax.xml.transform.TransformerConfigurationException;
15 import javax.xml.transform.TransformerException;
16 import javax.xml.transform.TransformerFactory;
17 import javax.xml.transform.TransformerFactoryConfigurationError;
18 import javax.xml.transform.dom.DOMSource;
19 import javax.xml.transform.stream.StreamResult;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.apache.velocity.VelocityContext;
24 import org.apache.velocity.app.Velocity;
25 import org.apache.velocity.exception.MethodInvocationException;
26 import org.apache.velocity.exception.ParseErrorException;
27 import org.apache.velocity.exception.ResourceNotFoundException;
28 import org.w3c.dom.Document;
29 import org.xml.sax.InputSource;
30 import org.xml.sax.SAXException;
31
32 import com.ozacc.mail.Mail;
33 import com.ozacc.mail.MailBuildException;
34 import com.ozacc.mail.VelocityMailBuilder;
35
36 /***
37 * XMLファイルを読み込み、Velocityと連携して動的にメールデータを生成し、そのデータからMailインスタンスを生成するクラス。
38 *
39 * @since 1.0.1
40 * @author Tomohiro Otsuka
41 * @version $Id: XMLVelocityMailBuilderImpl.java,v 1.10 2006/03/03 06:04:22 otsuka Exp $
42 */
43 public class XMLVelocityMailBuilderImpl extends XMLMailBuilderImpl implements VelocityMailBuilder {
44
45 private static Log log = LogFactory.getLog(XMLVelocityMailBuilderImpl.class);
46
47 protected String charset = "UTF-8";
48
49 static {
50 Velocity.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM, new VelocityLogSystem());
51 try {
52 Velocity.init();
53 } catch (Exception e) {
54 throw new MailBuildException("Velocityの初期化に失敗しました。", e);
55 }
56 }
57
58 protected Map templateCache = new HashMap();
59
60 private boolean cacheEnabled = false;
61
62 protected boolean hasTemplateCache(String key) {
63 if (cacheEnabled) {
64 return templateCache.containsKey(key);
65 }
66 return false;
67 }
68
69 protected void putTemplateCache(String key, String templateXmlText) {
70 if (cacheEnabled) {
71 log.debug("テンプレートをキャッシュします。[key='" + key + "']");
72 templateCache.put(key, templateXmlText);
73 }
74 }
75
76 protected String getTemplateCache(String key) {
77 if (hasTemplateCache(key)) {
78 log.debug("テンプレートキャッシュを返します。[key='" + key + "']");
79 return (String)templateCache.get(key);
80 }
81 return null;
82 }
83
84 /***
85 * @see com.ozacc.mail.VelocityMailBuilder#clearCache()
86 */
87 public synchronized void clearCache() {
88 log.debug("テンプレートキャッシュをクリアします。");
89 templateCache.clear();
90 }
91
92 /***
93 * @see com.ozacc.mail.VelocityMailBuilder#isCacheEnabled()
94 */
95 public boolean isCacheEnabled() {
96 return cacheEnabled;
97 }
98
99 /***
100 * @see com.ozacc.mail.VelocityMailBuilder#setCacheEnabled(boolean)
101 */
102 public void setCacheEnabled(boolean cacheEnabled) {
103 if (!cacheEnabled) {
104 clearCache();
105 }
106 this.cacheEnabled = cacheEnabled;
107 }
108
109 /***
110 * @see com.ozacc.mail.VelocityMailBuilder#buildMail(java.lang.String, org.apache.velocity.VelocityContext)
111 */
112 public Mail buildMail(String classPath, VelocityContext context) throws MailBuildException {
113 String templateXmlText;
114 if (!hasTemplateCache(classPath)) {
115 Document doc;
116 try {
117
118 doc = getDocumentFromClassPath(classPath, false);
119 } catch (SAXException e) {
120 throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);
121 } catch (IOException e) {
122 throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);
123 }
124 templateXmlText = convertDocumentIntoString(doc);
125 putTemplateCache(classPath, templateXmlText);
126 } else {
127 templateXmlText = getTemplateCache(classPath);
128 }
129
130 try {
131 return build(templateXmlText, context);
132 } catch (Exception e) {
133 throw new MailBuildException("メールの生成に失敗しました。", e);
134 }
135 }
136
137 /***
138 * @see com.ozacc.mail.VelocityMailBuilder#buildMail(java.io.File, org.apache.velocity.VelocityContext)
139 */
140 public Mail buildMail(File file, VelocityContext context) throws MailBuildException {
141 String templateXmlText;
142 if (!hasTemplateCache(file.getAbsolutePath())) {
143 Document doc;
144 try {
145
146 doc = getDocumentFromFile(file, false);
147 } catch (SAXException e) {
148 throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);
149 } catch (IOException e) {
150 throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);
151 }
152 templateXmlText = convertDocumentIntoString(doc);
153 putTemplateCache(file.getAbsolutePath(), templateXmlText);
154 } else {
155 templateXmlText = getTemplateCache(file.getAbsolutePath());
156 }
157
158 try {
159 return build(templateXmlText, context);
160 } catch (Exception e) {
161 throw new MailBuildException("メールの生成に失敗しました。", e);
162 }
163 }
164
165 /***
166 * メールデータをVelocityContextとマージして生成されたXMLからMailインスタンスを生成します。
167 *
168 * @param templateXmlText メールデータのテンプレート
169 * @param context テンプレートにマージする内容を格納したVelocityContext
170 * @return VelocityContextをテンプレートにマージして生成されたXMLから生成されたMailインスタンス
171 * @throws TransformerFactoryConfigurationError
172 * @throws Exception
173 * @throws ParseErrorException
174 * @throws MethodInvocationException
175 * @throws ResourceNotFoundException
176 * @throws IOException
177 */
178 protected Mail build(String templateXmlText, VelocityContext context)
179 throws TransformerFactoryConfigurationError,
180 Exception,
181 ParseErrorException,
182 MethodInvocationException,
183 ResourceNotFoundException,
184 IOException {
185 if (log.isDebugEnabled()) {
186 log.debug("Source XML Mail Data\n" + templateXmlText);
187 }
188
189 StringWriter w = new StringWriter();
190 Velocity.evaluate(context, w, "XML Mail Data", templateXmlText);
191 StringReader reader = new StringReader(w.toString());
192
193 DocumentBuilder db = createDocumentBuilder();
194 InputSource source = new InputSource(reader);
195 Document newDoc = db.parse(source);
196
197 if (log.isDebugEnabled()) {
198 String newXmlContent = convertDocumentIntoString(newDoc);
199 log.debug("VelocityContext-merged XML Mail Data\n" + newXmlContent);
200 }
201
202 return buildMail(newDoc);
203 }
204
205 /***
206 * 指定されたDOM Documentを文字列に変換します。
207 *
208 * @param doc
209 * @return XMLドキュメントの文字列
210 * @throws TransformerFactoryConfigurationError
211 */
212 protected String convertDocumentIntoString(Document doc)
213 throws TransformerFactoryConfigurationError {
214 TransformerFactory tf = TransformerFactory.newInstance();
215 Transformer t;
216 try {
217 t = tf.newTransformer();
218 } catch (TransformerConfigurationException e) {
219 throw new MailBuildException(e.getMessage(), e);
220 }
221 t.setOutputProperties(getOutputProperties());
222
223 DOMSource source = new DOMSource(doc);
224 StringWriter w = new StringWriter();
225 StreamResult result = new StreamResult(w);
226 try {
227 t.transform(source, result);
228 } catch (TransformerException e) {
229 throw new MailBuildException(e.getMessage(), e);
230 }
231
232 return w.toString();
233 }
234
235 /***
236 * 出力プロパティを生成。
237 * @return 出力プロパティを設定したPropertiesインスタンス
238 */
239 protected Properties getOutputProperties() {
240 Properties p = new Properties();
241 p.put(OutputKeys.ENCODING, charset);
242 p.put(OutputKeys.DOCTYPE_PUBLIC, Mail.DOCTYPE_PUBLIC);
243 p.put(OutputKeys.DOCTYPE_SYSTEM, Mail.DOCTYPE_SYSTEM);
244 return p;
245 }
246
247 }