View Javadoc

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 				// Velocityマージ前のXMLではコメントを許可する
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 				// Velocityマージ前のXMLではコメントを許可する
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 }