1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.struts2.components.template;
23
24 import java.io.IOException;
25 import java.io.Writer;
26 import java.util.*;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.CopyOnWriteArraySet;
29
30 import javax.servlet.ServletContext;
31 import javax.servlet.http.HttpServletRequest;
32 import javax.servlet.http.HttpServletResponse;
33
34 import org.apache.struts2.ServletActionContext;
35 import org.apache.struts2.StrutsConstants;
36 import org.apache.struts2.views.freemarker.FreemarkerManager;
37
38 import com.opensymphony.xwork2.ActionContext;
39 import com.opensymphony.xwork2.ActionInvocation;
40 import com.opensymphony.xwork2.inject.Inject;
41 import com.opensymphony.xwork2.util.ClassLoaderUtil;
42 import com.opensymphony.xwork2.util.ValueStack;
43 import com.opensymphony.xwork2.util.logging.Logger;
44 import com.opensymphony.xwork2.util.logging.LoggerFactory;
45
46 import freemarker.template.Configuration;
47 import freemarker.template.SimpleHash;
48 import freemarker.core.ParseException;
49
50 /***
51 * Freemarker based template engine.
52 */
53 public class FreemarkerTemplateEngine extends BaseTemplateEngine {
54 static Class bodyContent = null;
55 protected FreemarkerManager freemarkerManager;
56
57 protected final Map<String, freemarker.template.Template> templates = new ConcurrentHashMap<String, freemarker.template.Template>();
58 protected final Set<String> missingTemplates = new CopyOnWriteArraySet<String>();
59 protected boolean freemarkerCaching = false;
60
61 static {
62 try {
63 bodyContent = ClassLoaderUtil.loadClass("javax.servlet.jsp.tagext.BodyContent",
64 FreemarkerTemplateEngine.class);
65 } catch (ClassNotFoundException e) {
66
67
68
69
70 }
71 }
72
73 private static final Logger LOG = LoggerFactory.getLogger(FreemarkerTemplateEngine.class);
74
75 @Inject
76 public void setFreemarkerManager(FreemarkerManager mgr) {
77 this.freemarkerManager = mgr;
78 }
79
80 public void renderTemplate(TemplateRenderingContext templateContext) throws Exception {
81
82 ValueStack stack = templateContext.getStack();
83 Map context = stack.getContext();
84 ServletContext servletContext = (ServletContext) context.get(ServletActionContext.SERVLET_CONTEXT);
85 HttpServletRequest req = (HttpServletRequest) context.get(ServletActionContext.HTTP_REQUEST);
86 HttpServletResponse res = (HttpServletResponse) context.get(ServletActionContext.HTTP_RESPONSE);
87
88
89 Configuration config = freemarkerManager.getConfiguration(servletContext);
90
91
92 List<Template> templates = templateContext.getTemplate().getPossibleTemplates(this);
93
94
95 freemarker.template.Template template = null;
96 String templateName = null;
97 Exception exception = null;
98 for (Template t : templates) {
99 templateName = getFinalTemplateName(t);
100 if (freemarkerCaching) {
101 if (!isTemplateMissing(templateName)) {
102 try {
103 template = findInCache(templateName);
104 if (template == null) {
105
106 template = config.getTemplate(templateName);
107 addToCache(templateName, template);
108 }
109 break;
110 } catch (IOException e) {
111 addToMissingTemplateCache(templateName);
112 if (exception == null) {
113 exception = e;
114 }
115 }
116 }
117 } else {
118 try {
119
120 template = config.getTemplate(templateName);
121 break;
122 } catch (ParseException e) {
123
124 exception = e;
125 break;
126 } catch (IOException e) {
127
128 if (exception == null) {
129 exception = e;
130 }
131 }
132 }
133 }
134
135 if (template == null) {
136 if (LOG.isErrorEnabled()) {
137 LOG.error("Could not load the FreeMarker template named '" + templateContext.getTemplate().getName() +"':");
138 for (Template t : templates) {
139 LOG.error("Attempted: " + getFinalTemplateName(t));
140 }
141 LOG.error("The TemplateLoader provided by the FreeMarker Configuration was a: "+config.getTemplateLoader().getClass().getName());
142 }
143 if (exception != null) {
144 throw exception;
145 } else {
146 return;
147 }
148 }
149
150 if (LOG.isDebugEnabled()) {
151 LOG.debug("Rendering template " + templateName);
152 }
153
154 ActionInvocation ai = ActionContext.getContext().getActionInvocation();
155
156 Object action = (ai == null) ? null : ai.getAction();
157 SimpleHash model = freemarkerManager.buildTemplateModel(stack, action, servletContext, req, res, config.getObjectWrapper());
158
159 model.put("tag", templateContext.getTag());
160 model.put("themeProperties", getThemeProps(templateContext.getTemplate()));
161
162
163
164 Writer writer = templateContext.getWriter();
165 final Writer wrapped = writer;
166 writer = new Writer() {
167 public void write(char cbuf[], int off, int len) throws IOException {
168 wrapped.write(cbuf, off, len);
169 }
170
171 public void flush() throws IOException {
172
173 }
174
175 public void close() throws IOException {
176 wrapped.close();
177 }
178 };
179
180 try {
181 stack.push(templateContext.getTag());
182 template.process(model, writer);
183 } finally {
184 stack.pop();
185 }
186 }
187
188 protected String getSuffix() {
189 return "ftl";
190 }
191
192 protected void addToMissingTemplateCache(String templateName) {
193 missingTemplates.add(templateName);
194 }
195
196 protected boolean isTemplateMissing(String templateName) {
197 return missingTemplates.contains(templateName);
198 }
199
200 protected void addToCache(String templateName,
201 freemarker.template.Template template) {
202 templates.put(templateName, template);
203 }
204
205 protected freemarker.template.Template findInCache(String templateName) {
206 return templates.get(templateName);
207 }
208
209 /***
210 * Enables or disables Struts caching of Freemarker templates. By default disabled.
211 * Set struts.freemarker.templatesCache=true to enable cache
212 * @param cacheTemplates "true" if the template engine should cache freemarker template
213 * internally
214 */
215 @Inject(StrutsConstants.STRUTS_FREEMARKER_TEMPLATES_CACHE)
216 public void setCacheTemplates(String cacheTemplates) {
217 freemarkerCaching = "true".equals(cacheTemplates);
218 }
219
220 /***
221 * Clear the templates and missing templates cache
222 */
223 public void clearTemplatesCache() {
224 templates.clear();
225 missingTemplates.clear();
226 }
227 }