- JSP versus FreeMarker?
-
Why is FreeMarker so picky about
null
-s and missing variables, and what to do with it? - Why does FreeMarker print the numbers with strange formatting (as 1,000,000 or 1 000 000 instead of 1000000)?
- Why does FreeMarker print bad decimal and/or grouping separator symbol (as 3.14 instead of 3,14)?
-
Why does FreeMarker give an error when I try to print a
boolean like
${aBoolean}
, and how to fix it? -
FreeMarker can't find my templates
(
TemplateNotFoundException
orFileNotFoundException
, "Template not found" error message) - The documentation writes about feature X, but it seems that FreeMarker doesn't know that, or it behaves in a different way as documented, or a bug that was supposedly fixed is still present.
-
The
<
and>
of FreeMarker tags confuses my editor or the XML parser. What to do? -
${...}
and/or#{...}
is used in the output I have to generate a lot, and FreeMarker tries to resolve them. What to do? - What are the legal variable names?
-
How can I use variable names (macro name, parameter name)
that contain minus sign (
-
), colon (:
), dot (.
), or or other special characters? - Why do I get "java.lang.IllegalArgumentException: argument type mismatch" when I try to use X JSP custom tag?
-
How to include other resources in a way as
jsp:include
does it? -
How can I get the parameters to my
plain-Java-method/
TemplateMethodModelEx
/TemplateTransformModel
/TemplateDirectiveModel
implementation as plainjava.lang.*
/java.util.*
objects? -
Why I can't use non-string key in the
myMap[myKey]
expression? And what to do now? -
When I list the contents of a map (a hash) with
?keys
/?values
, I get thejava.util.Map
methods mixed with the real map entries. Of course, I only want to get the map entries. - How can I modify sequences (lists) and hashes (maps) in FreeMarker templates?
-
What about
null
and the FreeMarker template language? - How can I use the output of a directive (macro) in expressions (as a parameter to another directive)?
- Why do I have "?"-s in the output instead of character X?
- How to retrieve values calculated in templates after template execution done?
-
How to assign to (or
#import
into) a dynamically constructed variable name (like to name that's stored in another variable)? - Can I allow users to upload templates and what are the security implications?
- How to implement a function or macro in Java Language instead of in the template language?
- In my Servlet based application, how do I show a nice error page instead of a stack trace when error occurs during template processing?
- I'm using a visual HTML editor that mangles template tags. Will you change the template language syntax to accommodate my editor?
- 1. JSP versus FreeMarker?
-
We compare FreeMarker with the JSP 2.0 + JSTL combo here.
FreeMarker Pros:
-
FreeMarker is not tied to Servlets or networking/Web; it is just a class library to generate text output by merging a template with Java objects (the data-model). You can execute templates anywhere and anytime; no HTTP request forwarding or similar tricks needed, no Servlet environment needed at all. Because of this you can easily integrate it into any system.
-
Terser syntax. Consider this JSP (assuming
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
):Template<c:if test="${t}"> True </c:if> <c:choose> <c:when test="${n == 123}"> Do this </c:when> <c:otherwise> Do that </c:otherwise> </c:choose> <c:forEach var="i" items="${ls}"> - ${i} </c:forEach>
and the equivalent FTL:
Template<#if t> True </#if> <#if n == 123> Do this <#else> Do that </#if> <#list ls as i> - ${i} </#list>
-
Auto-escaping option to escape HTML and XML special characters printed with
${...}
. So you can just write${x}
instead of<c:out value="${x}"/>
, and most importantly, you can't accidentally forget to do escaping. -
Locale-sensitive number and date formatting by default. When you output for a human audience, all you need to do is just write
${x}
rather than<fmt:formatNumber value="${x}" />
. -
No servlet specific scopes and other highly technical things in templates (unless, of course, you expose them into the data-model deliberately). It was made for MVC from the beginning, it focuses only on the presentation.
-
You can load the templates from anywhere; from the class path, from a data-base, etc.
-
Easier to define ad-hoc macros and functions.
-
No sweeping errors under the carpet. Missing variables and
null
-s will not silently default to0
/false
/empty-string, but cause error. See more about this here... -
"Object wrapping". This lets you show the objects to templates in a customized, presentation oriented way (e.g. see here how a W3C DOM nodes can be seen by templates using this technology.)
-
Macros and functions are just variables, so they can be easily passed around as parameter values, put into the data-model, etc., just like any other values.
-
Virtually unnoticeable delay when visiting a page for the first time (or after it was changed), because no expensive compilation happens.
FreeMarker Cons:
-
Not a "standard". There are fewer tools and IDE integrations, fewer developers knows it and there's much less industry support in general. (However, most JSP tag libraries can work in FreeMarker templates with the proper setup, unless they are base on
.tag
files.) -
Its syntax doesn't follow the HTML/XML rules apart from some visual similarity, which is confusing for new users (it's the price of the terseness). JSP doesn't follow it either, but it's closer to it.
-
Since macros and functions are just variables, incorrect directive and parameter names and missing required parameters can be detected only on runtime.
-
Doesn't work with JSF. (It could work technically, but nobody has implemented that yet.)
You may read this if you are considering replacing JSP with FreeMarker in an existing application or in a legacy framework that only supports JSP: Programmer's Guide/Miscellaneous/Using FreeMarker with servlets/Using FreeMarker for "Model 2"
-
-
2.
Why is FreeMarker so picky about
null
-s and missing variables, and what to do with it? -
To recapitulate what's this entry is about: FreeMarker by default treats an attempt to access a non-existent variable or a
null
value (this two is the same for FreeMarker) as error, which aborts the template execution.First of all, you should understand the reason of being picky. Most scripting languages and template languages are rather forgiving with missing variables (and with
null
-s), and they usually treat them as empty string and/or 0 and/or logical false. This behavior has several problems:-
It potentially hides accidental mistakes, like a typo in a variable name, or when the template author refers to a variable that the programmer doesn't put into the data-model for that template, or for which the programmer uses a different name. Humans are prone to do such mistakes, while computers are not, so missing this opportunity that the template engine can show these errors is a bad business. Even if you very carefully check the output of the templates during development, it is easy to look over mistakes like
<#if hasWarnigs>print warnings here...</#if>
, which would then silently never print the warnings, since you have mistyped the variable name (have you noticed it?). Also think about maintenance, when you later modify your application; probably you will not re-check templates (many applications has hundreds of them) that carefully each time, for all possible scenarios. Unit tests typically doesn't cover web page content very good either (if you have them at all...); they mostly only check certain manually set patterns in the web page, so they will often gloss though changes that are actually bugs. But if the page fails with exception, that's something human testers will notice and unit test will notice (as the whole page will fail), and in production the maintainers will notice (assuming somebody check error logs). -
Makes dangerous assumptions. The script language or template engine knows nothing about the application domain, so when it decides the value of something that it doesn't know to be 0/false, it is a quite irresponsible and arbitrary thing. Just because it's not know what's your current balance at your bank, can we just say it's $0? Just because it is not known if a patient has penicillin allergy, can we just say he/she doesn't have it? Just consider the implications of such mistakes. Showing an error page is often better than showing incorrect information that looks good, leading to bad decisions on the user side.
Being not picky is mostly sweeping under the carpet in this case (not facing the problems), which of course most people feels more convenient, but still, we believe that in most cases being strict will save your time and increase your software quality on the long run.
On the other hand, we recognize that there are cases where you don't want FreeMarker to be that picky for good reason, and there is solution for them:
-
It's often normal that your data-model contains
null
-s or have optional variables. In such cases use these operators. If you use them too often, try to rethink your data-model, because depending on them too much won't just make the templates too verbose, but increases the probability of hiding errors and printing arbitrary incorrect output (for the reasons described earlier). -
In some application you may rather want to show an incomplete/damaged page than an error page. In this case you can use another error handler than the default one. A custom error handler can skip the problematic part, or show an error indicator there, instead of aborting the whole page rendering. Note, however, that although the error handlers don't give arbitrary default values to variables, for pages that show critical information it's maybe still better to show an error page.
-
If the pages contain parts that aren't critically important (like some side bars), another feature you may interested in is the
attempt
/recover
directives.
-
- 3. Why does FreeMarker print the numbers with strange formatting (as 1,000,000 or 1 000 000 instead of 1000000)?
-
FreeMarker uses the locale-sensitive number formatting capability of the Java platform. The default number format for your locale may uses grouping or other formatting. If you don't want that, you have to override the number format suggested by the Java platform with the
number_format
FreeMarker setting. For example:cfg.setNumberFormat("0.######"); // now it will print 1000000 // where cfg is a freemarker.template.Configuration object
Note however than humans often find it hard to read big numbers without grouping separator. So in general it is recommended to keep them, and in cases where the numbers are for ''computer audience'' (which is confused on the grouping separators), use the
c
built-in. For example:Template<a href="/shop/productdetails?id=${product.id?c}">Details...</a>
For computer audience you need
?c
anyway, as the decimal separators can also wary depending on the locale. - 4. Why does FreeMarker print bad decimal and/or grouping separator symbol (as 3.14 instead of 3,14)?
-
Different countries use different decimal/grouping separator symbols. If you see incorrect symbols, then probably your locale is not set properly. Set the default locale of the JVM or override the default locale with the
locale
FreeMarker setting. For example:cfg.setLocale(java.util.Locale.ITALY); // where cfg is a freemarker.template.Configuration object
However, sometimes you want to output a number not for human audience, but for "computer audience" (like you want to print a size in CSS), in which case you must use dot as decimal separator, regardless of the locale (language) of the page. For that use the
c
built-in, for example:Templatefont-size: ${fontSize?c}pt;
-
5.
Why does FreeMarker give an error when I try to print a
boolean like
${aBoolean}
, and how to fix it? -
${...}
meant to format values for human consumption, and unlike numbers, booleans has no commonly accepted format (true
/false
is common in computer languages, but you rarely use it outside that). The proper format depends quite much on the context, therefore, usually the template author should decide the proper format for each case, like${washable?string("yes", "no")}
,${caching?string("Enabled", "Disabled")}
,${heating?string("on", "off")}
, etc.However, there are two cases where this gets impractical:
-
When printing boolean to generate computer language output, and hence you want
true
/false
. In such case use${someBoolean?c}
(requires FreeMarker 2.3.20). If you never generate for human consumption, only for computer language output, you might want to setboolean_format
toc
(available since FreeMarker 2.3.29), and then${aBoolean}
will behave as${aBoolean?c}
.Before 2.3.20, if you really can't upgrade FreeMarker, try if
${someBoolean?string}
. By default that printstrue
/false
. Then somehow try to ensure thatboolean_format
won't be changed later by someone, breaking your output. -
When you have format most of the booleans on the same way. In this case you can set the
boolean_format
setting (Configuration.setBooleanFormat
) to reflect that, and then since FreeMarker 2.3.20 you can just write${someBoolean}
. (Note that this doesn't work fortrue
/false
though - you have to use?c
there.)
-
-
6.
FreeMarker can't find my templates
(
TemplateNotFoundException
orFileNotFoundException
, "Template not found" error message) -
First of all, you should know that FreeMarker doesn't load templates from file system paths directly. Instead, it uses a simple virtual file system that might reads non-filesystem resources (templates from inside jar-s, from inside a database table, etc.). What that virtual file is decided by a configuration setting,
Configuration.setTemplateLoader(TemplateLoader)
. Even if theTemplateLoader
your are using maps to the file system, it will have a base directory that contains all the templates, and that will be the root of your virtual file system that you can't reach out from (i.e., absolute paths will be still relative to the virtual file system root).Tips to solve the problem:
-
If you are the one who configure FreeMarker, be sure that you set a proper
TemplateLoader
. -
Otherwise see if the template-not-found error's message contains the description of the
TemplateLoader
used. If it doesn't, you are using an old FreeMarker version, so update it. GettingFileNotFoundException
instead ofTemplateNotFoundException
is also a sign of that, and so you will get less helpful error messages. (If theTemplateLoader
in the error message is likefoo.SomeTemplateLoader@64f6106c
and so doesn't show some relevant parameters, you may should ask the author to define a nicertoString()
.) -
A frequent mistake is using a
FileTemplateLoader
for a Servlet-based web application, instead of aWebappTemplateLoader
. It may works in one environment, but not in another, as the Servlet specification makes no promises about your resources being accessible as plain files, not even when thewar
file is extracted. -
Know that when you are including/importing a template from another template, if you don't start the template name with
/
, it will be interpreted relatively to the directory of the including template. The error message contains the full (resolved) name, so you should notice this there. -
Check that you aren't using
\
(backslash) instead of/
(slash). (FreeMarker 2.3.22 and later will warn you about that in the error message.) -
As a last resort, turn on debug level logging (in the logging framework that you are using) for the category
freemarker.cache
, to see more of what's going on.
-
- 7. The documentation writes about feature X, but it seems that FreeMarker doesn't know that, or it behaves in a different way as documented, or a bug that was supposedly fixed is still present.
-
Are you sure that you are using the documentation written for the same version of FreeMarker that you actually use? Especially, note that our online documentation is for the latest stable FreeMarker release. You may use an older release; update it.
Are you sure that the Java class loader finds the same
freemarker.jar
that you expect to use? Maybe there is an older version offreemarker.jar
around, which shadows the never. To check this, try to print the version number in a template with${.version}
. (If it dies with "Unknown built-in variable: version" error message, then you use a very, very old release.).If you suspect that the problem is that you have multiple
freemarker.jar
-s, the typical culprit is that some module has a Maven or Ivy dependency with the oldfreemarker
group ID, as opposed to the more modernorg.freemarker
group ID. Because of the different group ID-s these aren't seen as conflicting artifacts by Maven or Ivy, and so both version gets in. In this case you have to exclude thefreemarker
dependency.If you think that the documentation or FreeMarker is wrong, please report it using the bug tracker, or the mailing list. Thank you!
-
8.
The
<
and>
of FreeMarker tags confuses my editor or the XML parser. What to do? -
You can use
[
and]
instead of<
and>
; see more about square bracket tag syntax here. The comparison operators, like<
, also have an alternative syntax (lt
and<
in this case); see more about them here. Also, the&&
operator (which is not well-format HTML/XML) can be written as\and
or&&
since 2.3.27. -
9.
${...}
and/or#{...}
is used in the output I have to generate a lot, and FreeMarker tries to resolve them. What to do? -
You can escape them like
${'$'}{...}
, however that's impractical if you have to do that often. So starting from FreeMarker 2.3.28 you can use[=...
]
instead; see more about the square bracket interpolation syntax here. If you are going to generate JSP files or even FreeMarker templates, this is very useful. - 10. What are the legal variable names?
-
FreeMarker has no limitations regarding the characters used in variable names, nor regarding the length of the variable names, but for your convenience try to chose variable names that can be used with the simple variable reference expressions (see it here). If you have to choose a more extreme variable name, that's not a big problem either: see here.
-
11.
How can I use variable names (macro name, parameter name)
that contain minus sign (
-
), colon (:
), dot (.
), or or other special characters? -
If you have a variable with strange name like "foo-bar", FreeMarker will misunderstand what you mean if you just use it like in
${foo-bar}
. In this case, it will believe that you want to subtract the value ofbar
fromfoo
. This FAQ entry explains how to handle situations like this.First of all it should be clear that these are just syntactical problems, as otherwise FreeMarker has no limitations regarding the characters used in variable names, nor regarding the length of them.
If the special character is one of minus sign (
-
, UCS 0x2D) or dot (.
, UCS 0x2E) or colon (:
, UCS 0x3A), then all you have to do is putting a backslash (\
) before these characters, like infoo\-bar
(since FreeMarker 2.3.22). Then FreeMarker will know that you didn't mean the operator with the same symbol. This works everywhere where you specify unquoted identifiers, like for macro and function names, parameter names, and all kind of variable references in general. (Note that these escapes only work in identifiers, not in string literals.)When the special character is not one of minus sign, dot, or colon, then it gets trickier. Let's say the problematic variable name is "a+b". Then:
-
If you want to read the variable: If it's a subvariable of something, you can write
something["a+b"]
(remember,something.x
is equivalent tosomething["x"])
. If it's a top-level variable, those are accessible through the special hash variable ,.vars
, so you can write.vars["a+b"]
. Naturally, this trick works with macro and function invocations too:<@.vars["a+b"]/>
,.vars["a+b"](1, 2)
. -
If you want to create or modify the variable: All directives that let you create or modify a variable (such as
assign
,local
,global
,macro
,function
, etc.) allows the quotation of the destination variable name. For example,<#assign foo = 1>
is the same as<#assign "foo" = 1>
. So you can write things like<#assign "a+b" = 1>
and<#macro "a+b">
. -
Unfortunately, you can't use such a variable name (that contains special characters other than
-
,.
and:
) as macro parameter name.
-
- 12. Why do I get "java.lang.IllegalArgumentException: argument type mismatch" when I try to use X JSP custom tag?
-
Fist of all, update FreeMarker, because 2.3.22 and later gives a much more helpful error message, that pretty much answers the question. Anyway, the reason is as follows. On JSP pages you quote all parameter (attribute) values, it does not mater if the type of the parameter is string or boolean or number. But since custom tags are accessible in FTL templates as plain user-defined FTL directives, you have to use the FTL syntax rules inside the custom tags, not the JSP rules. Thus, according to FTL rules, you must not quote boolean and numerical parameter values, or they are interpreted as string values, and this will cause a type mismatch error when FreeMarker tries to pass the value to the custom tag that expects non-string value.
For example, the
flush
parameter to Struts Tilesinsert
tag is boolean. In JSP the correct syntax was:Template<tiles:insert page="/layout.jsp" flush="true"/> ...
but in FTL you should write:
Template<@tiles.insert page="/layout.ftl" flush=true/> ...
Also, for similar reasons, this is wrong:
Template<tiles:insert page="/layout.jsp" flush="${needFlushing}"/> ...
and you should write:
Template<tiles:insert page="/layout.jsp" flush=needFlushing/> ...
(Not
flush=${needFlushing}
!) -
13.
How to include other resources in a way as
jsp:include
does it? -
Not with
<#include ...>
, as that just includes another FreeMarker template without involving the Servlet container.Since the inclusion method you look for is Servlet-related, and pure FreeMarker is unaware of Servlets or even HTTP, it's the Web Application Framework that decides if you can do this and if so how. For example, in Struts 2 you can do this like this:
Template<@s.include value="/WEB-INF/just-an-example.jspf" />
If the FreeMarker support of the Web Application Framework is based on
freemarker.ext.servlet.FreemarkerServlet
, then you can also do this (since FreeMarker 2.3.15):Template<@include_page path="/WEB-INF/just-an-example.jspf" />
but if the Web Application Framework provides its own solution, then you may prefer that, after all it may does something special.
For more information about
include_page
read this... -
14.
How can I get the parameters to my
plain-Java-method/
TemplateMethodModelEx
/TemplateTransformModel
/TemplateDirectiveModel
implementation as plainjava.lang.*
/java.util.*
objects? -
Unfortunately, there is no simple general-purpose solution for this problem. The problem is that FreeMarker object wrapping is very flexible, which is good when you access variables from templates, but makes unwrapping on the Java side a tricky question. For example, it is possible to wrap a non-
java.util.Map
object asTemplateHashModel
(FTL hash variable). But then, it can't be unwrapped tojava.util.Map
, since there is no wrappedjava.util.Map
around at all.So what to do then? Basically there are two cases:
-
Directives and methods that are written for presentation purposes (like kind of "tools" for helping FreeMarker templates) should declare their arguments as
TemplateModel
-s and the more specific sub interfaces of that. After all, the object wrapping is about transforming the data-model to something that serves the purpose of the presentation layer, and these methods are part of the presentation layer. If you still need a plain Java type there, you may turn to theObjectWrapperAndUnwrapper
interface of the currentObjectWrapper
(can be get withEnvironment.getObjectWrapper()
). -
Methods that are not for presentation related tasks (but for business logic and like) should be implemented as plain Java methods, and should not use any FreeMarker specific classes at all, since according the MVC paradigm they must be independent of the presentation technology (FreeMarker). If such a method is called from a template, then it is the responsibility of the object wrapper to ensure the conversion of the arguments to the proper type. If you use the
DefaultObjectWrapper
or theBeansWrapper
then this will happen automatically. ForDefaultObjectWrapper
, this mechanism works much better, if you set itsincompatibleImprovements
to 2.3.22.
-
-
15.
Why I can't use non-string key in the
myMap[myKey]
expression? And what to do now? -
The "hash" type of the FreeMarker Template Language (FTL) is not the same as Java's
Map
. FTL's hash is an associative array too, but it uses string keys exclusively. This is because it was introduced for sub variables (aspassword
inuser.password
, which is the same asuser["password"]
), and variable names are strings.If you only need to list the key-value pairs of a
Map
, you can just write something like<#list myMap as k, v>${k}: ${v}</#list>
(see more about thelist directive
here). This enumerates theMap
entries, and supports non-string keys. This requires FreeMarker 2.3.25 or later. (If for some reason you can't upgrade to 2.3.25, you can use the Java API ofMap
instead, like<#list myMap?api.entrySet() as kvp>${kvp.key}: ${kvp.value}</#list>
.)If you need to do more than listing, you will have to turn to the Java API of the
Map
. You can do it like this:myMap?api.get(nonStringKey)
. However, for?api
to be enabled, you may need to configure FreeMarker a bit (see more here).Note that as Java's
Map
is particular about the exact class of the key, at least for numerical keys calculated inside the templates you will have to cast them to the proper Java type, otherwise the item will not be found. For example if you useInteger
keys in a Map, then you should write${myMap.get(numKey?int)}
. This is because of FTL's deliberately simplified type system has only a single numerical type, while Java distinguishes a lot of numerical types. Note that the casting is not needed when the key value comes directly from the data-model (i.e., you didn't modified its value with arithmetical calculations in the template), including the case when it's the return value of a method, and it was of the proper class before wrapping, because then the result of the unwrapping will be of the original type. -
16.
When I list the contents of a map (a hash) with
?keys
/?values
, I get thejava.util.Map
methods mixed with the real map entries. Of course, I only want to get the map entries. -
Certainly you are using pure
BeansWrapper
as your object wrapper (instead of the default,DefaultObjectWrapper
), or a custom subclass of it, and thesimpleMapWrapper
property of that is left tofalse
. Unfortunately, that's the default ofBeansWrapper
(for backward compatibility), so you have to explicitly set it totrue
where you instantiate it. Also, at least since 2.3.22, applications should just useDefaultObjectWrapper
(with itsincompatibleImprovements
set to at least 2.3.22 - that's especially important if you are switching from pureBeansWrapper
), which never had this problem. - 17. How can I modify sequences (lists) and hashes (maps) in FreeMarker templates?
-
First of all, you may don't want to modify the sequence/hash, just concatenate (add) two or more of them, which results in a new sequence/hash, rather than modifying an existing one. In this case use the sequence concatenation and hash concatenation operators. Also, you may use the subsequence operator instead of removing sequence items. However, be aware of the performance implications: these operations are fast, but the hashes/sequences that are the result of many subsequent applications of these operations (i.e., when you use the result of the operation as the input of yet another operation, and so on) will be slow to read.
Now if you still want to modify sequences/hashes, then read on...
The FreeMarkes Template Language doesn't support the modification of sequences/hashes. It's for displaying already calculated things, not for calculating data. Keep templates simple. But don't give it up, you will see some advices and tricks bellow.
The best is if you can divide the work between the data-model builder program and the template so that the template doesn't need to modify sequences/hashes. Maybe if you rethink your data-model, you will realize this is possible. But, seldom there are cases where you need to modify sequences/hashes for some complex but purely presentation related algorithms. It seldom happens, so think twice whether that calculation (or parts of it) rather belongs to the data-model domain than to the presentation domain. Let's assume you are sure it belongs to the presentation domain. For example, you want to display a keyword index on some very smart way, whose algorithm need you to create and write some sequence variables. Then you should do something like this (ugly situations has ugly solutions...):
Template<#assign caculatedResults = 'com.example.foo.SmartKeywordIndexHelper'?new().calculate(keywords)> <#-- some simple algorithms comes here, like: --> <ul> <#list caculatedResults as kw> <li><a href="${kw.link}">${kw.word}</a> </#list> </ul>
That is, you move out the complex part of the presentation task from the template into Java code. Note that it doesn't affect the data-model, so the presentation is still kept separated from other the other application logic. Of course the drawback is that for this the template author will need the help of a Java programmer, but for complex algorithms that's probably needed anyway.
Now, if you still say you need to modify sequences/hashes directly with the FreeMarker template, here are some solutions, but please read the warning after them:
-
You can access the Java API of a
java.util.Map
with the help of theapi
built-in, likemyMap?api.put(11, "eleven")
. You will need to get aMap
from somewhere though (an FTL hash literal like{}
won't suffice, as it's read only and doesn't supportapi
either). For example, you could expose a Java method orTemplateMethodModelEx
to the template that returns anew LinkeHashMap()
, so you can do<#assign myMap = utils.newLinkedHashMap()>
. -
You can write a
TemplateMethodModelEx
andTemplateDirectiveModel
implementation that can modify certain types of sequences/hashes. Just certain types, becauseTemplateSequenceModel
andTemplateHashModel
doesn't have methods for modification, so you will need the sequence or hash to implement some additional methods. An example of this solution can be seen in FMPP. It allows you to do things like this (pp
stores the services provided by FMPP for templates):Template<#assign a = pp.newWritableSequence()> <@pp.add seq=a value="red" />
The
pp.add
directive works only with sequences that were created withpp.newWritableSequence()
. So for example the template author can't modify a sequence that comes from the data-model with this. -
A sequence can have some methods/directives if you use a customized wrapper (so you can write something like
<@myList.append foo />
).
But beware, these solutions have a problem: The sequence concatenation, sequence slice operator (like
seq[5..10]
) and?reverse
do not copy the original sequence, just wraps it (for efficiency), so the resulting sequence will change if the original sequence is changed later (an abnormal aliasing effect). The same problem exists with the result of hash concatenation; it just wraps the two hashes, so the resulting hash will magically change if you modify the hashes you have added earlier. As a work-around, after you did the above problematic operations, either be sure you will not modify the objects that were used as input, or create a copy of the result with a method provided by the solution described in above two points (e.g. in FMPP you could do<#assign b = pp.newWritableSequence(a[5..10])>
and<#assign c = pp.newWritableHash(hashA + hashB)>
). Of course this is easy to miss... so again, rather try to build the data-model so you will not need to modify collections, or use a presentation task helper class as was shown earlier. -
-
18.
What about
null
and the FreeMarker template language? -
The FreeMarker template language doesn't know the Java language
null
at all. It doesn't havenull
keyword, and it can't test if something isnull
or not. When it technically faces with anull
, it treats it exactly as a missing variable. For example, both ifx
isnull
in the data-model and if it's not present at all,${x!'missing'}
will print "missing", you can't tell the difference. Also, if for example you want to test if a Java method has returnednull
, just write something like<#if foo.bar()??>
.You may interested in the rationale behind this. From the viewpoint of the presentation layer a
null
and non-existent thing is almost always the same. The difference between this two is usually just a technical detail, which is rather the result of implementation details than of the application logic. That you can't compare something tonull
(unlike in Java); it doesn't make sense to compare something withnull
in a template, since the template language doesn't do identity comparison (like the Java==
operator when you compare two objects) but the more common sense value comparison (like Java'sObject.equals(Object)
; that doesn't work withnull
either). And how could FreeMarker tell if something concrete equals with something that is missing and thus unknown? Or if two missing (unknown) things are equal? Of course these questions can't be answered.There is at least one problem with this
null
-unaware approach. When you call a Java method from a template, you may want to pass anull
value as argument (since the method was designed to be used in Java language, where the concept ofnull
is known). In this case you can exploit a bug of FreeMarker (that we will not fix until we provide a correct solution for passingnull
values to a method): if you specify a missing variable as the argument, then it will not cause an error, but anull
will be passed to the method instead. Likefoo.bar(nullArg)
will call thebar
method withnull
as argument, assuming that there is no variable exists with "nullArg" name. - 19. How can I use the output of a directive (macro) in expressions (as a parameter to another directive)?
-
Capture the output into a variable with the
assign
orlocal
directive. For example:Template<#assign capturedOutput><@outputSomething /></#assign> <@otherDirective someParam=capturedOutput />
- 20. Why do I have "?"-s in the output instead of character X?
-
This is because the character that you want to print can't be represented with the charset (encoding) used for the output stream, so the Java platform (not FreeMarker) substitutes the problematic character with question mark. In general you should use the same charset for the output as for the template (use the
getEncoding()
method of the template object), or which is even safer, you should always use UTF-8 charset for the output. The charset used for the output stream is not decided by FreeMarker, but by you, when you create theWriter
that you pass to theprocess
method of the template.Example: Here I use UTF-8 charset in a servlet:
... resp.setContentType("text/html; charset=utf-8"); Writer out = resp.getWriter(); ... t.process(root, out); ...
Note that the question marks (or other substitution characters) may be produced outside FreeMarker, in which case the above obviously will not help. For example a bad/missconfigured database connection or JDBC driver may bring the text already with substitution characters in it. HTML forms are another potential source of encoding problems. It's a good idea to print the numerical code of the characters of the string on various places, to see where the problem occurs first.
You can read more about charsets and FreeMarker here...
- 21. How to retrieve values calculated in templates after template execution done?
-
First of all, be sure your application is designed well: templates should display data, and almost never calculate data. If you are still sure you want to do it, read on...
When you use
<#assign x = "foo">
, then you do not actually modify the data-model (since that is read-only, see: Programmer's Guide/Miscellaneous/Multithreading), but create thex
variable in the runtime environment of the processing (see Programmer's Guide/Miscellaneous/Variables, scopes). The problem is that this runtime environment will be discarded whenTemplate.process
returns, as it was created for a singleTemplate.process
call:// internally an Environment will be created, and then discarded myTemplate.process(root, out);
To prevent this, you can do the below, which is equivalent with the above, except that you have chance to return the variables created in the template:
Environment env = myTemplate.createProcessingEnvironment(root, out); env.process(); // process the template TemplateModel x = env.getVariable("x"); // get variable x
-
22.
How to assign to (or
#import
into) a dynamically constructed variable name (like to name that's stored in another variable)? -
If you really can't avoid doing that (you should, as it's confusing), you can solve that with constructing the appropriate FTL source code dynamically in a string, then using the
interpret
built-in. For example, if you want to assign to the variable whose name is stored in thevarName
variable:Template<@"<#assign ${varName}='example'>"?interpret />
- 23. Can I allow users to upload templates and what are the security implications?
-
In general you shouldn't allow that, unless those users are application developers, system administrators, or other highly trusted personnel. Consider templates as part of the source code just like
*.java
files are. If you still want to allow untrusted users to upload templates, here's what to consider:-
Data-model and wrapping (
Configuration.setObjectWrapper
): The data-model might gives access to the public Java API of some objects that you have put into the data-model. By default, for objects that aren't instances of the bunch of specially handled types (String
,Number
,Boolean
,Date
,Map
,List
, array, and a few others), their public Java API will be exposed, including most methods inherited from standard Java classes (getClass()
, etc.). To avoid that, you have to construct the data-model so that it only exposes the members that are really necessary for the template. One possibility is usingSimpleObjectWrapper
(viaConfiguration.setObjectWrapper
or theobject_wrapper
setting) and then create the data-model purely fromMap
-s,List
-s,Array
-s,String
-s,Number
-s,Boolean
-s andDate
-s. But for many applications that's too restrictive, and instead you have to create aWhitelistMemberAccessPolicy
, and create aDefaultObjectWrapper
(or otherBeansWrapper
subclass that you would use) that uses that. See the Java API documentation ofWhitelistMemberAccessPolicy
for more. (Or, you can roll your ownMemberAccessPolicy
implementation, or even your own restrictiveObjectWrapper
implementation of course.)Always expect that templates may get some objects that you haven't put into the data-model yourself. Notably, templates can always get a
Locale
object with the.locale_object
expression. Or the web application framework you are using may exposes some objects, like attributes from the Servlet scopes. Such objects will be still wrapped with theObjectWrapper
that you set in theConfiguration
, and this is why it's important to ensure safety on that level. Controlling what objects the template will have access to is hard, but you can control centrally what members of any object they have access to.If you are creating
TemplateModel
-s in custom code (instead of theObjectWrapper
creating those), be sure that you avoid deprecated container constructors likenew SimpleSequence()
, as those will use the also deprecated default object wrapper instance, which doesn't have the same restrictions than theObjectWrapper
you have set on theConfiguration
.Also, don't forget about the
?api
built-in, if you have enabled it (it's disabled by default). While the Java API ofMap
-s,List
-s and similar container-like objects is not directly exposed by mostObjectWrapper
-s, sosomeMap.someJavaMethod()
won't work, using?api
the template author can still get to the Java API-s of these objects, likesomeMap?api.someJavaMethod()
. But note that theObjectWrapper
is still in control, as it decides what objects support?api
, and what will?api
expose for them (it usually exposes the same as for a generic POJO). Members not allowed by theMemberAccessPolicy
also won't be visible with?api
(assuming you are using a well behavingObjectWrapper
, likeDefaultObjectWrapper
.)If you are using the default object wrapper class (
freemarker.template.DefaultObjectWrapper
), or a subclass of it, you should disable the XML (DOM) wrapping feature of it, by setting itsDOMNodeSupport
property tofalse
. The problem with the XML wrapping feature, which wrapsorg.w3c.dom.Node
objects on special way to make them easier to work with in templates, is that this facility by design lets template authors evaluate arbitrary XPath expressions, and XPath can do too much in certain setups. If you really need the XML wrapping facility, review carefully what XPath expressions are possible in your setup. Also, be sure you don't use the long deprecated, and more dangerousfreemarker.ext.xml
package, onlyfreemarker.ext.dom
. Also, note that when using the XML wrapping feature, not allowingorg.w3c.dom.Node
methods in theMemberAccessPolicy
has no effect, since it doesn't expose JavaNode
members to templates directly.Last not least, some maybe aware of the historical legacy that standard object wrappers filter out some well known "unsafe" methods, like
System.exit
. Do not ever rely on that, since it only blocks the methods from a small predefined list. The standard Java API is huge and ever growing, and then there are the 3rd party libraries, and the API-s of your own application. Clearly it's impossible to blacklist all the problematic members in those. -
Template-loader (
Configuration.setTemplateLoader
): Templates may load other templates by name (by path), like<#include "../secret.txt">
. To avoid loading sensitive data, you have to use aTemplateLoader
that double-checks that the file to load is something that should be exposed. FreeMarker tries to prevent the loading of files outside the template root directory regardless of template loader, but depending on the underlying storage mechanism, exploits may exist that FreeMarker can't consider (like, just as an example,~
jumps to the current user's home directory). Note thatfreemarker.cache.FileTemplateLoader
checks the canonical paths, so that's maybe a good candidate for this task, yet, adding a file extension check (file must be*.ftl
) is maybe a good idea. -
The
new
built-in (Configuration.setNewBuiltinClassResolver
,Environment.setNewBuiltinClassResolver
): It's used in templates like"com.example.SomeClass"?new()
, and is important for FTL libraries that are partially implemented in Java, but shouldn't be needed in normal templates. Whilenew
will not instantiate classes that are notTemplateModel
-s, FreeMarker contains aTemplateModel
class that can be used to create arbitrary Java objects. Other "dangerous"TemplateModel
-s can exist in you class-path. Plus, even if a class doesn't implementTemplateModel
, its static initialization will be run. To avoid these, you should use aTemplateClassResolver
that restricts the accessible classes to the absolute minimum (possibly based on which template asks for them), such asTemplateClassResolver.ALLOWS_NOTHING_RESOLVER
. Do not useTemplateClassResolver.SAFER_RESOLVER
, it's not restrictive enough for this purpose! Note that if, and only if yourObjectWrapper
is aBeansWrapper
or a subclass of it (typicallyDefaultObjectWrapper
), constructors not allowed by theMemberAccessPolicy
also won't be accessible for?new
. -
Denial-of-Service (DoS) attacks: It's trivial to create templates that run practically forever (with a loop), or exhaust memory (by concatenating to a string in a loop). FreeMarker can't enforce CPU or memory usage limits, so this is something that has no solution on the FreeMarker-level.
-
- 24. How to implement a function or macro in Java Language instead of in the template language?
-
It's not possible (yet), but something very similar is possible if you write a class that implements
freemarker.template.TemplateMethodModelEx
orfreemarker.template.TemplateDirectiveModel
respectively, and then where you were write<#function my ...>...</#function>
or<#macro my ...>...</#macro>
you write<#assign my = "your.package.YourClass "?
new
()>
instead. Note that using theassign
directive for this works because functions (and methods) and macros are just plain variables in FreeMarker. (For the same reason you could also putTemplateMethodModelEx
orTemplateDirectiveModel
instances into the data-model before calling the template, or into the shared variable map (see:freemarker.template.Configuration.setSharedVariable(String, TemplateModel)
) when you initialize the application.) - 25. In my Servlet based application, how do I show a nice error page instead of a stack trace when error occurs during template processing?
-
First of all, use
RETHROW_HANDLER
instead of the defaultDEBUG_HANDLER
(for more information about template exception handlers read this...). Now FreeMarker will not print anything to the output when an error occurs, so the control is in your hands. After you have caught the exception ofTemplate.process(...)
basically you can follow two strategies:-
Call
httpResp.isCommitted()
, and if that returnsfalse
, then you callhttpResp.reset()
and print a "nice error page" for the visitor. If the return value wastrue
, then try to finish the page be printing something that makes clear for the visitor that the page generation was abruptly interrupted because of an error on the Web server. You may have to print a lot of redundant HTML end-tags and set colors and font size to ensure that the error message will be actually readable in the browser window (check the source code of theHTML_DEBUG_HANDLER
insrc\freemarker\template\TemplateException.java
to see an example). -
Use full page buffering. This means that the
Writer
doesn't send the output to the client progressively, but buffers the whole page in the memory. Since you provide theWriter
instance for theTemplate.process(...)
method, this is your responsibility, FreeMarker has nothing to do with it. For example, you may use aStringWriter
, and ifTemplate.process(...)
returns by throwing an exception, then ignore the content accumulated by theStringWriter
, and send an error page instead, otherwise you print the content ofStringWriter
to the output. With this method you surely don't have to deal with partially sent pages, but it can have negative performance implications depending on the characteristic of the pages (for example, the user will experience more response delay for a long page that is generated slowly, also the server will consume more RAM). Note that using aStringWriter
is surely not the most efficient solution, as it often reallocates its buffer as the accumulated content grows.
-
- 26. I'm using a visual HTML editor that mangles template tags. Will you change the template language syntax to accommodate my editor?
-
We won't change the standard version, because a lot of templates depend on it.
Our view is that the editors that break template code are themselves broken. A good editor should ignore, not mangle, what it doesn't understand.
You maybe interested in that starting from FreeMarker 2.3.4 you can use
[
and]
instead of<
and>
. For more details read this...