blob: aae02751402c5b52f6cb61e116012c86459a9b9e [file] [log] [blame]
/*
* 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.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import freemarker.template.Configuration;
/**
* This is an abstract template loader that can load templates whose location can be described by an URL. This
* superclass only works for cases where merely getting the URL immediately tells if the resource exists, not for cases
* where for example you had to check response headers to know that. The subclasses only need to override the
* {@link #getURL(String)} method.
*/
public abstract class URLTemplateLoader implements TemplateLoader {
private Boolean urlConnectionUsesCaches;
@Override
public Object findTemplateSource(String name)
throws IOException {
URL url = getURL(name);
return url == null ? null : new URLTemplateSource(url, getURLConnectionUsesCaches());
}
@Override
public long getLastModified(Object templateSource) {
return ((URLTemplateSource) templateSource).lastModified();
}
@Override
public Reader getReader(Object templateSource, String encoding)
throws IOException {
return new InputStreamReader(
((URLTemplateSource) templateSource).getInputStream(),
encoding);
}
@Override
public void closeTemplateSource(Object templateSource)
throws IOException {
((URLTemplateSource) templateSource).close();
}
/**
* Getter pair of {@link #setURLConnectionUsesCaches(Boolean)}.
*
* @since 2.3.21
*/
public Boolean getURLConnectionUsesCaches() {
return urlConnectionUsesCaches;
}
/**
* Sets if {@link URLConnection#setUseCaches(boolean)} will be called, and with what value. By default this is
* {@code null}; see the behavior then below. The recommended value is {@code false}, so that FreeMarker can always
* reliably detect when a template was changed. The default is {@code null} only for backward compatibility,
* and certainly will be changed to {@code false} in 2.4.0. As FreeMarker has its own template cache with its
* own update delay setting ({@link Configuration#setTemplateUpdateDelay(int)}), setting this to {@code false}
* shouldn't cause performance problems.
*
* <p>Regarding {@code null} value: By default then {@link URLConnection#setUseCaches(boolean)} won't be called,
* and so the default of the {@link URLConnection} subclass will be in effect (usually {@code true}). That's the
* 2.3.0-compatible mode. However, if {@link Configuration#getIncompatibleImprovements()} is at least 2.3.21, then
* when {@code Configuration.getTemplate} is used, {@code null} will mean {@code false}. Note that this 2.3.21 trick
* only works if the template is loaded through {@code Configuration.getTemplate} (or {@link TemplateCache}).
*
* @since 2.3.21
*/
public void setURLConnectionUsesCaches(Boolean urlConnectionUsesCaches) {
this.urlConnectionUsesCaches = urlConnectionUsesCaches;
}
/**
* Given a template name (plus potential locale decorations) retrieves
* an URL that points the template source.
* @param name the name of the sought template, including the locale
* decorations.
* @return an URL that points to the template source, or {@code null} if the template does not exist.
*/
protected abstract URL getURL(String name);
/**
* Can be used by subclasses to canonicalize URL path prefixes.
* @param prefix the path prefix to canonicalize
* @return the canonicalized prefix. All backslashes are replaced with
* forward slashes, and a trailing slash is appended if the original
* prefix wasn't empty and didn't already end with a slash.
*/
protected static String canonicalizePrefix(String prefix) {
// make it foolproof
prefix = prefix.replace('\\', '/');
// ensure there's a trailing slash
if (prefix.length() > 0 && !prefix.endsWith("/")) {
prefix += "/";
}
return prefix;
}
}