1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.web.servlet.view.freemarker;
18
19 import java.io.FileNotFoundException;
20 import java.io.IOException;
21 import java.util.Collections;
22 import java.util.Enumeration;
23 import java.util.HashSet;
24 import java.util.Locale;
25 import java.util.Map;
26 import javax.servlet.GenericServlet;
27 import javax.servlet.ServletConfig;
28 import javax.servlet.ServletContext;
29 import javax.servlet.ServletException;
30 import javax.servlet.ServletRequest;
31 import javax.servlet.ServletResponse;
32 import javax.servlet.http.HttpServletRequest;
33 import javax.servlet.http.HttpServletResponse;
34 import javax.servlet.http.HttpSession;
35
36 import freemarker.core.ParseException;
37 import freemarker.ext.jsp.TaglibFactory;
38 import freemarker.ext.servlet.AllHttpScopesHashModel;
39 import freemarker.ext.servlet.FreemarkerServlet;
40 import freemarker.ext.servlet.HttpRequestHashModel;
41 import freemarker.ext.servlet.HttpRequestParametersHashModel;
42 import freemarker.ext.servlet.HttpSessionHashModel;
43 import freemarker.ext.servlet.ServletContextHashModel;
44 import freemarker.template.Configuration;
45 import freemarker.template.ObjectWrapper;
46 import freemarker.template.SimpleHash;
47 import freemarker.template.Template;
48 import freemarker.template.TemplateException;
49
50 import org.springframework.beans.BeansException;
51 import org.springframework.beans.factory.BeanFactoryUtils;
52 import org.springframework.beans.factory.BeanInitializationException;
53 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
54 import org.springframework.context.ApplicationContextException;
55 import org.springframework.web.servlet.support.RequestContextUtils;
56 import org.springframework.web.servlet.view.AbstractTemplateView;
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 public class FreeMarkerView extends AbstractTemplateView {
87
88 private String encoding;
89
90 private Configuration configuration;
91
92 private TaglibFactory taglibFactory;
93
94 private ServletContextHashModel servletContextHashModel;
95
96
97
98
99
100
101
102
103 public void setEncoding(String encoding) {
104 this.encoding = encoding;
105 }
106
107
108
109
110 protected String getEncoding() {
111 return this.encoding;
112 }
113
114
115
116
117
118
119
120
121
122
123 public void setConfiguration(Configuration configuration) {
124 this.configuration = configuration;
125 }
126
127
128
129
130 protected Configuration getConfiguration() {
131 return this.configuration;
132 }
133
134
135
136
137
138
139
140
141
142
143 @Override
144 protected void initServletContext(ServletContext servletContext) throws BeansException {
145 if (getConfiguration() != null) {
146 this.taglibFactory = new TaglibFactory(servletContext);
147 }
148 else {
149 FreeMarkerConfig config = autodetectConfiguration();
150 setConfiguration(config.getConfiguration());
151 this.taglibFactory = config.getTaglibFactory();
152 }
153
154 GenericServlet servlet = new GenericServletAdapter();
155 try {
156 servlet.init(new DelegatingServletConfig());
157 }
158 catch (ServletException ex) {
159 throw new BeanInitializationException("Initialization of GenericServlet adapter failed", ex);
160 }
161 this.servletContextHashModel = new ServletContextHashModel(servlet, getObjectWrapper());
162 }
163
164
165
166
167
168
169
170
171 protected FreeMarkerConfig autodetectConfiguration() throws BeansException {
172 try {
173 return BeanFactoryUtils.beanOfTypeIncludingAncestors(
174 getApplicationContext(), FreeMarkerConfig.class, true, false);
175 }
176 catch (NoSuchBeanDefinitionException ex) {
177 throw new ApplicationContextException(
178 "Must define a single FreeMarkerConfig bean in this web application context " +
179 "(may be inherited): FreeMarkerConfigurer is the usual implementation. " +
180 "This bean may be given any name.", ex);
181 }
182 }
183
184
185
186
187
188
189 @SuppressWarnings("deprecation")
190 protected ObjectWrapper getObjectWrapper() {
191 ObjectWrapper ow = getConfiguration().getObjectWrapper();
192 return (ow != null ? ow : ObjectWrapper.DEFAULT_WRAPPER);
193 }
194
195
196
197
198
199
200 @Override
201 public boolean checkResource(Locale locale) throws Exception {
202 try {
203
204 getTemplate(getUrl(), locale);
205 return true;
206 }
207 catch (FileNotFoundException ex) {
208 if (logger.isDebugEnabled()) {
209 logger.debug("No FreeMarker view found for URL: " + getUrl());
210 }
211 return false;
212 }
213 catch (ParseException ex) {
214 throw new ApplicationContextException(
215 "Failed to parse FreeMarker template for URL [" + getUrl() + "]", ex);
216 }
217 catch (IOException ex) {
218 throw new ApplicationContextException(
219 "Could not load FreeMarker template for URL [" + getUrl() + "]", ex);
220 }
221 }
222
223
224
225
226
227
228
229 @Override
230 protected void renderMergedTemplateModel(
231 Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
232
233 exposeHelpers(model, request);
234 doRender(model, request, response);
235 }
236
237
238
239
240
241
242
243
244
245
246
247 protected void exposeHelpers(Map<String, Object> model, HttpServletRequest request) throws Exception {
248 }
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273 protected void doRender(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
274
275 exposeModelAsRequestAttributes(model, request);
276
277 SimpleHash fmModel = buildTemplateModel(model, request, response);
278
279 if (logger.isDebugEnabled()) {
280 logger.debug("Rendering FreeMarker template [" + getUrl() + "] in FreeMarkerView '" + getBeanName() + "'");
281 }
282
283 Locale locale = RequestContextUtils.getLocale(request);
284 processTemplate(getTemplate(locale), fmModel, response);
285 }
286
287
288
289
290
291
292
293
294
295 protected SimpleHash buildTemplateModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) {
296 AllHttpScopesHashModel fmModel = new AllHttpScopesHashModel(getObjectWrapper(), getServletContext(), request);
297 fmModel.put(FreemarkerServlet.KEY_JSP_TAGLIBS, this.taglibFactory);
298 fmModel.put(FreemarkerServlet.KEY_APPLICATION, this.servletContextHashModel);
299 fmModel.put(FreemarkerServlet.KEY_SESSION, buildSessionModel(request, response));
300 fmModel.put(FreemarkerServlet.KEY_REQUEST, new HttpRequestHashModel(request, response, getObjectWrapper()));
301 fmModel.put(FreemarkerServlet.KEY_REQUEST_PARAMETERS, new HttpRequestParametersHashModel(request));
302 fmModel.putAll(model);
303 return fmModel;
304 }
305
306
307
308
309
310
311
312
313 private HttpSessionHashModel buildSessionModel(HttpServletRequest request, HttpServletResponse response) {
314 HttpSession session = request.getSession(false);
315 if (session != null) {
316 return new HttpSessionHashModel(session, getObjectWrapper());
317 }
318 else {
319 return new HttpSessionHashModel(null, request, response, getObjectWrapper());
320 }
321 }
322
323
324
325
326
327
328
329
330
331
332
333
334 protected Template getTemplate(Locale locale) throws IOException {
335 return getTemplate(getUrl(), locale);
336 }
337
338
339
340
341
342
343
344
345
346
347
348 protected Template getTemplate(String name, Locale locale) throws IOException {
349 return (getEncoding() != null ?
350 getConfiguration().getTemplate(name, locale, getEncoding()) :
351 getConfiguration().getTemplate(name, locale));
352 }
353
354
355
356
357
358
359
360
361
362
363
364 protected void processTemplate(Template template, SimpleHash model, HttpServletResponse response)
365 throws IOException, TemplateException {
366
367 template.process(model, response.getWriter());
368 }
369
370
371
372
373
374
375 @SuppressWarnings("serial")
376 private static class GenericServletAdapter extends GenericServlet {
377
378 @Override
379 public void service(ServletRequest servletRequest, ServletResponse servletResponse) {
380
381 }
382 }
383
384
385
386
387
388
389 private class DelegatingServletConfig implements ServletConfig {
390
391 @Override
392 public String getServletName() {
393 return FreeMarkerView.this.getBeanName();
394 }
395
396 @Override
397 public ServletContext getServletContext() {
398 return FreeMarkerView.this.getServletContext();
399 }
400
401 @Override
402 public String getInitParameter(String paramName) {
403 return null;
404 }
405
406 @Override
407 public Enumeration<String> getInitParameterNames() {
408 return Collections.enumeration(new HashSet<String>());
409 }
410 }
411
412 }