OSDN Git Service

.gitignore を追加
[spring-ext/ozacc-mail.git] / src / main / java / com / ozacc / mail / impl / XMLVelocityMailBuilderImpl.java
1 package com.ozacc.mail.impl;\r
2 \r
3 import java.io.File;\r
4 import java.io.IOException;\r
5 import java.io.StringReader;\r
6 import java.io.StringWriter;\r
7 import java.util.HashMap;\r
8 import java.util.Map;\r
9 import java.util.Properties;\r
10 \r
11 import javax.xml.parsers.DocumentBuilder;\r
12 import javax.xml.transform.OutputKeys;\r
13 import javax.xml.transform.Transformer;\r
14 import javax.xml.transform.TransformerConfigurationException;\r
15 import javax.xml.transform.TransformerException;\r
16 import javax.xml.transform.TransformerFactory;\r
17 import javax.xml.transform.TransformerFactoryConfigurationError;\r
18 import javax.xml.transform.dom.DOMSource;\r
19 import javax.xml.transform.stream.StreamResult;\r
20 \r
21 import org.apache.commons.logging.Log;\r
22 import org.apache.commons.logging.LogFactory;\r
23 import org.apache.velocity.VelocityContext;\r
24 import org.apache.velocity.app.Velocity;\r
25 import org.apache.velocity.exception.MethodInvocationException;\r
26 import org.apache.velocity.exception.ParseErrorException;\r
27 import org.apache.velocity.exception.ResourceNotFoundException;\r
28 import org.apache.velocity.runtime.log.LogSystem;\r
29 import org.w3c.dom.Document;\r
30 import org.w3c.dom.Element;\r
31 import org.xml.sax.InputSource;\r
32 import org.xml.sax.SAXException;\r
33 \r
34 import com.ozacc.mail.Mail;\r
35 import com.ozacc.mail.MailBuildException;\r
36 import com.ozacc.mail.VelocityMultipleMailBuilder;\r
37 \r
38 /**\r
39  * XMLファイルを読み込み、Velocityと連携して動的にメールデータを生成し、そのデータからMailインスタンスを生成するクラス。\r
40  * \r
41  * @since 1.0.1\r
42  * @author Tomohiro Otsuka\r
43  * @version $Id: XMLVelocityMailBuilderImpl.java,v 1.4.2.4 2005/01/23 06:13:10 otsuka Exp $\r
44  */\r
45 public class XMLVelocityMailBuilderImpl extends XMLMailBuilderImpl implements\r
46                 VelocityMultipleMailBuilder {\r
47 \r
48         private static Log log = LogFactory.getLog(XMLVelocityMailBuilderImpl.class);\r
49 \r
50         private static String CACHE_KEY_SEPARATOR = "#";\r
51 \r
52         private static String DEFAULT_MAIL_ID = "DEFAULT";\r
53 \r
54         protected String charset = "UTF-8";\r
55 \r
56         protected LogSystem velocityLogSystem = new VelocityLogSystem();\r
57 \r
58         protected Map templateCache = new HashMap();\r
59 \r
60         private boolean cacheEnabled = false;\r
61 \r
62         protected boolean hasTemplateCache(String key) {\r
63                 if (cacheEnabled) {\r
64                         return templateCache.containsKey(key);\r
65                 }\r
66                 return false;\r
67         }\r
68 \r
69         protected void putTemplateCache(String key, String templateXmlText) {\r
70                 if (cacheEnabled) {\r
71                         log.debug("テンプレートをキャッシュします。[key='" + key + "']");\r
72                         templateCache.put(key, templateXmlText);\r
73                 }\r
74         }\r
75 \r
76         protected String getTemplateCache(String key) {\r
77                 if (hasTemplateCache(key)) {\r
78                         log.debug("テンプレートキャッシュを返します。[key='" + key + "']");\r
79                         return (String)templateCache.get(key);\r
80                 }\r
81                 return null;\r
82         }\r
83 \r
84         /**\r
85          * @see com.ozacc.mail.VelocityMailBuilder#clearCache()\r
86          */\r
87         public synchronized void clearCache() {\r
88                 log.debug("テンプレートキャッシュをクリアします。");\r
89                 templateCache.clear();\r
90         }\r
91 \r
92         /**\r
93          * @see com.ozacc.mail.VelocityMailBuilder#isCacheEnabled()\r
94          */\r
95         public boolean isCacheEnabled() {\r
96                 return cacheEnabled;\r
97         }\r
98 \r
99         /**\r
100          * @see com.ozacc.mail.VelocityMailBuilder#setCacheEnabled(boolean)\r
101          */\r
102         public void setCacheEnabled(boolean cacheEnabled) {\r
103                 if (!cacheEnabled) {\r
104                         clearCache();\r
105                 }\r
106                 this.cacheEnabled = cacheEnabled;\r
107         }\r
108 \r
109         /**\r
110          * @see com.ozacc.mail.VelocityMailBuilder#buildMail(java.lang.String, org.apache.velocity.VelocityContext)\r
111          */\r
112         public Mail buildMail(String classPath, VelocityContext context) throws MailBuildException {\r
113                 String cacheKey = classPath + CACHE_KEY_SEPARATOR + DEFAULT_MAIL_ID;\r
114 \r
115                 String templateXmlText;\r
116                 if (!hasTemplateCache(cacheKey)) {\r
117                         Document doc;\r
118                         try {\r
119                                 // Velocityマージ前のXMLではコメントを許可する\r
120                                 doc = getDocumentFromClassPath(classPath, false);\r
121                         } catch (SAXException e) {\r
122                                 throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);\r
123                         } catch (IOException e) {\r
124                                 throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);\r
125                         }\r
126                         templateXmlText = convertDocumentIntoString(doc.getDocumentElement());\r
127                         putTemplateCache(cacheKey, templateXmlText);\r
128                 } else {\r
129                         templateXmlText = getTemplateCache(cacheKey);\r
130                 }\r
131 \r
132                 try {\r
133                         return build(templateXmlText, context);\r
134                 } catch (Exception e) {\r
135                         throw new MailBuildException("メールの生成に失敗しました。", e);\r
136                 }\r
137         }\r
138 \r
139         /**\r
140          * @see com.ozacc.mail.VelocityMailBuilder#buildMail(java.io.File, org.apache.velocity.VelocityContext)\r
141          */\r
142         public Mail buildMail(File file, VelocityContext context) throws MailBuildException {\r
143                 String cacheKey = file.getAbsolutePath() + CACHE_KEY_SEPARATOR + DEFAULT_MAIL_ID;\r
144 \r
145                 String templateXmlText;\r
146                 if (!hasTemplateCache(cacheKey)) {\r
147                         Document doc;\r
148                         try {\r
149                                 // Velocityマージ前のXMLではコメントを許可する\r
150                                 doc = getDocumentFromFile(file, false);\r
151                         } catch (SAXException e) {\r
152                                 throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);\r
153                         } catch (IOException e) {\r
154                                 throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);\r
155                         }\r
156                         templateXmlText = convertDocumentIntoString(doc.getDocumentElement());\r
157                         putTemplateCache(cacheKey, templateXmlText);\r
158                 } else {\r
159                         templateXmlText = getTemplateCache(cacheKey);\r
160                 }\r
161 \r
162                 try {\r
163                         return build(templateXmlText, context);\r
164                 } catch (Exception e) {\r
165                         throw new MailBuildException("メールの生成に失敗しました。", e);\r
166                 }\r
167         }\r
168 \r
169         /**\r
170          * メールデータをVelocityContextとマージして生成されたXMLからMailインスタンスを生成します。\r
171          * \r
172          * @param templateXmlText メールデータのテンプレート\r
173          * @param context テンプレートにマージする内容を格納したVelocityContext\r
174          * @return VelocityContextをテンプレートにマージして生成されたXMLから生成されたMailインスタンス\r
175          * @throws TransformerFactoryConfigurationError\r
176          * @throws Exception\r
177          * @throws ParseErrorException\r
178          * @throws MethodInvocationException\r
179          * @throws ResourceNotFoundException\r
180          * @throws IOException\r
181          */\r
182         protected synchronized Mail build(String templateXmlText, VelocityContext context)\r
183                                                                                                                                                         throws TransformerFactoryConfigurationError,\r
184                                                                                                                                                         Exception,\r
185                                                                                                                                                         ParseErrorException,\r
186                                                                                                                                                         MethodInvocationException,\r
187                                                                                                                                                         ResourceNotFoundException,\r
188                                                                                                                                                         IOException {\r
189                 if (log.isDebugEnabled()) {\r
190                         log.debug("Source XML Mail Data\n" + templateXmlText);\r
191                 }\r
192 \r
193                 Velocity.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM, velocityLogSystem);\r
194                 Velocity.init();\r
195                 StringWriter w = new StringWriter();\r
196                 Velocity.evaluate(context, w, "XML Mail Data", templateXmlText);\r
197                 StringReader reader = new StringReader(w.toString());\r
198 \r
199                 DocumentBuilder db = createDocumentBuilder();\r
200                 InputSource source = new InputSource(reader);\r
201                 Document newDoc = db.parse(source);\r
202 \r
203                 if (log.isDebugEnabled()) {\r
204                         String newXmlContent = convertDocumentIntoString(newDoc.getDocumentElement());\r
205                         log.debug("VelocityContext-merged XML Mail Data\n" + newXmlContent);\r
206                 }\r
207 \r
208                 return buildMail(newDoc.getDocumentElement());\r
209         }\r
210 \r
211         /**\r
212          * 指定されたDOM Documentを文字列に変換します。\r
213          * \r
214          * @param mailElement\r
215          * @return XMLドキュメントの文字列\r
216          * @throws TransformerFactoryConfigurationError \r
217          */\r
218         protected String convertDocumentIntoString(Element mailElement)\r
219                                                                                                                                         throws TransformerFactoryConfigurationError {\r
220                 TransformerFactory tf = TransformerFactory.newInstance();\r
221                 Transformer t;\r
222                 try {\r
223                         t = tf.newTransformer();\r
224                 } catch (TransformerConfigurationException e) {\r
225                         throw new MailBuildException(e.getMessage(), e);\r
226                 }\r
227                 t.setOutputProperties(getOutputProperties());\r
228 \r
229                 DOMSource source = new DOMSource(mailElement);\r
230                 StringWriter w = new StringWriter();\r
231                 StreamResult result = new StreamResult(w);\r
232                 try {\r
233                         t.transform(source, result);\r
234                 } catch (TransformerException e) {\r
235                         throw new MailBuildException(e.getMessage(), e);\r
236                 }\r
237 \r
238                 return w.toString();\r
239         }\r
240 \r
241         /**\r
242          * 出力プロパティを生成。\r
243          * @return 出力プロパティを設定したPropertiesインスタンス\r
244          */\r
245         protected Properties getOutputProperties() {\r
246                 Properties p = new Properties();\r
247                 p.put(OutputKeys.ENCODING, charset);\r
248                 p.put(OutputKeys.DOCTYPE_PUBLIC, Mail.DOCTYPE_PUBLIC);\r
249                 p.put(OutputKeys.DOCTYPE_SYSTEM, Mail.DOCTYPE_SYSTEM);\r
250                 return p;\r
251         }\r
252 \r
253         /**\r
254          * @see com.ozacc.mail.VelocityMultipleMailBuilder#buildMail(java.lang.String, org.apache.velocity.VelocityContext, java.lang.String)\r
255          */\r
256         public Mail buildMail(String classPath, VelocityContext context, String mailId)\r
257                                                                                                                                                                         throws MailBuildException {\r
258                 if (mailId == null || "".equals(mailId)) {\r
259                         throw new IllegalArgumentException("メールIDが指定されていません。");\r
260                 }\r
261 \r
262                 String cacheKey = classPath + CACHE_KEY_SEPARATOR + mailId;\r
263 \r
264                 String templateXmlText;\r
265                 if (!hasTemplateCache(cacheKey)) {\r
266                         Document doc;\r
267                         try {\r
268                                 // Velocityマージ前のXMLではコメントを許可する\r
269                                 doc = getDocumentFromClassPath(classPath, false);\r
270                         } catch (SAXException e) {\r
271                                 throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);\r
272                         } catch (IOException e) {\r
273                                 throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);\r
274                         }\r
275                         if (Mail.DOCTYPE_PUBLIC.equals(doc.getDoctype().getPublicId())) {\r
276                                 throw new MailBuildException("指定されたクラスパスのXMLはシングルメールテンプレートです。[classPath='"\r
277                                                 + classPath + "']");\r
278                         }\r
279                         templateXmlText = getAndCacheTemplateText(doc, mailId, cacheKey);\r
280                 } else {\r
281                         templateXmlText = getTemplateCache(cacheKey);\r
282                 }\r
283 \r
284                 try {\r
285                         return build(templateXmlText, context);\r
286                 } catch (Exception e) {\r
287                         throw new MailBuildException("メールの生成に失敗しました。", e);\r
288                 }\r
289         }\r
290 \r
291         private String getAndCacheTemplateText(Document doc, String mailId, String cacheKey)\r
292                                                                                                                                                                                 throws TransformerFactoryConfigurationError {\r
293                 Element mailElem = doc.getElementById(mailId);\r
294                 if (mailElem == null) {\r
295                         throw new MailBuildException("指定されたID[" + mailId + "]のメールデータは見つかりませんでした。");\r
296                 }\r
297                 String templateXmlText = convertDocumentIntoString(mailElem);\r
298                 putTemplateCache(cacheKey, templateXmlText);\r
299                 return templateXmlText;\r
300         }\r
301 \r
302         /**\r
303          * @see com.ozacc.mail.VelocityMultipleMailBuilder#buildMail(java.io.File, org.apache.velocity.VelocityContext, java.lang.String)\r
304          */\r
305         public Mail buildMail(File file, VelocityContext context, String mailId)\r
306                                                                                                                                                         throws MailBuildException {\r
307                 if (mailId == null || "".equals(mailId)) {\r
308                         throw new IllegalArgumentException("メールIDが指定されていません。");\r
309                 }\r
310 \r
311                 String cacheKey = file.getAbsolutePath() + CACHE_KEY_SEPARATOR + mailId;\r
312 \r
313                 String templateXmlText;\r
314                 if (!hasTemplateCache(cacheKey)) {\r
315                         Document doc;\r
316                         try {\r
317                                 // Velocityマージ前のXMLではコメントを許可する\r
318                                 doc = getDocumentFromFile(file, false);\r
319                         } catch (SAXException e) {\r
320                                 throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);\r
321                         } catch (IOException e) {\r
322                                 throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);\r
323                         }\r
324                         if (Mail.DOCTYPE_PUBLIC.equals(doc.getDoctype().getPublicId())) {\r
325                                 throw new MailBuildException("指定されたファイルのXMLはシングルメールテンプレートです。[filePath='"\r
326                                                 + file.getAbsolutePath() + "']");\r
327                         }\r
328                         templateXmlText = getAndCacheTemplateText(doc, mailId, cacheKey);\r
329                 } else {\r
330                         templateXmlText = getTemplateCache(cacheKey);\r
331                 }\r
332 \r
333                 try {\r
334                         return build(templateXmlText, context);\r
335                 } catch (Exception e) {\r
336                         throw new MailBuildException("メールの生成に失敗しました。", e);\r
337                 }\r
338         }\r
339 \r
340 }