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