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