freemarker.cache.TemplateLoader Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package freemarker.cache;
import java.io.IOException;
import java.io.Reader;
import freemarker.template.Configuration;
import freemarker.template.TemplateNotFoundException;
/**
* FreeMarker loads template "files" through objects that implement this interface,
* thus the templates need not be real files, and can come from any kind of data source
* (like classpath, servlet context, database, etc). While FreeMarker provides a few
* {@link TemplateLoader} implementations out-of-the-box, it's normal for embedding
* frameworks to use their own implementations.
*
* To set the {@link TemplateLoader} used by FreeMaker, use
* {@link Configuration#setTemplateLoader(TemplateLoader)}.
*
*
Implementations of this interface should be thread-safe.
*
*
Implementations should override {@link Object#toString()} to show information about from where the
* {@link TemplateLoader} loads the templates. For example, for a template loader that loads template from database
* table {@code toString} could return something like
* {@code "MyDatabaseTemplateLoader(user=\"cms\", table=\"mail_templates\")"}. This string will be shown in
* {@link TemplateNotFoundException} exception messages, next to the template name.
*
*
For those who has to dig deeper, note that the {@link TemplateLoader} is actually stored inside
* the {@link TemplateCache} of the {@link Configuration}, and is normally only accessed directly
* by the {@link TemplateCache}, and templates are get via the {@link TemplateCache} API-s.
*/
public interface TemplateLoader {
/**
* Finds the template in the backing storage and returns an object that identifies the storage location where the
* template can be loaded from. See the return value for more information.
*
* @param name
* The name (template root directory relative path) of the template, already localized and normalized by
* the {@link freemarker.cache.TemplateCache cache}. It is completely up to the loader implementation to
* interpret the name, however it should expect to receive hierarchical paths where path components are
* separated by a slash (not backslash). Backslashes (or any other OS specific separator character) are
* not considered as separators by FreeMarker, and thus they will not be replaced with slash before
* passing to this method, so it's up to the template loader to handle them (say, by throwing an
* exception that tells the user that the path (s)he has entered is invalid, as (s)he must use slash --
* typical mistake of Windows users). The passed names are always considered relative to some
* loader-defined root location (often referred as the "template root directory"), and will never start
* with a slash, nor will they contain a path component consisting of either a single or a double dot --
* these are all resolved by the template cache before passing the name to the loader. As a side effect,
* paths that trivially reach outside template root directory, such as ../my.ftl, will be
* rejected by the template cache, so they never reach the template loader. Note again, that if the path
* uses backslash as path separator instead of slash as (the template loader should not accept that), the
* normalization will not properly happen, as FreeMarker (the cache) recognizes only the slashes as
* separators.
*
* @return An object representing the template source, which can be supplied in subsequent calls to
* {@link #getLastModified(Object)} and {@link #getReader(Object, String)}, when those are called on the
* same {@link TemplateLoader}. {@code null} must be returned if the source for the template doesn't exist;
* don't throw exception then! The exact type of this object is up to the {@link TemplateLoader}
* implementation. As this object is possibly used as hash key in caches, and is surly compared with another
* template source for equality, it must have a proper {@link Object#equals(Object)} and
* {@link Object#hashCode()}) implementation. Especially, template sources that refer to the same
* physical source must be equivalent, otherwise template caching can become inefficient. This is only
* expected from {@link Object#equals(Object)} when the compared template sources came from the same
* {@link TemplateLoader} instance. Also, it must not influence the equality if the source is open or
* closed ({@link #closeTemplateSource(Object)}).
*
* @throws IOException
* When an error occurs that makes it impossible to find out if the template exists, or to access the
* existing template. Don't throw exception if the template doesn't exist, instead return with
* {@code null} then!
*/
public Object findTemplateSource(String name)
throws IOException;
/**
* Returns the time of last modification of the specified template source. This method is called after
* findTemplateSource()
.
*
* @param templateSource
* an object representing a template source (the template file), obtained through a prior call to
* {@link #findTemplateSource(String)}. This must be an object on which
* {@link TemplateLoader#closeTemplateSource(Object)} wasn't applied yet.
* @return The time of last modification for the specified template source, or -1 if the time is not known. This
* value meant to be milliseconds since the epoch, but in fact FreeMarker doesn't care what it means, it
* only cares if it changes, in which case the template needs to be reloaded (even if the value has
* decreased). -1 is not special in that regard either; if you keep returning it, FreeMarker won't
* reload the template (as far as it's not evicted from the cache from some other
* reason). Note that {@link Long#MIN_VALUE} is reserved for internal use.
*/
public long getLastModified(Object templateSource);
/**
* Returns the character stream of a template represented by the specified template source. This method is possibly
* called for multiple times for the same template source object, and it must always return a {@link Reader} that
* reads the template from its beginning. Before this method is called for the second time (or later), its caller
* must close the previously returned {@link Reader}, and it must not use it anymore. That is, this method is not
* required to support multiple concurrent readers for the same source {@code templateSource} object.
*
*
* Typically, this method is called if the template is missing from the cache, or if after calling
* {@link #findTemplateSource(String)} and {@link #getLastModified(Object)} it was determined that the cached copy
* of the template is stale. Then, if it turns out that the {@code encoding} parameter used doesn't match the actual
* template content (based on the {@code #ftl encoding=...} header), this method will be called for a second time
* with the correct {@code encoding} parameter value.
*
*
* Unlike {@link #findTemplateSource(String)}, this method must not tolerate if the template is not found, and
* must throw {@link IOException} in that case.
*
* @param templateSource
* an object representing a template source, obtained through a prior call to
* {@link #findTemplateSource(String)}. This must be an object on which
* {@link TemplateLoader#closeTemplateSource(Object)} wasn't applied yet.
* @param encoding
* the character encoding used to translate source bytes to characters. Some loaders may not have access
* to the byte representation of the template stream, and instead directly obtain a character stream.
* These loaders should ignore the encoding parameter.
*
* @return A {@link Reader} representing the template character stream; not {@code null}. It's the responsibility of
* the caller (which is {@link TemplateCache} usually) to {@code close()} it. The {@link Reader} is not
* required to work after the {@code templateSource} was closed ({@link #closeTemplateSource(Object)}).
*
* @throws IOException
* if an I/O error occurs while accessing the stream.
*/
public Reader getReader(Object templateSource, String encoding) throws IOException;
/**
* Closes the template source, releasing any resources held that are only required for reading the template and/or
* its metadata. This is the last method that is called by the {@link TemplateCache} for a template source, except
* that {@link Object#equals(Object)} is might called later too. {@link TemplateCache} ensures that this method will
* be called on every object that is returned from {@link #findTemplateSource(String)}.
*
* @param templateSource
* the template source that should be closed.
*/
public void closeTemplateSource(Object templateSource) throws IOException;
}