freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [53/54] incubator-freemarker git commit: Added early draft of TemplateResolver, renamed TemplateCache to DefaultTemplateResolver. TemplateResolver is not yet directly used in Configuration. This was only added in a hurry, so that it's visible why the o.a
Date Thu, 16 Feb 2017 23:09:18 GMT
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/94d39312/src/main/java/org/apache/freemarker/core/templateresolver/MruCacheStorage.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/MruCacheStorage.java b/src/main/java/org/apache/freemarker/core/templateresolver/MruCacheStorage.java
index 8b0b5ed..1b34662 100644
--- a/src/main/java/org/apache/freemarker/core/templateresolver/MruCacheStorage.java
+++ b/src/main/java/org/apache/freemarker/core/templateresolver/MruCacheStorage.java
@@ -37,12 +37,12 @@ import java.util.Map;
  * On the other hand, if you wish to use this storage in a strong-only mode, or
  * in a soft-only mode, you might consider using {@link StrongCacheStorage} or
  * {@link SoftCacheStorage} instead, as they can be used by 
- * {@link TemplateCache} concurrently without any synchronization on a 5.0 or 
+ * {@link DefaultTemplateResolver} concurrently without any synchronization on a 5.0 or 
  * later JRE.
  *  
  * <p>This class is <em>NOT</em> thread-safe. If it's accessed from multiple
  * threads concurrently, proper synchronization must be provided by the callers.
- * Note that {@link TemplateCache}, the natural user of this class provides the
+ * Note that {@link DefaultTemplateResolver}, the natural user of this class provides the
  * necessary synchronizations when it uses the class.
  * Also you might consider whether you need this sort of a mixed storage at all
  * in your solution, as in most cases SoftCacheStorage can also be sufficient. 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/94d39312/src/main/java/org/apache/freemarker/core/templateresolver/TemplateCache.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/TemplateCache.java b/src/main/java/org/apache/freemarker/core/templateresolver/TemplateCache.java
