/* * Copyright 2014 Attila Szegedi, Daniel Dekany, Jonathan Revusky * * Licensed 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. * * <p>To set the {@link TemplateLoader} used by FreeMaker, use * {@link Configuration#setTemplateLoader(TemplateLoader)}. * * <p>Implementations of this interface should be thread-safe. * * <p>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. * * <p>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 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, be throwing and * 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 <tt>../my.ftl</tt>, 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, <b>it must have a proper {@link Object#equals(Object)} and * {@link Object#hashCode()}) implementation</b>. 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, if the backing storage mechanism supports * that. * * @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. * @return The time of last modification of the specified template source, or -1 if the time is not known. In * principle, -1 should be only returned if the backing storage doesn't store last modification times, not * when there was an error during getting the last modification time (then you should throw * {@link GetLastModifiedException}). However, Java's {@code File} and {@code URL} API can't differentiate * between these two cases, so for {@link TemplateLoader} based on those -1 is also used when an error has * occurred. * * @throws GetLastModifiedException * If there was an error when reading the last modification date (since 2.4.0). Note that for backward * compatibility, this is an unchecked exception, and is not an {@link IOException}. */ public long getLastModified(Object templateSource) throws GetLastModifiedException; /** * 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. * * <p> * 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. * * @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. 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; }