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