1 package org.apache.velocity.tools.view;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.InputStream;
23 import java.io.IOException;
24 import java.io.Writer;
25 import java.util.List;
26 import javax.servlet.FilterConfig;
27 import javax.servlet.ServletConfig;
28 import javax.servlet.ServletContext;
29 import javax.servlet.ServletRequest;
30 import javax.servlet.http.HttpServletRequest;
31 import javax.servlet.http.HttpServletResponse;
32 import javax.servlet.http.HttpSession;
33 import org.apache.commons.collections.ExtendedProperties;
34 import org.apache.velocity.Template;
35 import org.apache.velocity.app.VelocityEngine;
36 import org.apache.velocity.context.Context;
37 import org.apache.velocity.exception.ResourceNotFoundException;
38 import org.apache.velocity.io.VelocityWriter;
39 import org.apache.velocity.runtime.RuntimeConstants;
40 import org.apache.velocity.runtime.log.Log;
41 import org.apache.velocity.tools.generic.log.LogChuteCommonsLog;
42 import org.apache.velocity.tools.ClassUtils;
43 import org.apache.velocity.tools.Scope;
44 import org.apache.velocity.tools.Toolbox;
45 import org.apache.velocity.tools.ToolboxFactory;
46 import org.apache.velocity.tools.config.ConfigurationCleaner;
47 import org.apache.velocity.tools.config.ConfigurationUtils;
48 import org.apache.velocity.tools.config.FactoryConfiguration;
49 import org.apache.velocity.tools.view.ViewToolContext;
50 import org.apache.velocity.tools.view.context.ChainedContext;
51 import org.apache.velocity.util.SimplePool;
52
53
54
55
56
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
87
88
89
90
91 public class VelocityView extends ViewToolManager
92 {
93
94 public static final String CONTENT_TYPE_KEY = "default.contentType";
95
96
97
98
99
100 public static final String SERVLET_CONTEXT_KEY =
101 ServletContext.class.getName();
102
103
104 public static final String DEFAULT_CONTENT_TYPE = "text/html";
105
106
107 public static final String DEFAULT_OUTPUT_ENCODING = "ISO-8859-1";
108
109
110
111
112
113
114
115 public static final String TOOLS_KEY = ServletUtils.CONFIGURATION_KEY;
116 @Deprecated
117 public static final String DEPRECATED_TOOLS_KEY =
118 "org.apache.velocity.toolbox";
119
120
121
122
123
124 public static final String USER_TOOLS_PATH =
125 "/WEB-INF/tools.xml";
126 @Deprecated
127 public static final String DEPRECATED_USER_TOOLS_PATH =
128 "/WEB-INF/toolbox.xml";
129
130
131
132
133 public static final String DEFAULT_PROPERTIES_PATH =
134 "/org/apache/velocity/tools/view/velocity.properties";
135
136
137
138
139
140 public static final String PROPERTIES_KEY =
141 "org.apache.velocity.properties";
142
143
144
145
146
147 public static final String USER_PROPERTIES_PATH =
148 "/WEB-INF/velocity.properties";
149
150
151
152
153
154
155
156
157
158
159
160
161 public static final String LOAD_DEFAULTS_KEY =
162 "org.apache.velocity.tools.loadDefaults";
163
164
165
166
167
168
169 public static final String CLEAN_CONFIGURATION_KEY =
170 "org.apache.velocity.tools.cleanConfiguration";
171
172
173
174
175
176
177 public static final String USER_OVERWRITE_KEY =
178 "org.apache.velocity.tools.userCanOverwriteTools";
179
180
181
182
183
184
185 public static final String DEPRECATION_SUPPORT_MODE_KEY =
186 "org.apache.velocity.tools.deprecationSupportMode";
187
188
189 private static SimplePool writerPool = new SimplePool(40);
190 private String defaultContentType = DEFAULT_CONTENT_TYPE;
191 private boolean deprecationSupportMode = true;
192
193 public VelocityView(ServletConfig config)
194 {
195 this(new JeeServletConfig(config));
196 }
197
198 public VelocityView(FilterConfig config)
199 {
200 this(new JeeFilterConfig(config));
201 }
202
203 public VelocityView(ServletContext context)
204 {
205 this(new JeeContextConfig(context));
206 }
207
208 public VelocityView(JeeConfig config)
209 {
210
211 super(config.getServletContext(), false, false);
212
213 init(config);
214 }
215
216 @Deprecated
217 protected final void setDeprecationSupportMode(boolean support)
218 {
219 if (deprecationSupportMode != support)
220 {
221 this.deprecationSupportMode = support;
222 debug("deprecationSupportMode is now %s", (support ? "on" : "off"));
223 }
224 }
225
226
227
228
229 @Override
230 public void setVelocityEngine(VelocityEngine engine)
231 {
232 if (engine == null)
233 {
234 throw new NullPointerException("VelocityEngine cannot be null");
235 }
236 super.setVelocityEngine(engine);
237 }
238
239
240
241
242 public String getDefaultContentType()
243 {
244 return this.defaultContentType;
245 }
246
247
248
249
250 public void setDefaultContentType(String type)
251 {
252 if (!defaultContentType.equals(type))
253 {
254 this.defaultContentType = type;
255 debug("Default Content-Type was changed to %s", type);
256 }
257 }
258
259
260
261
262
263
264
265 protected String getProperty(String key, String alternate)
266 {
267 String prop = (String)velocity.getProperty(key);
268 if (prop == null || prop.length() == 0)
269 {
270 return alternate;
271 }
272 return prop;
273 }
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288 protected void init(JeeConfig config)
289 {
290
291
292 if (this.velocity == null)
293 {
294 this.velocity = new VelocityEngine();
295 }
296
297
298 String depMode = config.findInitParameter(DEPRECATION_SUPPORT_MODE_KEY);
299 if (depMode != null && depMode.equalsIgnoreCase("false"))
300 {
301 setDeprecationSupportMode(false);
302 }
303 String allowOverwrite = config.findInitParameter(USER_OVERWRITE_KEY);
304 if (allowOverwrite != null && allowOverwrite.equalsIgnoreCase("false"))
305 {
306 setUserCanOverwriteTools(false);
307 }
308
309
310 init(config, velocity);
311
312
313 configure(config, factory);
314
315
316 setEncoding(config);
317 }
318
319
320
321
322
323
324
325
326
327
328
329
330
331 protected void init(JeeConfig config, final VelocityEngine velocity)
332 {
333
334
335 LogChuteCommonsLog.setVelocityLog(getLog());
336
337
338
339 velocity.setApplicationAttribute(SERVLET_CONTEXT_KEY, this.servletContext);
340
341
342 configure(config, velocity);
343
344
345 try
346 {
347 velocity.init();
348 }
349 catch(Exception e)
350 {
351 String msg = "Could not initialize VelocityEngine";
352 getLog().error(msg, e);
353 e.printStackTrace();
354 throw new RuntimeException(msg + ": "+e, e);
355 }
356 }
357
358 protected void configure(final JeeConfig config, final VelocityEngine velocity)
359 {
360
361 ExtendedProperties defaultProperties = getProperties(DEFAULT_PROPERTIES_PATH, true);
362
363
364 try {
365 Class.forName("org.apache.velocity.tools.view.WebappUberspector");
366 } catch(Throwable t) {
367
368 List introspectors = defaultProperties.getList(VelocityEngine.UBERSPECT_CLASSNAME);
369 introspectors.remove("org.apache.velocity.tools.view.WebappUberspector");
370 defaultProperties.setProperty(VelocityEngine.UBERSPECT_CLASSNAME,introspectors);
371 }
372 velocity.setExtendedProperties(defaultProperties);
373
374
375 String appPropsPath = servletContext.getInitParameter(PROPERTIES_KEY);
376 setProps(velocity, appPropsPath, true);
377
378
379
380 setProps(velocity, USER_PROPERTIES_PATH, false);
381
382
383 String servletPropsPath = config.getInitParameter(PROPERTIES_KEY);
384 setProps(velocity, servletPropsPath, true);
385 }
386
387 private boolean setProps(VelocityEngine velocity, String path, boolean require)
388 {
389 if (path == null)
390 {
391
392 return false;
393 }
394
395
396
397
398 ExtendedProperties props = getProperties(path, require);
399 if (props == null)
400 {
401 return false;
402 }
403
404 debug("Configuring Velocity with properties at: %s", path);
405
406
407 velocity.setExtendedProperties(props);
408
409 return true;
410 }
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435 protected void configure(final JeeConfig config, final ToolboxFactory factory)
436 {
437 FactoryConfiguration factoryConfig = new FactoryConfiguration("VelocityView.configure(config,factory)");
438
439 boolean hasOldToolbox = false;
440 if (this.deprecationSupportMode)
441 {
442 FactoryConfiguration oldToolbox = getDeprecatedConfig(config);
443 if (oldToolbox != null)
444 {
445 hasOldToolbox = true;
446 factoryConfig.addConfiguration(oldToolbox);
447 }
448 }
449
450
451
452 String loadDefaults = config.findInitParameter(LOAD_DEFAULTS_KEY);
453 if ((!hasOldToolbox && loadDefaults == null) ||
454 "true".equalsIgnoreCase(loadDefaults))
455 {
456
457 getLog().trace("Loading default tools configuration...");
458 factoryConfig.addConfiguration(ConfigurationUtils.getDefaultTools());
459 }
460 else
461 {
462
463 debug("Default tools configuration has been suppressed%s",
464 (hasOldToolbox ?
465 " to avoid conflicts with older application's context and toolbox definition." :
466 "."));
467 }
468
469
470
471
472
473 FactoryConfiguration autoLoaded = ConfigurationUtils.getAutoLoaded(false);
474 factoryConfig.addConfiguration(autoLoaded);
475
476
477 String appToolsPath = servletContext.getInitParameter(TOOLS_KEY);
478 setConfig(factoryConfig, appToolsPath, true);
479
480
481
482 setConfig(factoryConfig, USER_TOOLS_PATH, false);
483
484
485 String servletToolsPath = config.getInitParameter(TOOLS_KEY);
486 setConfig(factoryConfig, servletToolsPath, true);
487
488
489 FactoryConfiguration injected = ServletUtils.getConfiguration(servletContext);
490 if (injected != null)
491 {
492 debug("Adding configuration instance in servletContext attributes as '%s'", TOOLS_KEY);
493 factoryConfig.addConfiguration(injected);
494 }
495
496
497 String cleanConfig = config.findInitParameter(CLEAN_CONFIGURATION_KEY);
498 if ("true".equals(cleanConfig))
499 {
500
501 ConfigurationCleaner cleaner = new ConfigurationCleaner();
502 cleaner.setLog(getLog());
503 cleaner.clean(factoryConfig);
504 }
505
506
507 debug("Configuring factory with: %s", factoryConfig);
508 configure(factoryConfig);
509 }
510
511
512
513
514
515
516
517
518
519 @Deprecated
520 protected FactoryConfiguration getDeprecatedConfig(JeeConfig config)
521 {
522 FactoryConfiguration toolbox = null;
523
524
525 String oldPath = config.findInitParameter(DEPRECATED_TOOLS_KEY);
526 if (oldPath != null)
527 {
528
529
530 toolbox = getConfiguration(oldPath, true);
531 }
532 else
533 {
534
535
536 oldPath = DEPRECATED_USER_TOOLS_PATH;
537 toolbox = getConfiguration(oldPath);
538 }
539
540 if (toolbox != null)
541 {
542 debug("Loaded deprecated configuration from: %s", oldPath);
543 getLog().warn("Please upgrade to new \"/WEB-INF/tools.xml\" format and conventional location."+
544 " Support for \"/WEB-INF/toolbox.xml\" format and conventional file name will "+
545 "be removed in a future version.");
546 }
547 return toolbox;
548 }
549
550 private boolean setConfig(FactoryConfiguration factory, String path, boolean require)
551 {
552 if (path == null)
553 {
554
555 return false;
556 }
557
558
559
560
561 FactoryConfiguration config = getConfiguration(path, require);
562 if (config == null)
563 {
564 return false;
565 }
566
567 debug("Loaded configuration from: %s", path);
568 factory.addConfiguration(config);
569
570
571 return true;
572 }
573
574
575 protected InputStream getInputStream(String path, boolean required)
576 {
577
578 InputStream inputStream = ServletUtils.getInputStream(path, this.servletContext);
579
580
581 if (inputStream == null)
582 {
583 String msg = "Did not find resource at: "+path;
584 if (required)
585 {
586 getLog().error(msg);
587 throw new ResourceNotFoundException(msg);
588 }
589 debug(msg);
590 return null;
591 }
592 return inputStream;
593 }
594
595
596 protected ExtendedProperties getProperties(String path)
597 {
598 return getProperties(path, false);
599 }
600
601 protected ExtendedProperties getProperties(String path, boolean required)
602 {
603 if (getLog().isTraceEnabled())
604 {
605 getLog().trace("Searching for properties at: "+path);
606 }
607
608 InputStream inputStream = getInputStream(path, required);
609 if (inputStream == null)
610 {
611 return null;
612 }
613
614 ExtendedProperties properties = new ExtendedProperties();
615 try
616 {
617 properties.load(inputStream);
618 }
619 catch (IOException ioe)
620 {
621 String msg = "Failed to load properties at: "+path;
622 getLog().error(msg, ioe);
623 if (required)
624 {
625 throw new RuntimeException(msg, ioe);
626 }
627 }
628 finally
629 {
630 try
631 {
632 if (inputStream != null)
633 {
634 inputStream.close();
635 }
636 }
637 catch (IOException ioe)
638 {
639 getLog().error("Failed to close input stream for "+path, ioe);
640 }
641 }
642 return properties;
643 }
644
645
646 protected FactoryConfiguration getConfiguration(String path)
647 {
648 return getConfiguration(path, false);
649 }
650
651 protected FactoryConfiguration getConfiguration(String path, boolean required)
652 {
653 if (getLog().isTraceEnabled())
654 {
655 getLog().trace("Searching for configuration at: "+path);
656 }
657
658 FactoryConfiguration config = null;
659 try
660 {
661 config = ServletUtils.getConfiguration(path,
662 this.servletContext,
663 this.deprecationSupportMode);
664 if (config == null)
665 {
666 String msg = "Did not find resource at: "+path;
667 if (required)
668 {
669 getLog().error(msg);
670 throw new ResourceNotFoundException(msg);
671 }
672 else
673 {
674 debug(msg);
675 }
676 }
677 }
678 catch (ResourceNotFoundException rnfe)
679 {
680
681 throw rnfe;
682 }
683 catch (RuntimeException re)
684 {
685 if (required)
686 {
687 getLog().error(re.getMessage(), re);
688 throw re;
689 }
690 getLog().debug(re.getMessage(), re);
691 }
692 return config;
693 }
694
695
696 protected void setEncoding(JeeConfig config)
697 {
698
699 this.defaultContentType =
700 getProperty(CONTENT_TYPE_KEY, DEFAULT_CONTENT_TYPE);
701
702 String encoding = getProperty(RuntimeConstants.OUTPUT_ENCODING,
703 DEFAULT_OUTPUT_ENCODING);
704
705
706
707 if (!DEFAULT_OUTPUT_ENCODING.equalsIgnoreCase(encoding))
708 {
709 int index = defaultContentType.lastIndexOf("charset");
710 if (index < 0)
711 {
712
713
714 this.defaultContentType += "; charset=" + encoding;
715 }
716 else
717 {
718
719 getLog().info("Charset was already " +
720 "specified in the Content-Type property. " +
721 "Output encoding property will be ignored.");
722 }
723 }
724
725 debug("Default Content-Type is: %s", defaultContentType);
726 }
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742 public Context render(HttpServletRequest request,
743 HttpServletResponse response) throws IOException
744 {
745
746 Context context = createContext(request, response);
747
748
749 Template template = getTemplate(request, response);
750
751
752 merge(template, context, response.getWriter());
753
754 return context;
755 }
756
757 public Context render(HttpServletRequest request, Writer out)
758 throws IOException
759 {
760
761 Context context = createContext(request, null);
762
763
764 Template template = getTemplate(request);
765
766
767 merge(template, context, out);
768
769 return context;
770 }
771
772
773
774
775
776
777
778
779
780
781
782 @Override
783 public ViewToolContext createContext(HttpServletRequest request,
784 HttpServletResponse response)
785 {
786 ViewToolContext ctx;
787 if (this.deprecationSupportMode)
788 {
789 ctx = new ChainedContext(velocity, request, response, servletContext);
790 }
791 else
792 {
793 ctx = new ViewToolContext(velocity, request, response, servletContext);
794 }
795 prepareContext(ctx, request);
796 return ctx;
797 }
798
799
800
801
802
803
804
805
806 public Template getTemplate(HttpServletRequest request)
807 {
808 return getTemplate(request, null);
809 }
810
811 public Template getTemplate(HttpServletRequest request,
812 HttpServletResponse response)
813 {
814 String path = ServletUtils.getPath(request);
815 if (response == null)
816 {
817 return getTemplate(path);
818 }
819 else
820 {
821 return getTemplate(path, response.getCharacterEncoding());
822 }
823 }
824
825
826
827
828
829
830
831
832
833
834
835
836
837 public Template getTemplate(String name)
838 {
839 return getTemplate(name, null);
840 }
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855 public Template getTemplate(String name, String encoding)
856 {
857 try
858 {
859 if (encoding == null)
860 {
861 return velocity.getTemplate(name);
862 }
863 else
864 {
865 return velocity.getTemplate(name, encoding);
866 }
867 }
868 catch (RuntimeException e)
869 {
870 throw e;
871 }
872 catch (Exception e)
873 {
874 throw new RuntimeException(e);
875 }
876 }
877
878
879
880
881
882
883
884
885
886
887 public void merge(Template template, Context context, Writer writer)
888 throws IOException
889 {
890 VelocityWriter vw = null;
891 try
892 {
893 vw = (VelocityWriter)writerPool.get();
894 if (vw == null)
895 {
896 vw = new VelocityWriter(writer, 4 * 1024, true);
897 }
898 else
899 {
900 vw.recycle(writer);
901 }
902 performMerge(template, context, vw);
903
904
905 vw.flush();
906 }
907 finally
908 {
909 if (vw != null)
910 {
911 try
912 {
913
914
915
916 vw.recycle(null);
917 writerPool.put(vw);
918 }
919 catch (Exception e)
920 {
921 getLog().error("Trouble releasing VelocityWriter: " +
922 e.getMessage(), e);
923 }
924 }
925 }
926 }
927
928
929
930
931
932
933
934
935
936
937
938
939 protected void performMerge(Template template, Context context, Writer writer)
940 throws IOException
941 {
942 template.merge(context, writer);
943 }
944
945 }