deleted file mode 100644
index 9efc58a..0000000
--- a/src/main/java/org/apache/freemarker/core/templateresolver/TemplateCache.java
+++ /dev/null
@@ -1,1111 +0,0 @@
-/*
- * 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 org.apache.freemarker.core.templateresolver;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.Serializable;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.StringTokenizer;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.Template;
-import org.apache.freemarker.core.TemplateNotFoundException;
-import org.apache.freemarker.core._CoreLogs;
-import org.apache.freemarker.core._TemplateAPI;
-import org.apache.freemarker.core.ast.BugException;
-import org.apache.freemarker.core.ast.Environment;
-import org.apache.freemarker.core.ast.MarkReleaserTemplateSpecifiedEncodingHandler;
-import org.apache.freemarker.core.ast.TemplateConfiguration;
-import org.apache.freemarker.core.ast.TemplateSpecifiedEncodingHandler;
-import org.apache.freemarker.core.util.NullArgumentException;
-import org.apache.freemarker.core.util.StringUtil;
-import org.apache.freemarker.core.util.UndeclaredThrowableException;
-import org.slf4j.Logger;
-
-/**
- * Performs caching and on-demand loading of the templates.
- * The actual template "file" loading is delegated to a {@link TemplateLoader} that you can specify in the constructor.
- * Some aspects of caching is delegated to a {@link CacheStorage} that you can also specify in the constructor.
- * 
- * <p>Typically you don't instantiate or otherwise use this class directly. The {@link Configuration} embeds an
- * instance of this class, that you access indirectly through {@link Configuration#getTemplate(String)} and other
- * {@link Configuration} API-s. Then {@link TemplateLoader} and {@link CacheStorage} can be set with
- * {@link Configuration#setTemplateLoader(TemplateLoader)} and
- * {@link Configuration#setCacheStorage(CacheStorage)}.
- */
-public class TemplateCache {
-    
-    /**
-     * The default template update delay; see {@link Configuration#setTemplateUpdateDelayMilliseconds(long)}.
-     * 
-     * @since 2.3.23
-     */
-    public static final long DEFAULT_TEMPLATE_UPDATE_DELAY_MILLIS = 5000L;
-    
-    private static final String ASTERISKSTR = "*";
-    private static final char ASTERISK = '*';
-    private static final char SLASH = '/';
-    private static final String LOCALE_PART_SEPARATOR = "_";
-    private static final Logger LOG = _CoreLogs.TEMPLATE_RESOLVER;
-
-    /** Maybe {@code null}. */
-    private final TemplateLoader templateLoader;
-    
-    /** Here we keep our cached templates */
-    private final CacheStorage cacheStorage;
-    private final TemplateLookupStrategy templateLookupStrategy;
-    private final TemplateNameFormat templateNameFormat;
-    private final TemplateConfigurationFactory templateConfigurations;
-    
-    private final boolean isCacheStorageConcurrent;
-    /** {@link Configuration#setTemplateUpdateDelayMilliseconds(long)} */
-    private long updateDelay = DEFAULT_TEMPLATE_UPDATE_DELAY_MILLIS;
-    /** {@link Configuration#setLocalizedLookup(boolean)} */
-    private boolean localizedLookup = true;
-
-    private Configuration config;
-
-    /**
-     * Same as {@link #TemplateCache(TemplateLoader, CacheStorage, Configuration)} with a new {@link SoftCacheStorage}
-     * as the 2nd parameter.
-     * 
-     * @since 2.3.21
-     */
-    public TemplateCache(TemplateLoader templateLoader, Configuration config) {
-        this(templateLoader, _TemplateAPI.createDefaultCacheStorage(Configuration.VERSION_2_3_0), config);
-    }
-    
-    /**
-     * Same as
-     * {@link #TemplateCache(TemplateLoader, CacheStorage, TemplateLookupStrategy, TemplateNameFormat, Configuration)}
-     * with {@link TemplateLookupStrategy#DEFAULT_2_3_0} and {@link TemplateNameFormat#DEFAULT_2_3_0}.
-     * 
-     * @since 2.3.21
-     */
-    public TemplateCache(TemplateLoader templateLoader, CacheStorage cacheStorage, Configuration config) {
-        this(templateLoader, cacheStorage,
-                _TemplateAPI.getDefaultTemplateLookupStrategy(Configuration.VERSION_2_3_0),
-                _TemplateAPI.getDefaultTemplateNameFormat(Configuration.VERSION_2_3_0),
-                config);
-    }
-    
-    /**
-     * Same as
-     * {@link TemplateCache#TemplateCache(TemplateLoader, CacheStorage, TemplateLookupStrategy, TemplateNameFormat,
-     * TemplateConfigurationFactory, Configuration)} with {@code null} for {@code templateConfigurations}-s.
-     * 
-     * @since 2.3.22
-     */
-    public TemplateCache(TemplateLoader templateLoader, CacheStorage cacheStorage,
-            TemplateLookupStrategy templateLookupStrategy, TemplateNameFormat templateNameFormat,
-            Configuration config) {
-        this(templateLoader, cacheStorage, templateLookupStrategy, templateNameFormat, null, config);
-    }
-
-    /**
-     * @param templateLoader
-     *            The {@link TemplateLoader} to use. Can be {@code null}, though then every request will result in
-     *            {@link TemplateNotFoundException}.
-     * @param cacheStorage
-     *            The {@link CacheStorage} to use. Can't be {@code null}.
-     * @param templateLookupStrategy
-     *            The {@link TemplateLookupStrategy} to use. Can't be {@code null}.
-     * @param templateNameFormat
-     *            The {@link TemplateNameFormat} to use. Can't be {@code null}.
-     * @param templateConfigurations
-     *            The {@link TemplateConfigurationFactory} to use. Can be {@code null} (then all templates will use the
-     *            settings coming from the {@link Configuration} as is, except in the very rare case where a
-     *            {@link TemplateLoader} itself specifies a {@link TemplateConfiguration}).
-     * @param config
-     *            The {@link Configuration} this cache will be used for. Can't be {@code null}.
-     * 
-     * @since 2.3.24
-     */
-    public TemplateCache(TemplateLoader templateLoader, CacheStorage cacheStorage,
-            TemplateLookupStrategy templateLookupStrategy, TemplateNameFormat templateNameFormat,
-            TemplateConfigurationFactory templateConfigurations,
-            Configuration config) {
-        this.templateLoader = templateLoader;
-        
-        NullArgumentException.check("cacheStorage", cacheStorage);
-        this.cacheStorage = cacheStorage;
-        isCacheStorageConcurrent = cacheStorage instanceof ConcurrentCacheStorage &&
-                ((ConcurrentCacheStorage) cacheStorage).isConcurrent();
-        
-        NullArgumentException.check("templateLookupStrategy", templateLookupStrategy);
-        this.templateLookupStrategy = templateLookupStrategy;
-
-        NullArgumentException.check("templateNameFormat", templateNameFormat);
-        this.templateNameFormat = templateNameFormat;
-
-        // Can be null
-        this.templateConfigurations = templateConfigurations;
-        
-        NullArgumentException.check("config", config);
-        this.config = config;
-    }
-    
-    /**
-     * Returns the configuration for internal usage.
-     */
-    // [FM3] After setConfiguration was removed, this can be too.
-    Configuration getConfiguration() {
-        return config;
-    }
-
-    public TemplateLoader getTemplateLoader() {
-        return templateLoader;
-    }
-
-    public CacheStorage getCacheStorage() {
-        return cacheStorage;
-    }
-    
-    /**
-     * @since 2.3.22
-     */
-    public TemplateLookupStrategy getTemplateLookupStrategy() {
-        return templateLookupStrategy;
-    }
-    
-    /**
-     * @since 2.3.22
-     */
-    public TemplateNameFormat getTemplateNameFormat() {
-        return templateNameFormat;
-    }
-    
-    /**
-     * @since 2.3.24
-     */
-    public TemplateConfigurationFactory getTemplateConfigurations() {
-        return templateConfigurations;
-    }
-
-    /**
-     * Retrieves the template with the given name (and according the specified further parameters) from the template
-     * cache, loading it into the cache first if it's missing/staled.
-     * 
-     * <p>
-     * All parameters must be non-{@code null}, except {@code customLookupCondition}. For the meaning of the parameters
-     * see {@link Configuration#getTemplate(String, Locale, String, boolean)}.
-     *
-     * @return A {@link MaybeMissingTemplate} object that contains the {@link Template}, or a
-     *         {@link MaybeMissingTemplate} object that contains {@code null} as the {@link Template} and information
-     *         about the missing template. The return value itself is never {@code null}. Note that exceptions occurring
-     *         during template loading will not be classified as a missing template, so they will cause an exception to
-     *         be thrown by this method instead of returning a {@link MaybeMissingTemplate}. The idea is that having a
-     *         missing template is normal (not exceptional), providing that the backing storage mechanism could indeed
-     *         check that it's missing.
-     * 
-     * @throws MalformedTemplateNameException
-     *             If the {@code name} was malformed according the current {@link TemplateNameFormat}. However, if the
-     *             {@link TemplateNameFormat} is {@link TemplateNameFormat#DEFAULT_2_3_0} and
-     *             {@link Configuration#getIncompatibleImprovements()} is less than 2.4.0, then instead of throwing this
-     *             exception, a {@link MaybeMissingTemplate} will be returned, similarly as if the template were missing
-     *             (the {@link MaybeMissingTemplate#getMissingTemplateReason()} will describe the real error).
-     * 
-     * @throws IOException
-     *             If reading the template has failed from a reason other than the template is missing. This method
-     *             should never be a {@link TemplateNotFoundException}, as that condition is indicated in the return
-     *             value.
-     * 
-     * @since 2.3.22
-     */
-    public MaybeMissingTemplate getTemplate(String name, Locale locale, Object customLookupCondition,
-            String encoding, boolean parseAsFTL)
-    throws IOException {
-        NullArgumentException.check("name", name);
-        NullArgumentException.check("locale", locale);
-        NullArgumentException.check("encoding", encoding);
-        
-        try {
-            name = templateNameFormat.normalizeRootBasedName(name);
-        } catch (MalformedTemplateNameException e) {
-            // If we don't have to emulate backward compatible behavior, then just rethrow it: 
-            if (templateNameFormat != TemplateNameFormat.DEFAULT_2_3_0
-                    || config.getIncompatibleImprovements().intValue() >= _TemplateAPI.VERSION_INT_2_4_0) {
-                throw e;
-            }
-            return new MaybeMissingTemplate(null, e);
-        }
-        
-        if (templateLoader == null) {
-            return new MaybeMissingTemplate(name, "The TemplateLoader (and TemplateLoader2) was null.");
-        }
-        
-        Template template = getTemplateInternal(name, locale, customLookupCondition, encoding, parseAsFTL);
-        return template != null ? new MaybeMissingTemplate(template) : new MaybeMissingTemplate(name, (String) null);
-    }
-
-    /**
-     * Similar to {@link #getTemplate(String, Locale, Object, String, boolean)} with {@code null}
-     * {@code customLookupCondition}.
-     * 
-     * @return {@link MaybeMissingTemplate#getTemplate()} of the
-     *         {@link #getTemplate(String, Locale, Object, String, boolean)} return value.
-     * 
-     * @deprecated Use {@link #getTemplate(String, Locale, Object, String, boolean)}, which can return more detailed
-     *             result when the template is missing.
-     */
-    @Deprecated
-    public Template getTemplate(String name, Locale locale, String encoding, boolean parseAsFTL)
-    throws IOException {
-        return getTemplate(name, locale, null, encoding, parseAsFTL).getTemplate();
-    }
-    
-    private Template getTemplateInternal(
-            final String name, final Locale locale, final Object customLookupCondition,
-            final String encoding, final boolean parseAsFTL)
-    throws IOException {
-        final boolean debug = LOG.isDebugEnabled();
-        final String debugPrefix = debug
-                ? getDebugPrefix("getTemplate", name, locale, customLookupCondition, encoding, parseAsFTL)
-                : null;
-        final CachedResultKey cacheKey = new CachedResultKey(name, locale, customLookupCondition, encoding, parseAsFTL);
-        
-        CachedResult oldCachedResult;
-        if (isCacheStorageConcurrent) {
-            oldCachedResult = (CachedResult) cacheStorage.get(cacheKey);
-        } else {
-            synchronized (cacheStorage) {
-                oldCachedResult = (CachedResult) cacheStorage.get(cacheKey);
-            }
-        }
-        
-        final long now = System.currentTimeMillis();
-        
-        boolean rethrownCachedException = false;
-        boolean suppressFinallyException = false;
-        TemplateLookupResult newLookupResult = null;
-        CachedResult newCachedResult = null;
-        TemplateLoaderSession session = null;
-        try {
-            if (oldCachedResult != null) {
-                // If we're within the refresh delay, return the cached result
-                if (now - oldCachedResult.lastChecked < updateDelay) {
-                    if (debug) {
-                        LOG.debug(debugPrefix + "Cached copy not yet stale; using cached.");
-                    }
-                    Object t = oldCachedResult.templateOrException;
-                    // t can be null, indicating a cached negative lookup
-                    if (t instanceof Template || t == null) {
-                        return (Template) t;
-                    } else if (t instanceof RuntimeException) {
-                        rethrowCachedException((RuntimeException) t);
-                    } else if (t instanceof IOException) {
-                        rethrownCachedException = true;
-                        rethrowCachedException((IOException) t);
-                    }
-                    throw new BugException("Unhandled class for t: " + t.getClass().getName());
-                }
-                // The freshness of the cache result must be checked.
-                
-                // Clone, as the instance in the cache store must not be modified to ensure proper concurrent behavior.
-                newCachedResult = oldCachedResult.clone();
-                newCachedResult.lastChecked = now;
-
-                session = templateLoader.createSession();
-                if (debug && session != null) {
-                    LOG.debug(debugPrefix + "Session created.");
-                }
-                
-                // Find the template source, load it if it doesn't correspond to the cached result.
-                newLookupResult = lookupAndLoadTemplateIfChanged(
-                        name, locale, customLookupCondition, oldCachedResult.source, oldCachedResult.version, session);
-
-                // Template source was removed (TemplateLoader2ResultStatus.NOT_FOUND, or no TemplateLoader2Result)
-                if (!newLookupResult.isPositive()) { 
-                    if (debug) {
-                        LOG.debug(debugPrefix + "No source found.");
-                    } 
-                    setToNegativeAndPutIntoCache(cacheKey, newCachedResult, null);
-                    return null;
-                }
-
-                final TemplateLoadingResult newTemplateLoaderResult = newLookupResult.getTemplateLoaderResult();
-                if (newTemplateLoaderResult.getStatus() == TemplateLoadingResultStatus.NOT_MODIFIED) {
-                    // Return the cached version.
-                    if (debug) {
-                        LOG.debug(debugPrefix + ": Using cached template "
-                                + "(source: " + newTemplateLoaderResult.getSource() + ")"
-                                + " as it hasn't been changed on the backing store.");
-                    }
-                    putIntoCache(cacheKey, newCachedResult);
-                    return (Template) newCachedResult.templateOrException;
-                } else {
-                    if (newTemplateLoaderResult.getStatus() != TemplateLoadingResultStatus.OPENED) {
-                        // TemplateLoader2ResultStatus.NOT_FOUND was already handler earlier
-                        throw new BugException("Unxpected status: " + newTemplateLoaderResult.getStatus());
-                    }
-                    if (debug) {
-                        StringBuilder debugMsg = new StringBuilder();
-                        debugMsg.append(debugPrefix)
-                                .append("Reloading template instead of using the cached result because ");
-                        if (newCachedResult.templateOrException instanceof Throwable) {
-                            debugMsg.append("it's a cached error (retrying).");
-                        } else {
-                            Object newSource = newTemplateLoaderResult.getSource();
-                            if (!nullSafeEquals(newSource, oldCachedResult.source)) {
-                                debugMsg.append("the source has been changed: ")
-                                        .append("cached.source=").append(StringUtil.jQuoteNoXSS(oldCachedResult.source))
-                                        .append(", current.source=").append(StringUtil.jQuoteNoXSS(newSource));
-                            } else {
-                                Serializable newVersion = newTemplateLoaderResult.getVersion();
-                                if (!nullSafeEquals(oldCachedResult.version, newVersion)) {
-                                    debugMsg.append("the version has been changed: ")
-                                            .append("cached.version=").append(oldCachedResult.version) 
-                                            .append(", current.version=").append(newVersion);
-                                } else {
-                                    debugMsg.append("??? (unknown reason)");
-                                }
-                            }
-                        }
-                        LOG.debug(debugMsg.toString());
-                    }
-                }
-            } else { // if there was no cached result
-                if (debug) {
-                    LOG.debug(debugPrefix + "No cached result was found; will try to load template.");
-                }
-                
-                newCachedResult = new CachedResult();
-                newCachedResult.lastChecked = now;
-            
-                session = templateLoader.createSession();
-                if (debug && session != null) {
-                    LOG.debug(debugPrefix + "Session created.");
-                } 
-                
-                newLookupResult = lookupAndLoadTemplateIfChanged(
-                        name, locale, customLookupCondition, null, null, session);
-                
-                if (!newLookupResult.isPositive()) {
-                    setToNegativeAndPutIntoCache(cacheKey, newCachedResult, null);
-                    return null;
-                }
-            }
-            // We have newCachedResult and newLookupResult initialized at this point.
-
-            TemplateLoadingResult templateLoaderResult = newLookupResult.getTemplateLoaderResult();
-            newCachedResult.source = templateLoaderResult.getSource();
-            
-            // If we get here, then we need to (re)load the template
-            if (debug) {
-                LOG.debug(debugPrefix + "Reading template content (source: "
-                        + StringUtil.jQuoteNoXSS(newCachedResult.source) + ")");
-            }
-            
-            Template template = loadTemplate(
-                    templateLoaderResult,
-                    name, newLookupResult.getTemplateSourceName(), locale, customLookupCondition,
-                    encoding, parseAsFTL);
-            if (session != null) {
-                session.close();
-                if (debug) {
-                    LOG.debug(debugPrefix + "Session closed.");
-                } 
-            }
-            newCachedResult.templateOrException = template;
-            newCachedResult.version = templateLoaderResult.getVersion();
-            putIntoCache(cacheKey, newCachedResult);
-            return template;
-        } catch (RuntimeException e) {
-            if (newCachedResult != null) {
-                setToNegativeAndPutIntoCache(cacheKey, newCachedResult, e);
-            }
-            suppressFinallyException = true;
-            throw e;
-        } catch (IOException e) {
-            // Rethrown cached exceptions are wrapped into IOException-s, so we only need this condition here.
-            if (!rethrownCachedException) {
-                setToNegativeAndPutIntoCache(cacheKey, newCachedResult, e);
-            }
-            suppressFinallyException = true;
-            throw e;
-        } finally {
-            try {
-                // Close streams first:
-                
-                if (newLookupResult != null && newLookupResult.isPositive()) {
-                    TemplateLoadingResult templateLoaderResult = newLookupResult.getTemplateLoaderResult();
-                    Reader reader = templateLoaderResult.getReader();
-                    if (reader != null) {
-                        try {
-                            reader.close();
-                        } catch (IOException e) { // [FM3] Exception e
-                            if (suppressFinallyException) {
-                                if (LOG.isWarnEnabled()) { 
-                                    LOG.warn("Failed to close template content Reader for: " + name, e);
-                                }
-                            } else {
-                                suppressFinallyException = true;
-                                throw e;
-                            }
-                        }
-                    } else if (templateLoaderResult.getInputStream() != null) {
-                        try {
-                            templateLoaderResult.getInputStream().close();
-                        } catch (IOException e) { // [FM3] Exception e
-                            if (suppressFinallyException) {
-                                if (LOG.isWarnEnabled()) { 
-                                    LOG.warn("Failed to close template content InputStream for: " + name, e);
-                                }
-                            } else {
-                                suppressFinallyException = true;
-                                throw e;
-                            }
-                        }
-                    }
-                }
-            } finally {
-                // Then close streams:
-                
-                if (session != null && !session.isClosed()) {
-                    try {
-                        session.close();
-                        if (debug) {
-                            LOG.debug(debugPrefix + "Session closed.");
-                        } 
-                    } catch (IOException e) { // [FM3] Exception e
-                        if (suppressFinallyException) {
-                            if (LOG.isWarnEnabled()) { 
-                                LOG.warn("Failed to close template loader session for" + name, e);
-                            }
-                        } else {
-                            suppressFinallyException = true;
-                            throw e;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    
-    
-    private static final Method INIT_CAUSE = getInitCauseMethod();
-    
-    private static final Method getInitCauseMethod() {
-        try {
-            return Throwable.class.getMethod("initCause", new Class[] { Throwable.class });
-        } catch (NoSuchMethodException e) {
-            return null;
-        }
-    }
-    
-    /**
-     * Creates an {@link IOException} that has a cause exception.
-     */
-    // [Java 6] Remove
-    private IOException newIOException(String message, Throwable cause) {
-        if (cause == null) {
-            return new IOException(message);
-        }
-        
-        IOException ioe;
-        if (INIT_CAUSE != null) {
-            ioe = new IOException(message);
-            try {
-                INIT_CAUSE.invoke(ioe, cause);
-            } catch (RuntimeException ex) {
-                throw ex;
-            } catch (Exception ex) {
-                throw new UndeclaredThrowableException(ex);
-            }
-        } else {
-            ioe = new IOException(message + "\nCaused by: " + cause.getClass().getName() + 
-            ": " + cause.getMessage());
-        }
-        return ioe;
-    }
-    
-    private void rethrowCachedException(Throwable e) throws IOException {
-        throw newIOException("There was an error loading the " +
-                "template on an earlier attempt; see cause exception.", e);
-    }
-
-    private void setToNegativeAndPutIntoCache(CachedResultKey cacheKey, CachedResult cachedResult, Exception e) {
-        cachedResult.templateOrException = e;
-        cachedResult.source = null;
-        cachedResult.version = null;
-        putIntoCache(cacheKey, cachedResult);
-    }
-
-    private void putIntoCache(CachedResultKey tk, CachedResult cachedTemplate) {
-        if (isCacheStorageConcurrent) {
-            cacheStorage.put(tk, cachedTemplate);
-        } else {
-            synchronized (cacheStorage) {
-                cacheStorage.put(tk, cachedTemplate);
-            }
-        }
-    }
-
-    @SuppressWarnings("deprecation")
-    private Template loadTemplate(
-            TemplateLoadingResult templateLoaderResult,
-            final String name, final String sourceName, Locale locale, final Object customLookupCondition,
-            String initialEncoding, final boolean parseAsFTL) throws IOException {
-        TemplateConfiguration tc;
-        {
-            TemplateConfiguration cfgTC;
-            try {
-                cfgTC = templateConfigurations != null
-                        ? templateConfigurations.get(sourceName, templateLoaderResult.getSource()) : null;
-            } catch (TemplateConfigurationFactoryException e) {
-                throw newIOException("Error while getting TemplateConfiguration; see cause exception.", e);
-            }
-            TemplateConfiguration resultTC = templateLoaderResult.getTemplateConfiguration();
-            if (resultTC != null) {
-                TemplateConfiguration mergedTC = new TemplateConfiguration();
-                if (cfgTC != null) {
-                    mergedTC.merge(cfgTC);
-                }
-                if (resultTC != null) {
-                    mergedTC.merge(resultTC);
-                }
-                mergedTC.setParentConfiguration(config);
-                
-                tc = mergedTC;
-            } else {
-                tc = cfgTC;
-            }
-        }
-        
-        if (tc != null) {
-            // TC.{encoding,locale} is stronger than the cfg.getTemplate arguments by design.
-            if (tc.isEncodingSet()) {
-                initialEncoding = tc.getEncoding();
-            }
-            if (tc.isLocaleSet()) {
-                locale = tc.getLocale();
-            }
-        }
-        
-        Template template;
-        {
-            Reader reader = templateLoaderResult.getReader();
-            InputStream inputStream = templateLoaderResult.getInputStream();
-            TemplateSpecifiedEncodingHandler templateSpecifiedEncodingHandler;
-            if (reader != null) {
-                if (inputStream != null) {
-                    throw new IllegalStateException("For a(n) " + templateLoaderResult.getClass().getName()
-                            + ", both getReader() and getInputStream() has returned non-null.");
-                }
-                initialEncoding = null;  // No charset decoding has happened
-                templateSpecifiedEncodingHandler = TemplateSpecifiedEncodingHandler.DEFAULT; 
-            } else if (inputStream != null) {
-                if (parseAsFTL) {
-                    // We need mark support, to restart if the charset suggested by <#ftl encoding=...> differs
-                    // from that we use initially.
-                    if (!inputStream.markSupported()) {
-                        inputStream = new BufferedInputStream(inputStream);
-                    }
-                    inputStream.mark(Integer.MAX_VALUE); // Mark is released after the 1st FTL tag
-                    templateSpecifiedEncodingHandler = new MarkReleaserTemplateSpecifiedEncodingHandler(inputStream);
-                } else {
-                    templateSpecifiedEncodingHandler = null; 
-                }
-                // Regarding buffering worries: On the Reader side we should only read in chunks (like through a
-                // BufferedReader), so there shouldn't be a problem if the InputStream is not buffered. (Also, at least
-                // on Oracle JDK and OpenJDK 7 the InputStreamReader itself has an internal ~8K buffer.)
-                reader = new InputStreamReader(inputStream, initialEncoding);
-            } else {
-                throw new IllegalStateException("For a(n) " + templateLoaderResult.getClass().getName()
-                        + ", both getReader() and getInputStream() has returned null.");
-            }
-            
-            try {
-                if (parseAsFTL) {
-                    try {
-                        template = new Template(name, sourceName, reader, config, tc,
-                                initialEncoding, templateSpecifiedEncodingHandler);
-                    } catch (Template.WrongEncodingException wee) {
-                        final String templateSpecifiedEncoding = wee.getTemplateSpecifiedEncoding();
-                        
-                        if (inputStream != null) {
-                            // We restart InputStream to re-decode it with the new charset.
-                            inputStream.reset();
-                            
-                            // Don't close `reader`; it's an InputStreamReader that would close the wrapped InputStream.
-                            reader = new InputStreamReader(inputStream, templateSpecifiedEncoding);
-                        } else {
-                            // Should be impossible to get here
-                            throw new BugException();
-                        }
-                        
-                        template = new Template(name, sourceName, reader, config, tc,
-                                templateSpecifiedEncoding, templateSpecifiedEncodingHandler);
-                    }
-                } else {
-                    // Read the contents into a StringWriter, then construct a single-text-block template from it.
-                    final StringBuilder sb = new StringBuilder();
-                    final char[] buf = new char[4096];
-                    int charsRead;
-                    while ((charsRead = reader.read(buf)) > 0) {
-                        sb.append(buf, 0, charsRead);
-                    }
-                    template = Template.getPlainTextTemplate(name, sourceName, sb.toString(), config);
-                    template.setEncoding(initialEncoding);
-                }
-            } finally {
-                reader.close();
-            }
-        }
-
-        if (tc != null) {
-            tc.apply(template);
-        }
-        
-        template.setLocale(locale);
-        template.setCustomLookupCondition(customLookupCondition);
-        return template;
-    }
-
-    /**
-     * Gets the delay in milliseconds between checking for newer versions of a
-     * template source.
-     * @return the current value of the delay
-     */
-    public long getDelay() {
-        // synchronized was moved here so that we don't advertise that it's thread-safe, as it's not.
-        synchronized (this) {
-            return updateDelay;
-        }
-    }
-
-    /**
-     * Sets the delay in milliseconds between checking for newer versions of a
-     * template sources.
-     * @param delay the new value of the delay
-     */
-    public void setDelay(long delay) {
-        // synchronized was moved here so that we don't advertise that it's thread-safe, as it's not.
-        synchronized (this) {
-            this.updateDelay = delay;
-        }
-    }
-
-    /**
-     * Returns if localized template lookup is enabled or not.
-     */
-    public boolean getLocalizedLookup() {
-        // synchronized was moved here so that we don't advertise that it's thread-safe, as it's not.
-        synchronized (this) {
-            return localizedLookup;
-        }
-    }
-
-    /**
-     * Setis if localized template lookup is enabled or not.
-     */
-    public void setLocalizedLookup(boolean localizedLookup) {
-        // synchronized was moved here so that we don't advertise that it's thread-safe, as it's not.
-        synchronized (this) {
-            if (this.localizedLookup != localizedLookup) {
-                this.localizedLookup = localizedLookup;
-                clear();
-            }
-        }
-    }
-
-    /**
-     * Removes all entries from the cache, forcing reloading of templates on subsequent
-     * {@link #getTemplate(String, Locale, String, boolean)} calls.
-     * 
-     * @param resetTemplateLoader
-     *            Whether to call {@link TemplateLoader#resetState()}. on the template loader.
-     */
-    public void clear(boolean resetTemplateLoader) {
-        synchronized (cacheStorage) {
-            cacheStorage.clear();
-            if (templateLoader != null && resetTemplateLoader) {
-                templateLoader.resetState();
-            }
-        }
-    }
-    
-    /**
-     * Same as {@link #clear(boolean)} with {@code true} {@code resetTemplateLoader} argument.
-     */
-    public void clear() {
-        synchronized (cacheStorage) {
-            cacheStorage.clear();
-            if (templateLoader != null) {
-                templateLoader.resetState();
-            }
-        }
-    }
-
-    /**
-     * Same as {@link #removeTemplate(String, Locale, Object, String, boolean)} with {@code null}
-     * {@code customLookupCondition}.
-     */
-    public void removeTemplate(
-            String name, Locale locale, String encoding, boolean parse) throws IOException {
-        removeTemplate(name, locale, null, encoding, parse);
-    }
-    
-    /**
-     * Removes an entry from the cache, hence forcing the re-loading of it when it's next time requested. (It doesn't
-     * delete the template file itself.) This is to give the application finer control over cache updating than
-     * {@link #setDelay(long)} alone does.
-     * 
-     * For the meaning of the parameters, see
-     * {@link Configuration#getTemplate(String, Locale, Object, String, boolean, boolean)}
-     */
-    public void removeTemplate(
-            String name, Locale locale, Object customLookupCondition, String encoding, boolean parse)
-    throws IOException {
-        if (name == null) {
-            throw new IllegalArgumentException("Argument \"name\" can't be null");
-        }
-        if (locale == null) {
-            throw new IllegalArgumentException("Argument \"locale\" can't be null");
-        }
-        if (encoding == null) {
-            throw new IllegalArgumentException("Argument \"encoding\" can't be null");
-        }
-        name = templateNameFormat.normalizeRootBasedName(name);
-        if (name != null && templateLoader != null) {
-            boolean debug = LOG.isDebugEnabled();
-            String debugPrefix = debug
-                    ? getDebugPrefix("removeTemplate", name, locale, customLookupCondition, encoding, parse)
-                    : null;
-            CachedResultKey tk = new CachedResultKey(name, locale, customLookupCondition, encoding, parse);
-            
-            if (isCacheStorageConcurrent) {
-                cacheStorage.remove(tk);
-            } else {
-                synchronized (cacheStorage) {
-                    cacheStorage.remove(tk);
-                }
-            }
-            if (debug) {
-                LOG.debug(debugPrefix + "Template was removed from the cache, if it was there");
-            }
-        }
-    }
-
-    private String getDebugPrefix(String operation, String name, Locale locale, Object customLookupCondition, String encoding,
-            boolean parse) {
-        return operation + " " + StringUtil.jQuoteNoXSS(name) + "("
-                + StringUtil.jQuoteNoXSS(locale)
-                + (customLookupCondition != null ? ", cond=" + StringUtil.jQuoteNoXSS(customLookupCondition) : "")
-                + ", " + encoding
-                + (parse ? ", parsed)" : ", unparsed]")
-                + ": ";
-    }    
-
-    /**
-     * @deprecated Use {@link Environment#toFullTemplateName(String, String)} instead, as that can throw
-     *             {@link MalformedTemplateNameException}, and is on a more logical place anyway.
-     * 
-     * @throws IllegalArgumentException
-     *             If the {@code baseName} or {@code targetName} is malformed according the {@link TemplateNameFormat}
-     *             in use.
-     */
-    @Deprecated
-    public static String getFullTemplatePath(Environment env, String baseName, String targetName) {
-        try {
-            return env.toFullTemplateName(baseName, targetName);
-        } catch (MalformedTemplateNameException e) {
-            throw new IllegalArgumentException(e.getMessage());
-        }
-    }
-
-    /**
-     * Looks up according the {@link TemplateLookupStrategy} and then starts reading the template, if it was changed
-     * compared to the cached result, or if there was no cached result yet.
-     */
-    private TemplateLookupResult lookupAndLoadTemplateIfChanged(
-            String name, Locale locale, Object customLookupCondition,
-            TemplateLoadingSource cachedResultSource, Serializable cachedResultVersion,
-            TemplateLoaderSession session) throws IOException {
-        final TemplateLookupResult lookupResult = templateLookupStrategy.lookup(
-                new TemplateCacheTemplateLookupContext(
-                        name, locale, customLookupCondition,
-                        cachedResultSource, cachedResultVersion,
-                        session));
-        if (lookupResult == null) {
-            throw new NullPointerException("Lookup result shouldn't be null");
-        }
-        return lookupResult;
-    }
-
-    private String concatPath(List<String> pathSteps, int from, int to) {
-        StringBuilder buf = new StringBuilder((to - from) * 16);
-        for (int i = from; i < to; ++i) {
-            buf.append(pathSteps.get(i));
-            if (i < pathSteps.size() - 1) {
-                buf.append('/');
-            }
-        }
-        return buf.toString();
-    }
-    
-    // Replace with Objects.equals in Java 7
-    private static boolean nullSafeEquals(Object o1, Object o2) {
-        if (o1 == o2) return true;
-        if (o1 == null || o2 == null) return false;
-        return o1.equals(o2);
-    }
-    
-    /**
-     * Used as cache key to look up a {@link CachedResult}. 
-     */
-    @SuppressWarnings("serial")
-    private static final class CachedResultKey implements Serializable {
-        private final String name;
-        private final Locale locale;
-        private final Object customLookupCondition;
-        private final String encoding;
-        private final boolean parse;
-
-        CachedResultKey(String name, Locale locale, Object customLookupCondition, String encoding, boolean parse) {
-            this.name = name;
-            this.locale = locale;
-            this.customLookupCondition = customLookupCondition;
-            this.encoding = encoding;
-            this.parse = parse;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o instanceof CachedResultKey) {
-                CachedResultKey tk = (CachedResultKey) o;
-                return
-                    parse == tk.parse &&
-                    name.equals(tk.name) &&
-                    locale.equals(tk.locale) &&
-                    nullSafeEquals(customLookupCondition, tk.customLookupCondition) &&
-                    encoding.equals(tk.encoding);
-            }
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            return
-                name.hashCode() ^
-                locale.hashCode() ^
-                encoding.hashCode() ^
-                (customLookupCondition != null ? customLookupCondition.hashCode() : 0) ^
-                Boolean.valueOf(!parse).hashCode();
-        }
-    }
-
-    /**
-     * Hold the a cached {@link #getTemplate(String, Locale, Object, String, boolean)} result and the associated
-     * information needed to check if the cached value is up to date.
-     * 
-     * <p>
-     * Note: this class is Serializable to allow custom 3rd party CacheStorage implementations to serialize/replicate
-     * them; FreeMarker code itself doesn't rely on its serializability.
-     * 
-     * @see CachedResultKey
-     */
-    private static final class CachedResult implements Cloneable, Serializable {
-        private static final long serialVersionUID = 1L;
-
-        Object templateOrException;
-        TemplateLoadingSource source;
-        Serializable version;
-        long lastChecked;
-        
-        @Override
-        public CachedResult clone() {
-            try {
-                return (CachedResult) super.clone();
-            } catch (CloneNotSupportedException e) {
-                throw new UndeclaredThrowableException(e);
-            }
-        }
-    }
-    
-    private class TemplateCacheTemplateLookupContext extends TemplateLookupContext {
-
-        private final TemplateLoaderSession session; 
-        
-        TemplateCacheTemplateLookupContext(String templateName, Locale templateLocale, Object customLookupCondition,
-                TemplateLoadingSource cachedResultSource, Serializable cachedResultVersion,
-                TemplateLoaderSession session) {
-            super(templateName, localizedLookup ? templateLocale : null, customLookupCondition,
-                    cachedResultSource, cachedResultVersion);
-            this.session = session;
-        }
-
-        @Override
-        public TemplateLookupResult lookupWithAcquisitionStrategy(String path) throws IOException {
-            // Only one of the possible ways of making a name non-normalized, but is the easiest mistake to do:
-            if (path.startsWith("/")) {
-                throw new IllegalArgumentException("Non-normalized name, starts with \"/\": " + path);
-            }
-            
-            int asterisk = path.indexOf(ASTERISK);
-            // Shortcut in case there is no acquisition
-            if (asterisk == -1) {
-                return TemplateLookupResult.from(
-                        path,
-                        templateLoader.load(path, getCachedResultSource(), getCachedResultVersion(), session));
-            }
-            StringTokenizer pathTokenizer = new StringTokenizer(path, "/");
-            int lastAsterisk = -1;
-            List<String> pathSteps = new ArrayList<String>();
-            while (pathTokenizer.hasMoreTokens()) {
-                String pathStep = pathTokenizer.nextToken();
-                if (pathStep.equals(ASTERISKSTR)) {
-                    if (lastAsterisk != -1) {
-                        pathSteps.remove(lastAsterisk);
-                    }
-                    lastAsterisk = pathSteps.size();
-                }
-                pathSteps.add(pathStep);
-            }
-            if (lastAsterisk == -1) {  // if there was no real "*" step after all
-                return TemplateLookupResult.from(
-                        path,
-                        templateLoader.load(path, getCachedResultSource(), getCachedResultVersion(), session));
-            }
-            String basePath = concatPath(pathSteps, 0, lastAsterisk);
-            String postAsteriskPath = concatPath(pathSteps, lastAsterisk + 1, pathSteps.size());
-            StringBuilder buf = new StringBuilder(path.length()).append(basePath);
-            int basePathLen = basePath.length();
-            while (true) {
-                String fullPath = buf.append(postAsteriskPath).toString();
-                TemplateLoadingResult templateLoaderResult = templateLoader.load(
-                        fullPath, getCachedResultSource(), getCachedResultVersion(), session);
-                if (templateLoaderResult.getStatus() == TemplateLoadingResultStatus.OPENED) {
-                    return TemplateLookupResult.from(fullPath, templateLoaderResult);
-                }
-                if (basePathLen == 0) {
-                    return TemplateLookupResult.createNegativeResult();
-                }
-                basePathLen = basePath.lastIndexOf(SLASH, basePathLen - 2) + 1;
-                buf.setLength(basePathLen);
-            }
-        }
-
-        @Override
-        public TemplateLookupResult lookupWithLocalizedThenAcquisitionStrategy(final String templateName,
-                final Locale templateLocale) throws IOException {
-            
-                if (templateLocale == null) {
-                    return lookupWithAcquisitionStrategy(templateName);
-                }
-                
-                int lastDot = templateName.lastIndexOf('.');
-                String prefix = lastDot == -1 ? templateName : templateName.substring(0, lastDot);
-                String suffix = lastDot == -1 ? "" : templateName.substring(lastDot);
-                String localeName = LOCALE_PART_SEPARATOR + templateLocale.toString();
-                StringBuilder buf = new StringBuilder(templateName.length() + localeName.length());
-                buf.append(prefix);
-                tryLocaleNameVariations: while (true) {
-                    buf.setLength(prefix.length());
-                    String path = buf.append(localeName).append(suffix).toString();
-                    TemplateLookupResult lookupResult = lookupWithAcquisitionStrategy(path);
-                    if (lookupResult.isPositive()) {
-                        return lookupResult;
-                    }
-                    
-                    int lastUnderscore = localeName.lastIndexOf('_');
-                    if (lastUnderscore == -1) {
-                        break tryLocaleNameVariations;
-                    }
-                    localeName = localeName.substring(0, lastUnderscore);
-                }
-                return createNegativeLookupResult();
-        }
-        
-    }
-    
-    /**
-     * Used for the return value of {@link TemplateCache#getTemplate(String, Locale, Object, String, boolean)}.
-     * 
-     * @since 2.3.22
-     */
-    public final static class MaybeMissingTemplate {
-        
-        private final Template template;
-        private final String missingTemplateNormalizedName;
-        private final String missingTemplateReason;
-        private final MalformedTemplateNameException missingTemplateCauseException;
-        
-        private MaybeMissingTemplate(Template template) {
-            this.template = template;
-            this.missingTemplateNormalizedName = null;
-            this.missingTemplateReason = null;
-            this.missingTemplateCauseException = null;
-        }
-        
-        private MaybeMissingTemplate(String normalizedName, MalformedTemplateNameException missingTemplateCauseException) {
-            this.template = null;
-            this.missingTemplateNormalizedName = normalizedName;
-            this.missingTemplateReason = null;
-            this.missingTemplateCauseException = missingTemplateCauseException;
-        }
-        
-        private MaybeMissingTemplate(String normalizedName, String missingTemplateReason) {
-            this.template = null;
-            this.missingTemplateNormalizedName = normalizedName;
-            this.missingTemplateReason = missingTemplateReason;
-            this.missingTemplateCauseException = null;
-        }
-        
-        /**
-         * The {@link Template} if it wasn't missing, otherwise {@code null}.
-         */
-        public Template getTemplate() {
-            return template;
-        }
-
-        /**
-         * When the template was missing, this <em>possibly</em> contains the explanation, or {@code null}. If the
-         * template wasn't missing (i.e., when {@link #getTemplate()} return non-{@code null}) this is always
-         * {@code null}.
-         */
-        public String getMissingTemplateReason() {
-            return missingTemplateReason != null
-                    ? missingTemplateReason
-                    : (missingTemplateCauseException != null
-                            ? missingTemplateCauseException.getMalformednessDescription()
-                            : null);
-        }
-        
-        /**
-         * When the template was missing, this <em>possibly</em> contains its normalized name. If the template wasn't
-         * missing (i.e., when {@link #getTemplate()} return non-{@code null}) this is always {@code null}. When the
-         * template is missing, it will be {@code null} for example if the normalization itself was unsuccessful.
-         */
-        public String getMissingTemplateNormalizedName() {
-            return missingTemplateNormalizedName;
-        }
-        
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/94d39312/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoader.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoader.java b/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoader.java
index e754581..f484224 100644
--- a/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoader.java
+++ b/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoader.java
@@ -45,8 +45,8 @@ import org.apache.freemarker.core.TemplateNotFoundException;
  * 
  * <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.
+ * {@link DefaultTemplateResolver} of the {@link Configuration}, and is normally only accessed directly by the
+ * {@link DefaultTemplateResolver}, and templates are get via the {@link DefaultTemplateResolver} API-s.
  */
 public interface TemplateLoader {
 
@@ -61,7 +61,7 @@ public interface TemplateLoader {
      * 
      * @param name
      *            The name (template root directory relative path) of the template, already localized and normalized by
-     *            the {@link org.apache.freemarker.core.templateresolver.TemplateCache cache}. It is completely up to the loader implementation to
+     *            the {@link org.apache.freemarker.core.templateresolver.DefaultTemplateResolver 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

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/94d39312/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoaderSession.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoaderSession.java b/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoaderSession.java
index bbc5445..9fd99ff 100644
--- a/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoaderSession.java
+++ b/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoaderSession.java
@@ -26,7 +26,7 @@ import java.io.Reader;
  * Stores shared state between {@link TemplateLoader} operations that are executed close to each other in the same
  * thread. For example, a {@link TemplateLoader} that reads from a database might wants to store the database
  * connection in it for reuse. The goal of sessions is mostly to increase performance. However, because a
- * {@link TemplateCache#getTemplate(String, java.util.Locale, Object, String, boolean)} call is executed inside a single
+ * {@link DefaultTemplateResolver#getTemplate(String, java.util.Locale, Object, String, boolean)} call is executed inside a single
  * session, sessions can be also be utilized to ensure that the template lookup (see {@link TemplateLookupStrategy})
  * happens on a consistent view (a snapshot) of the backing storage, if the backing storage mechanism supports such
  * thing.
@@ -56,7 +56,7 @@ public interface TemplateLoaderSession {
      * closing the {@link Reader} or {@link InputStream} has thrown an exception, the caller should just proceed with
      * closing the session regardless. After {@link #close()} was called on the session, the methods of the
      * {@link Reader} or {@link InputStream} is allowed to throw an exception, or behave in any other erratic way.
-     * (Because the caller of this interface is usually FreeMarker (the {@link TemplateCache}), normally you don't have
+     * (Because the caller of this interface is usually FreeMarker (the {@link DefaultTemplateResolver}), normally you don't have
      * to deal with these rules, but it's useful to know the expectations if you implement
      * {@link TemplateLoaderSession}.)
      * 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/94d39312/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResult.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResult.java b/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResult.java
index d27dede..426e050 100644
--- a/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResult.java
+++ b/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingResult.java
@@ -124,7 +124,7 @@ public final class TemplateLoadingResult {
      * Returns non-{@code null} exactly if {@link #getStatus()} is {@link TemplateLoadingResultStatus#OPENED} and the
      * backing store mechanism returns content as {@code byte}-s, as opposed to as {@code chars}-s. See also
      * {@link #TemplateLoadingResult(TemplateLoadingSource, Serializable, InputStream, TemplateConfiguration)}. It's the
-     * responsibility of the caller (which is {@link TemplateCache} usually) to {@code close()} the {@link InputStream}.
+     * responsibility of the caller (which is {@link DefaultTemplateResolver} usually) to {@code close()} the {@link InputStream}.
      * The return value is always the same instance, no mater when and how many times this method is called.
      * 
      * <p>
@@ -173,7 +173,7 @@ public final class TemplateLoadingResult {
      * Returns non-{@code null} exactly if {@link #getStatus()} is {@link TemplateLoadingResultStatus#OPENED} and the
      * backing store mechanism returns content as {@code char}-s, as opposed to as {@code byte}-s. See also
      * {@link #TemplateLoadingResult(TemplateLoadingSource, Serializable, Reader, TemplateConfiguration)}. It's the
-     * responsibility of the caller (which is {@link TemplateCache} usually) to {@code close()} the {@link Reader}. The
+     * responsibility of the caller (which is {@link DefaultTemplateResolver} usually) to {@code close()} the {@link Reader}. The
      * return value is always the same instance, no mater when and how many times this method is called.
      * 
      * <p>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/94d39312/src/main/java/org/apache/freemarker/core/templateresolver/TemplateNameFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/TemplateNameFormat.java b/src/main/java/org/apache/freemarker/core/templateresolver/TemplateNameFormat.java
index 423492b..5b68241 100644
--- a/src/main/java/org/apache/freemarker/core/templateresolver/TemplateNameFormat.java
+++ b/src/main/java/org/apache/freemarker/core/templateresolver/TemplateNameFormat.java
@@ -99,40 +99,12 @@ public abstract class TemplateNameFormat {
     public static final TemplateNameFormat DEFAULT_2_4_0 = new Default020400();
     
     /**
-     * Converts a name to a template root directory based name, so that it can be used to find a template without
-     * knowing what (like which template) has referred to it. The rules depend on the name format, but a typical example
-     * is converting "t.ftl" with base "sub/contex.ftl" to "sub/t.ftl".
-     * 
-     * @param baseName
-     *            Maybe a file name, maybe a directory name. The meaning of file name VS directory name depends on the
-     *            name format, but typically, something like "foo/bar/" is a directory name, and something like
-     *            "foo/bar" is a file name, and thus in the last case the effective base is "foo/" (i.e., the directory
-     *            that contains the file). Not {@code null}.
-     * @param targetName
-     *            The name to convert. This usually comes from a template that refers to another template by name. It
-     *            can be a relative name, or an absolute name. (In typical name formats absolute names start with
-     *            {@code "/"} or maybe with an URL scheme, and all others are relative). Not {@code null}.
-     * 
-     * @return The path in template root directory relative format, or even an absolute name (where the root directory
-     *         is not the real root directory of the file system, but the imaginary directory that exists to store the
-     *         templates). The standard implementations shipped with FreeMarker always return a root relative path
-     *         (except if the name starts with an URI schema, in which case a full URI is returned).
+     * Implements {@link TemplateResolver#toRootBasedName(String, String)}; see more there.
      */
     abstract String toRootBasedName(String baseName, String targetName) throws MalformedTemplateNameException;
     
     /**
-     * Normalizes a template root directory based name (relative to the root or absolute), so that equivalent names
-     * become equivalent according {@link String#equals(Object)} too. The rules depend on the name format, but typical
-     * examples are "sub/../t.ftl" to "t.ftl", "sub/./t.ftl" to "sub/t.ftl" and "/t.ftl" to "t.ftl".
-     * 
-     * <p>The standard implementations shipped with FreeMarker always returns a root relative path
-     * (except if the name starts with an URI schema, in which case a full URI is returned), for example, "/foo.ftl"
-     * becomes to "foo.ftl".
-     * 
-     * @param name
-     *            The root based name. Not {@code null}.
-     * 
-     * @return The normalized root based name. Not {@code null}.
+     * Implements {@link TemplateResolver#normalizeRootBasedName(String)}; see more there.
      */
     abstract String normalizeRootBasedName(String name) throws MalformedTemplateNameException;
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/94d39312/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolver.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolver.java b/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolver.java
new file mode 100644
index 0000000..6253354
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolver.java
@@ -0,0 +1,165 @@
+/*
+ * 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 org.apache.freemarker.core.templateresolver;
+
+import java.io.IOException;
+import java.util.Locale;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.Template;
+import org.apache.freemarker.core.TemplateNotFoundException;
+import org.apache.freemarker.core.ast.ParseException;
+
+/**
+ * This class was introduced to allow users to fully implement the template lookup, loading and caching logic,
+ * in case the standard mechanism ({@link DefaultTemplateResolver}) is not flexible enough. By implementing this class,
+ * you can take over the duty of the following {@link Configuration} settings, and it's up to the implementation if you
+ * delegate some of those duties back to the {@link Configuration} setting:
+ * 
+ * <ul>
+ * <li>{@link Configuration#setTemplateLoader(TemplateLoader) template_loader}
+ * <li>{@link Configuration#setTemplateNameFormat(TemplateNameFormat) template_name_format}
+ * <li>{@link Configuration#setTemplateLookupStrategy(TemplateLookupStrategy) template_lookup_strategy}
+ * <li>{@link Configuration#setCacheStorage(CacheStorage) cache_storage}
+ * </ul>
+ * 
+ * @since 3.0.0
+ */
+//TODO DRAFT only [FM3]
+public abstract class TemplateResolver {
+
+    private final Configuration configuration;
+
+    protected TemplateResolver(Configuration configuration) {
+        this.configuration = configuration;
+    }
+
+    public Configuration getConfiguration() {
+        return configuration;
+    }
+
+    /**
+     * Retrieves the parsed template with the given name (and according the specified further parameters), or returns a
+     * result that indicates that no such template exists. The result should come from a cache most of the time
+     * (avoiding I/O and template parsing), as this method is typically called frequently.
+     * 
+     * <p>
+     * All parameters must be non-{@code null}, except {@code customLookupCondition}. For the meaning of the parameters
+     * see {@link Configuration#getTemplate(String, Locale, String, boolean)}.
+     *
+     * @return A {@link GetTemplateResult} object that contains the {@link Template}, or a
+     *         {@link GetTemplateResult} object that contains {@code null} as the {@link Template} and information
+     *         about the missing template. The return value itself is never {@code null}. Note that exceptions occurring
+     *         during template loading mustn't be treated as a missing template, they must cause an exception to be
+     *         thrown by this method instead of returning a {@link GetTemplateResult}. The idea is that having a
+     *         missing template is normal (not exceptional), because of how some lookup strategies work. That the
+     *         backing storage mechanism should indeed check that it's missing though, and not cover an error as such.
+     * 
+     * @throws MalformedTemplateNameException
+     *             If the {@code name} was malformed. This is certainly originally thrown by
+     *             {@link #normalizeRootBasedName(String)}; see more there.
+     * 
+     * @throws IOException
+     *             If reading the template has failed from a reason other than the template is missing. This method
+     *             should never be a {@link TemplateNotFoundException}, as that condition is indicated in the return
+     *             value.
+     */
+    // [FM3] These parameters will certainly be removed: String suggestedEncoding, boolean parseAsFTL
+    public abstract GetTemplateResult getTemplate(String name, Locale locale, Object customLookupCondition,
+            String encoding, boolean parseAsFTL)
+            throws MalformedTemplateNameException, ParseException, IOException;
+
+    /**
+     * Clears the cache of templates, to enforce re-loading templates when they are get next time; this is an optional
+     * operation.
+     * 
+     * <p>
+     * Note that if the {@link TemplateResolver} implementation uses {@link TemplateLoader}-s, it should also call
+     * {@link TemplateLoader#resetState()} on them.
+     * 
+     * <p>
+     * This method is thread-safe and can be called while the engine processes templates.
+     * 
+     * @throw {@link UnsupportedOperationException} If the {@link TemplateResolver} implementation doesn't support this
+     *        operation.
+     */
+    public abstract void clearTemplateCache();
+
+    /**
+     * Removes a template from the template cache, hence forcing the re-loading of it when it's next time requested;
+     * this is an optional operation. This is to give the application finer control over cache updating than
+     * {@link Configuration#setTemplateUpdateDelay(int)} alone does.
+     * 
+     * <p>
+     * For the meaning of the parameters, see {@link #getTemplate(String, Locale, Object, String, boolean)}
+     * 
+     * <p>
+     * This method is thread-safe and can be called while the engine processes templates.
+     * 
+     * @throw {@link UnsupportedOperationException} If the {@link TemplateResolver} implementation doesn't support this
+     *        operation.
+     */
+    public abstract void removeTemplateFromCache(String name, Locale locale, String encoding, boolean parse)
+            throws IOException;
+
+    /**
+     * Converts a name to a template root directory based name, so that it can be used to find a template without
+     * knowing what (like which template) has referred to it. The rules depend on the name format, but a typical example
+     * is converting "t.ftl" with base "sub/contex.ftl" to "sub/t.ftl".
+     * 
+     * <p>
+     * Some implementations, notably {@link DefaultTemplateResolver}, delegates this check to the
+     * {@link TemplateNameFormat} coming from the {@link Configuration}.
+     * 
+     * @param baseName
+     *            Maybe a file name, maybe a directory name. The meaning of file name VS directory name depends on the
+     *            name format, but typically, something like "foo/bar/" is a directory name, and something like
+     *            "foo/bar" is a file name, and thus in the last case the effective base is "foo/" (i.e., the directory
+     *            that contains the file). Not {@code null}.
+     * @param targetName
+     *            The name to convert. This usually comes from a template that refers to another template by name. It
+     *            can be a relative name, or an absolute name. (In typical name formats absolute names start with
+     *            {@code "/"} or maybe with an URL scheme, and all others are relative). Not {@code null}.
+     * 
+     * @return The path in template root directory relative format, or even an absolute name (where the root directory
+     *         is not the real root directory of the file system, but the imaginary directory that exists to store the
+     *         templates). The standard implementations shipped with FreeMarker always return a root relative path
+     *         (except if the name starts with an URI schema, in which case a full URI is returned).
+     */
+    public abstract String toRootBasedName(String baseName, String targetName) throws MalformedTemplateNameException;
+
+    /**
+     * Normalizes a template root directory based name (relative to the root or absolute), so that equivalent names
+     * become equivalent according {@link String#equals(Object)} too. The rules depend on the name format, but typical
+     * examples are "sub/../t.ftl" to "t.ftl", "sub/./t.ftl" to "sub/t.ftl" and "/t.ftl" to "t.ftl".
+     * 
+     * <p>
+     * Some implementations, notably {@link DefaultTemplateResolver}, delegates this check to the {@link TemplateNameFormat}
+     * coming from the {@link Configuration}. The standard {@link TemplateNameFormat} implementations shipped with
+     * FreeMarker always returns a root relative path (except if the name starts with an URI schema, in which case a
+     * full URI is returned), for example, "/foo.ftl" becomes to "foo.ftl".
+     * 
+     * @param name
+     *            The root based name. Not {@code null}.
+     * 
+     * @return The normalized root based name. Not {@code null}.
+     */
+    public abstract String normalizeRootBasedName(String name) throws MalformedTemplateNameException;
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/94d39312/src/manual/en_US/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/src/manual/en_US/FM3-CHANGE-LOG.txt b/src/manual/en_US/FM3-CHANGE-LOG.txt
index 2547d70..e75b945 100644
--- a/src/manual/en_US/FM3-CHANGE-LOG.txt
+++ b/src/manual/en_US/FM3-CHANGE-LOG.txt
@@ -77,4 +77,7 @@ the FreeMarer 3 changelog here:
   write protected (non-configurable). Also now they come from the pool that ObjectWrapper builders use.
 - WrappingTemplateModel.objectWrapper is now final, and its statically stored default value can't be set anymore.
 - Removed SimpleObjectWrapper deprecated paramerless constructor
-- Removed ResourceBundleLocalizedString and LocalizedString: Hardly anybody has discovered these, and they had no JUnit coverage.
\ No newline at end of file
+- Removed ResourceBundleLocalizedString and LocalizedString: Hardly anybody has discovered these, and they had no JUnit coverage.
+- Added early draft of TemplateResolver, renamed TemplateCache to DefaultTemplateResolver. TemplateResolver is not
+  yet directly used in Configuration. This was only added in a hurry, so that it's visible why the
+  o.a.f.core.templateresolver subpackage name makes sense.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/94d39312/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/ConfigurationTest.java b/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
index 6688d17..6e14cc3 100644
--- a/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
+++ b/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
@@ -81,7 +81,7 @@ import org.apache.freemarker.core.templateresolver.NullCacheStorage;
 import org.apache.freemarker.core.templateresolver.SoftCacheStorage;
 import org.apache.freemarker.core.templateresolver.StringTemplateLoader;
 import org.apache.freemarker.core.templateresolver.StrongCacheStorage;
-import org.apache.freemarker.core.templateresolver.TemplateCache;
+import org.apache.freemarker.core.templateresolver.DefaultTemplateResolver;
 import org.apache.freemarker.core.templateresolver.TemplateLookupContext;
 import org.apache.freemarker.core.templateresolver.TemplateLookupResult;
 import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy;
@@ -1263,7 +1263,7 @@ public class ConfigurationTest extends TestCase {
     public void testTemplateUpdateDelay() throws IOException, TemplateException {
         Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
 
-        assertEquals(TemplateCache.DEFAULT_TEMPLATE_UPDATE_DELAY_MILLIS, cfg.getTemplateUpdateDelayMilliseconds());
+        assertEquals(DefaultTemplateResolver.DEFAULT_TEMPLATE_UPDATE_DELAY_MILLIS, cfg.getTemplateUpdateDelayMilliseconds());
         
         cfg.setTemplateUpdateDelay(4);
         assertEquals(4000L, cfg.getTemplateUpdateDelayMilliseconds());


Mime
View raw message