Template loaders
Template loaders are objects that load raw textual data based
on abstract template paths like "index.ftl"
or
"products/catalog.ftl"
. It is up to the concrete
template loader object what source does it use to fetch the
requested data (files in a directory, data base, etc.). When you
call cfg.getTemplate
(where
cfg
is a Configuration
instance), FreeMarker ask the template loader you have set up for
the cfg
to return the text for the given template
path, and then FreeMarker parses that text as template.
Built-in template loaders
You can set up three template loading methods in the
Configuration
using the following convenience
methods. (Each method will create a template loader object
internally and set up the Configuration
instance to use that.)
void setDirectoryForTemplateLoading(File dir);
or
void setClassForTemplateLoading(Class cl, String prefix);
or
void setServletContextForTemplateLoading(Object servletContext, String path);
The first method above sets an explicit directory on the
file system from which to load templates. Needless to say perhaps,
the File
parameter must be an existing
directory. Otherwise, an exception will be thrown.
The second call takes a Class
as a
parameter and a prefix. This is for when you want to load
templates via the same mechanism that a java
ClassLoader
uses to load classes. This means
that the class you pass in will be used to call
Class.getResource()
to find the templates. The
prefix
parameter is prepended to the name of
the template. The classloading mechanism will very likely be the
preferred means of loading templates for production code, since
loading from the classpath mechanism is usually more foolproof
than specifying an explicit directory location on the file system.
It is also nicer in a final application to keep everything in a
.jar
file that the user can simply execute
directly and have all the icons and text and everything else
inside the .jar
file.
The third call takes the context of your web application,
and a base path, which is interpreted relative to the web
application root directory (that's the parent of the
WEB-INF
directory). This loader will load the
templates from the web application directory. Note that we refer
to "directory" here although this loading method works even for
unpacked .war
files since it uses
ServletContext.getResource()
to access the
templates. If you omit the second parameter (or use
""
), you can simply store the static files
(.html
, .jpg
, etc.) mixed
with the .ftl
files, just
.ftl
files will be sent to the client
processed. Of course, you must set up a Servlet for the
*.ftl
uri-pattern in
WEB-INF/web.xml
for this, otherwise the client
will get the templates as is, and thus may see confidential
content! You should not use empty path if this is a problem for
your site, rather you should store the templates somewhere inside
the WEB-INF
directory, so the raw templates are
never served accidentally. This mechanism will very likely be the
preferred means of loading templates for servlet applications,
since the templates can be updated without restarting the web
application, while this often doesn't work with the class-loader
mechanism.
Loading templates from multiple locations
If you need to load templates from multiple locations, you
have to instantiate the template loader objects for every
location, wrap them into a special template loader named
MultiTemplateLoader
and finally pass that
loader to the setTemplateLoader(TemplateLoader
loader)
method of Configuration
.
Here's an example for loading templates from two distinct
directories and with the class-loader:
import freemarker.cache.*; // template loaders live in this package ... FileTemplateLoader ftl1 = new FileTemplateLoader(new File("/tmp/templates")); FileTemplateLoader ftl2 = new FileTemplateLoader(new File("/usr/data/templates")); ClassTemplateLoader ctl = new ClassTemplateLoader(getClass(), ""); TemplateLoader[] loaders = new TemplateLoader[] { ftl1, ftl2, ctl }; MultiTemplateLoader mtl = new MultiTemplateLoader(loaders); cfg.setTemplateLoader(mtl);
Now FreeMarker will try to load templates from
/tmp/templates
directory, and if it does not
find the requested template there, it will try to load that from
/usr/data/templates
, and if it still does not
find the requested template, then it tries to load that with the
class-loader.
Loading templates from other sources
If none of the built-in class loaders are good for you, you
will have to write your own class that implements the
freemarker.cache.TemplateLoader
interface and
pass it to the setTemplateLoader(TemplateLoader
loader)
method of Configuration
.
Please read the API JavaDoc for more information.
If your template source accesses the templates through an
URL, you needn't implement a TemplateLoader
from scratch; you can choose to subclass
freemarker.cache.URLTemplateLoader
instead and
just implement the URL getURL(String
templateName)
method.
The template name (template path)
It is up to the template loader how it interprets template
names (also known as template paths). But to work together with
other components there are restrictions regarding the format of
the path. In general, it is strongly recommended that template
loaders use URL-style paths. The path must not use
/
(path step separator) character, nor the
.
(same-directory) and ..
(parent directory) path steps with other meaning than they have in
URL paths (or in UN*X paths). The *
(asterisk)
step is also reserved, and used for "template
acquisition" feature of FreeMarker.
://
(or with
template_name_format
setting set to
DEFAULT_2_4_0
, the :
(colon)
character) is reserved for specifying a scheme part, similarly as
it works with URI-s. For example
someModule://foo/bar.ftl
uses the
someModule
, or assuming the
DEFAULT_2_4_0
format,
classpath:foo/bar.ftl
uses the
classpath
scheme. Interpreting the scheme part
is completely up to the TemplateLoader
. (The
FreeMarker core is only aware of the idea of schemes because
otherwise it couldn't resolve relative template names
properly.)
FreeMarker always normalizes the paths before passing them
to the TemplateLoader
, so the paths don't
contain /../
or such, and are relative to the
imaginary template root directory (that is, they don't start with
/
). They don't contain the *
step either, as template acquisition happens in an earlier stage.
Furthermore, with template_name_format
setting
set to DEFAULT_2_4_0
, multiple consecutive
/
-s will be normalized to a single
/
(unless they are part of the
://
scheme separator).
Note that FreeMarker template path should always uses slash (not backslash) regardless of the host OS.
Template caching
FreeMarker caches templates (assuming you use the
Configuration
methods to create
Template
objects). This means that when you call
getTemplate
, FreeMarker not only returns the
resulting Template
object, but stores it in a
cache, so when next time you call getTemplate
with the same (or equivalent) path, it just returns the cached
Template
instance, and will not load and parse
the template file again.
If you change the template file, then FreeMarker will re-load
and re-parse the template automatically when you get the template
next time. However, since checking if the file has been changed can
be time consuming, there is a Configuration
level
setting called ``update delay''. This is the time that must elapse
since the last checking for a newer version of a certain template
before FreeMarker will check that again. This is set to 5 seconds by
default. If you want to see the changes of templates immediately,
set it to 0. Note that some template loaders may have problems with
template updating. For example, class-loader based template loaders
typically do not notice that you have changed the template
file.
A template will be removed from the cache if you call
getTemplate
and FreeMarker realizes that the
template file has been removed meanwhile. Also, if the JVM thinks
that it begins to run out of memory, by default it can arbitrarily
drop templates from the cache. Furthermore, you can empty the cache
manually with the clearTemplateCache
method of
Configuration
.
The actual strategy of when a cached template should be thrown
away is pluggable with the cache_storage
setting,
by which you can plug any CacheStorage
implementation. For most users
freemarker.cache.MruCacheStorage
will be
sufficient. This cache storage implements a two-level Most Recently
Used cache. In the first level, items are strongly referenced up to
the specified maximum (strongly referenced items can't be dropped by
the JVM, as opposed to softly referenced items). When the maximum is
exceeded, the least recently used item is moved into the second
level cache, where they are softly referenced, up to another
specified maximum. The size of the strong and soft parts can be
specified with the constructor. For example, set the size of the
strong part to 20, and the size of soft part to 250:
cfg.setCacheStorage(new freemarker.cache.MruCacheStorage(20, 250))
Or, since MruCacheStorage
is the default
cache storage implementation:
cfg.setSetting(Configuration.CACHE_STORAGE_KEY, "strong:20, soft:250");
When you create a new Configuration
object,
initially it uses an MruCacheStorage
where
strongSizeLimit
is 0, and
softSizeLimit
is
Integer.MAX_VALUE
(that is, in practice,
infinite). But using non-0 strongSizeLimit
is
maybe a better strategy for high load servers, since it seems that,
with only softly referenced items, JVM tends to cause just higher
resource consumption if the resource consumption was already high,
because it constantly throws frequently used templates from the
cache, which then have to be re-loaded and re-parsed.