Template loading - Apache FreeMarker Manual

Unsupported web browser - Use a modern browser to view this website!

- Home
- Manual
- Java API

- Contribute
- Report a Bug
- Download

Manual
Previous Next

# Template loading

## Template loaders

Template loaders are objects that load raw textual data based on abstract template paths like `"index.ftl"` or `"products/catalog.ftl"`. It's up to the concrete template loader if from where and how the template "files" are loaded. They could be real files inside a specified directory, or values in a data base table, or `String`-s in a Java Map, etc. When you call `cfg.getTemplate` (where `cfg` is a `Configuration` instance), FreeMarker asks the template loader (`cfg.getTemplateLoader`) to return the text for the given template path, and then FreeMarker parses that text as template. It doesn't care or even know if the template is a real file or not, and where it is physically; those details are only known by the template loader.

### Built-in template loaders

You can set up the three most common template loading mechanism in the `Configuration` using the following convenience methods:

- `void setDirectoryForTemplateLoading(File dir)`: Sets a directory on the file system from which to load templates. Template names (template paths) will be interpreted relatively to this physical directory. It won't let you load files outside this directory.

- `void setClassForTemplateLoading(Class cl, String basePackagePath)` and `void setClassLoaderForTemplateLoading(ClassLoader classLoader, String basePackagePath)`: These are for when you want to load templates via the same mechanism with which Java loads classes (from the class-path, as they used to say vaguely). This is very likely be the preferred means of loading templates for production code, as it allows you to keep everything inside the deployment `jar` files. The first parameter decides which Java `ClassLoader` will be used. The second parameter specifies the package that contains the templates, in `/`-separated format. Note that if you don't start it with `/`, it will be interpreted relatively to the package of the `Class` parameter.

- `void setServletContextForTemplateLoading(Object servletContext, String path)`: Takes the context of your Servlet-based 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). 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. Of course, you must set up a Servlet for the `*.ftl`, `*.ftlh`, `*.ftlx` uri-patterns in `WEB-INF/web.xml` for this, otherwise the client will get the raw templates as is! To avoid a such accident, many prefers storing the templates somewhere inside the `WEB-INF` directory, which is never visitable directly. 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.

If you want to use a custom `TemplateLoader` implementation, or need to set up some extra settings of a built-in template loader, you need to instantiate the `TemplateLoader` object yourself, and then call `Configuration.setTemplateLoader(TemplateLoader)`:

    WebappTemplateLoader templateLoader = new WebappTemplateLoader(servletContext, "WEB-INF/templates");
    templateLoader.setURLConnectionUsesCaches(false);
    templateLoader.setAttemptFileAccess(false);
    cfg.setTemplateLoader(templateLoader);

### 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 `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(), "/com/example/templates");

    MultiTemplateLoader mtl = new MultiTemplateLoader(new TemplateLoader[] { ftl1, ftl2, ctl });

    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 it from the `com.example.templates` Java package.

### Loading templates from other sources

If none of the built-in class loaders fit your needs, you can 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 always checking for changes can be burden for a system that processes lot of templates, there is a `Configuration` level setting called "update delay" (defaults is 5 seconds). Until this much time has elapsed since the last checking for a newer version, FreeMarker will not check again if the template was changed. If you want to see the changes without delay, set this setting to 0. Note that some template loaders won't see that a template was changed because of the underlying storage mechanism doesn't support that; for example, class-loader based template loaders may have this problem.

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`. You can also drop selected template from the cache with `removeTemplateFromCache`; this can be also utilized to force re-loading a template regardless of the "update delay" setting.

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). Depending on how smart the JVM is, using non-0 `strongSizeLimit` is maybe a safer option, as with only softly referenced items the JVM could even throw the most frequently used templates when there's a resource shortage, which then have to be re-loaded and re-parsed, burdening the system even more.

Previous Next