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