blob: 07ee50161655095898f9b1ca00d50dfb31e3b24b [file] [log] [blame]
/*
* Copyright (c) 2003 The Visigoth Software Society. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowledgement:
* "This product includes software developed by the
* Visigoth Software Society (http://www.visigoths.org/)."
* Alternately, this acknowledgement may appear in the software itself,
* if and wherever such third-party acknowledgements normally appear.
*
* 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
* project contributors may be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact visigoths@visigoths.org.
*
* 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
* nor may "FreeMarker" or "Visigoth" appear in their names
* without prior written permission of the Visigoth Software Society.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Visigoth Software Society. For more
* information on the Visigoth Software Society, please see
* http://www.visigoths.org/
*/
package freemarker.ext.servlet;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.Locale;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.cache.WebappTemplateLoader;
import freemarker.core.Configurable;
import freemarker.ext.jsp.TaglibFactory;
import freemarker.log.Logger;
import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.utility.StringUtil;
/**
* <p>This is a general-purpose FreeMarker view servlet.</p>
*
* <p>The main features are:
*
* <ul>
*
* <li>It makes all request, request parameters, session, and servlet
* context attributes available to templates through <code>Request</code>,
* <code>RequestParameters</code>, <code>Session</code>, and <code>Application</code>
* variables.
*
* <li>The scope variables are also available via automatic scope discovery. That is,
* writing <code>Application.attrName</code>, <code>Session.attrName</code>,
* <code>Request.attrName</code> is not mandatory; it's enough to write <code>attrName</code>,
* and if no such variable was created in the template, it will search the
* variable in <code>Request</code>, and then in <code>Session</code>,
* and finally in <code>Application</code>.
*
* <li>It creates a variable with name <code>JspTaglibs</code>, that can be used
* to load JSP taglibs. For example:<br>
* <code>&lt;#assign tiles=JspTaglibs["/WEB-INF/struts-tiles.tld"]></code>
*
* <li>A custom directive named <code>include_page</code> allows you to
* include the output of another servlet resource from your servlet container,
* just as if you used <code>ServletRequest.getRequestDispatcher(path).include()</code>:<br>
* <code>&lt;@include_page path="/myWebapp/somePage.jsp"/></code>. You can also
* pass parameters to the newly included page by passing a hash named 'params':
* <code>&lt;@include_page path="/myWebapp/somePage.jsp" params={lang: "en", q="5"}/></code>.
* By default, the request parameters of the original request (the one being
* processed by FreemarkerServlet) are also inherited by the include. You can
* explicitly control this inheritance using the 'inherit_params' parameter:
* <code>&lt;@include_page path="/myWebapp/somePage.jsp" params={lang: "en", q="5"} inherit_params=false/></code>.
* </ul>
*
* <p>The servlet will rethrow the errors occurring during template processing,
* wrapped into <code>ServletException</code>, except if the class name of the
* class set by the <code>template_exception_handler</code> contains the
* substring <code>"Debug"</code>. If it contains <code>"Debug"</code>, then it
* is assumed that the template exception handler prints the error message to the
* page, thus <code>FreemarkerServlet</code> will suppress the exception, and
* logs the problem with the servlet logger
* (<code>javax.servlet.GenericServlet.log(...)</code>).
*
* <p>Supported init-params are:</p>
*
* <ul>
*
* <li><strong>TemplatePath</strong> specifies the location of the templates.
* By default, this is interpreted as web application directory relative URI.<br>
* Alternatively, you can prepend it with <tt>file://</tt> to indicate a literal
* path in the file system (i.e. <tt>file:///var/www/project/templates/</tt>).
* Note that three slashes were used to specify an absolute path.<br>
* Also, you can prepend it with <tt>class://path/to/template/package</tt> to
* indicate that you want to load templates from the specified package
* accessible through the classloader of the servlet.<br>
* Default value is <tt>class://</tt> (that is, the root of the class hierarchy).
* <i>Note that this default is different than the default in FreeMarker 1.x, when
* it defaulted <tt>/</tt>, that is to loading from the webapp root directory.</i></li>
*
* <li><strong>NoCache</strong> if set to true, generates headers in the response
* that advise the HTTP client not to cache the returned page.
* The default is <tt>false</tt>.</li>
*
* <li><strong>ContentType</strong> if specified, response uses the specified
* Content-type HTTP header. The value may include the charset (e.g.
* <tt>"text/html; charset=ISO-8859-1"</tt>). If not specified, <tt>"text/html"</tt>
* is used. If the charset is not specified in this init-param, then the charset
* (encoding) of the actual template file will be used (in the response HTTP header
* and for encoding the output stream). Note that this setting can be overridden
* on a per-template basis by specifying a custom attribute named
* <tt>content_type</tt> in the <tt>attributes</tt> parameter of the
* <tt>&lt;#ftl></tt> directive.
* </li>
*
* <li>The following init-params are supported only for backward compatibility, and
* their usage is discouraged: TemplateUpdateInterval, DefaultEncoding,
* ObjectWrapper, TemplateExceptionHandler. Use setting init-params such as
* object_wrapper instead.
*
* <li>Any other init-param will be interpreted as <code>Configuration</code>
* level setting. See {@link Configuration#setSetting}</li>
*
* </ul>
*
* @author Attila Szegedi
*/
public class FreemarkerServlet extends HttpServlet
{
private static final Logger logger = Logger.getLogger("freemarker.servlet");
public static final long serialVersionUID = -2440216393145762479L;
private static final String INITPARAM_TEMPLATE_PATH = "TemplatePath";
private static final String INITPARAM_NOCACHE = "NoCache";
private static final String INITPARAM_CONTENT_TYPE = "ContentType";
private static final String DEFAULT_CONTENT_TYPE = "text/html";
private static final String INITPARAM_DEBUG = "Debug";
private static final String DEPR_INITPARAM_TEMPLATE_DELAY = "TemplateDelay";
private static final String DEPR_INITPARAM_ENCODING = "DefaultEncoding";
private static final String DEPR_INITPARAM_OBJECT_WRAPPER = "ObjectWrapper";
private static final String DEPR_INITPARAM_WRAPPER_SIMPLE = "simple";
private static final String DEPR_INITPARAM_WRAPPER_BEANS = "beans";
private static final String DEPR_INITPARAM_WRAPPER_JYTHON = "jython";
private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER = "TemplateExceptionHandler";
private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_RETHROW = "rethrow";
private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_DEBUG = "debug";
private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_HTML_DEBUG = "htmlDebug";
private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_IGNORE = "ignore";
private static final String DEPR_INITPARAM_DEBUG = "debug";
public static final String KEY_REQUEST = "Request";
public static final String KEY_INCLUDE = "include_page";
public static final String KEY_REQUEST_PRIVATE = "__FreeMarkerServlet.Request__";
public static final String KEY_REQUEST_PARAMETERS = "RequestParameters";
public static final String KEY_SESSION = "Session";
public static final String KEY_APPLICATION = "Application";
public static final String KEY_APPLICATION_PRIVATE = "__FreeMarkerServlet.Application__";
public static final String KEY_JSP_TAGLIBS = "JspTaglibs";
// Note these names start with dot, so they're essentially invisible from
// a freemarker script.
private static final String ATTR_REQUEST_MODEL = ".freemarker.Request";
private static final String ATTR_REQUEST_PARAMETERS_MODEL =
".freemarker.RequestParameters";
private static final String ATTR_SESSION_MODEL = ".freemarker.Session";
private static final String ATTR_APPLICATION_MODEL =
".freemarker.Application";
private static final String ATTR_JSP_TAGLIBS_MODEL =
".freemarker.JspTaglibs";
private static final String EXPIRATION_DATE;
static {
// Generate expiration date that is one year from now in the past
GregorianCalendar expiration = new GregorianCalendar();
expiration.roll(Calendar.YEAR, -1);
SimpleDateFormat httpDate =
new SimpleDateFormat(
"EEE, dd MMM yyyy HH:mm:ss z",
java.util.Locale.US);
EXPIRATION_DATE = httpDate.format(expiration.getTime());
}
private String templatePath;
private boolean nocache;
protected boolean debug;
private Configuration config;
private ObjectWrapper wrapper;
private String contentType;
private boolean noCharsetInContentType;
public void init() throws ServletException {
try {
config = createConfiguration();
// Set defaults:
config.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
contentType = DEFAULT_CONTENT_TYPE;
// Process object_wrapper init-param out of order:
wrapper = createObjectWrapper();
if (logger.isDebugEnabled()) {
logger.debug("Using object wrapper of class " + wrapper.getClass().getName());
}
config.setObjectWrapper(wrapper);
// Process TemplatePath init-param out of order:
templatePath = getInitParameter(INITPARAM_TEMPLATE_PATH);
if (templatePath == null)
templatePath = "class://";
config.setTemplateLoader(createTemplateLoader(templatePath));
// Process all other init-params:
Enumeration initpnames = getServletConfig().getInitParameterNames();
while (initpnames.hasMoreElements()) {
String name = (String) initpnames.nextElement();
String value = getInitParameter(name);
if (name == null) {
throw new ServletException(
"init-param without param-name. "
+ "Maybe the web.xml is not well-formed?");
}
if (value == null) {
throw new ServletException(
"init-param without param-value. "
+ "Maybe the web.xml is not well-formed?");
}
if (name.equals(DEPR_INITPARAM_OBJECT_WRAPPER)
|| name.equals(Configurable.OBJECT_WRAPPER_KEY)
|| name.equals(INITPARAM_TEMPLATE_PATH)) {
// ignore: we have already processed these
} else if (name.equals(DEPR_INITPARAM_ENCODING)) { // BC
if (getInitParameter(Configuration.DEFAULT_ENCODING_KEY) != null) {
throw new ServletException(
"Conflicting init-params: "
+ Configuration.DEFAULT_ENCODING_KEY + " and "
+ DEPR_INITPARAM_ENCODING);
}
config.setDefaultEncoding(value);
} else if (name.equals(DEPR_INITPARAM_TEMPLATE_DELAY)) { // BC
if (getInitParameter(Configuration.TEMPLATE_UPDATE_DELAY_KEY) != null) {
throw new ServletException(
"Conflicting init-params: "
+ Configuration.TEMPLATE_UPDATE_DELAY_KEY + " and "
+ DEPR_INITPARAM_TEMPLATE_DELAY);
}
try {
config.setTemplateUpdateDelay(Integer.parseInt(value));
} catch (NumberFormatException e) {
// Intentionally ignored
}
} else if (name.equals(DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER)) { // BC
if (getInitParameter(Configurable.TEMPLATE_EXCEPTION_HANDLER_KEY) != null) {
throw new ServletException(
"Conflicting init-params: "
+ Configurable.TEMPLATE_EXCEPTION_HANDLER_KEY + " and "
+ DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER);
}
if (DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_RETHROW.equals(value)) {
config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
} else if (DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_DEBUG.equals(value)) {
config.setTemplateExceptionHandler(TemplateExceptionHandler.DEBUG_HANDLER);
} else if (DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_HTML_DEBUG.equals(value)) {
config.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
} else if (DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_IGNORE.equals(value)) {
config.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
} else {
throw new ServletException(
"Invalid value for servlet init-param "
+ DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER + ": " + value);
}
} else if (name.equals(INITPARAM_NOCACHE)) {
nocache = StringUtil.getYesNo(value);
} else if (name.equals(DEPR_INITPARAM_DEBUG)) { // BC
if (getInitParameter(INITPARAM_DEBUG) != null) {
throw new ServletException(
"Conflicting init-params: "
+ INITPARAM_DEBUG + " and "
+ DEPR_INITPARAM_DEBUG);
}
debug = StringUtil.getYesNo(value);
} else if (name.equals(INITPARAM_DEBUG)) {
debug = StringUtil.getYesNo(value);
} else if (name.equals(INITPARAM_CONTENT_TYPE)) {
contentType = value;
} else {
config.setSetting(name, value);
}
} // while initpnames
noCharsetInContentType = true;
int i = contentType.toLowerCase().indexOf("charset=");
if (i != -1) {
char c = ' ';
i--;
while (i >= 0) {
c = contentType.charAt(i);
if (!Character.isWhitespace(c)) break;
i--;
}
if (i == -1 || c == ';') {
noCharsetInContentType = false;
}
}
} catch (ServletException e) {
throw e;
} catch (Exception e) {
throw new ServletException(e);
}
}
/**
* Create the template loader. The default implementation will create a
* {@link ClassTemplateLoader} if the template path starts with "class://",
* a {@link FileTemplateLoader} if the template path starts with "file://",
* and a {@link WebappTemplateLoader} otherwise.
* @param templatePath the template path to create a loader for
* @return a newly created template loader
* @throws IOException
*/
protected TemplateLoader createTemplateLoader(String templatePath) throws IOException
{
if (templatePath.startsWith("class://")) {
// substring(7) is intentional as we "reuse" the last slash
return new ClassTemplateLoader(getClass(), templatePath.substring(7));
} else {
if (templatePath.startsWith("file://")) {
templatePath = templatePath.substring(7);
return new FileTemplateLoader(new File(templatePath));
} else {
return new WebappTemplateLoader(this.getServletContext(), templatePath);
}
}
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
process(request, response);
}
public void doPost(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
process(request, response);
}
private void process(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// Give chance to subclasses to perform preprocessing
if (preprocessRequest(request, response)) {
return;
}
String path = requestUrlToTemplatePath(request);
if (debug) {
log("Requested template: " + StringUtil.jQuoteNoXSS(path));
}
Template template = null;
try {
template = config.getTemplate(
path,
deduceLocale(path, request, response));
} catch (FileNotFoundException e) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
Object attrContentType = template.getCustomAttribute("content_type");
if(attrContentType != null) {
response.setContentType(attrContentType.toString());
}
else {
if (noCharsetInContentType) {
response.setContentType(
contentType + "; charset=" + template.getEncoding());
} else {
response.setContentType(contentType);
}
}
// Set cache policy
setBrowserCachingPolicy(response);
ServletContext servletContext = getServletContext();
try {
TemplateModel model = createModel(wrapper, servletContext, request, response);
// Give subclasses a chance to hook into preprocessing
if (preTemplateProcess(request, response, template, model)) {
try {
// Process the template
template.process(model, response.getWriter());
} finally {
// Give subclasses a chance to hook into postprocessing
postTemplateProcess(request, response, template, model);
}
}
} catch (TemplateException te) {
if (config.getTemplateExceptionHandler()
.getClass().getName().indexOf("Debug") != -1) {
this.log("Error executing FreeMarker template", te);
} else {
ServletException e = new ServletException(
"Error executing FreeMarker template", te);
// Attempt to set init cause, but don't freak out if the method
// is not available (i.e. pre-1.4 JRE). This is required as the
// constructor-passed throwable won't show up automatically in
// stack traces.
try {
e.getClass().getMethod("initCause",
new Class[] { Throwable.class }).invoke(e,
new Object[] { te });
} catch (Exception ex) {
// Can't set init cause, we're probably running on a pre-1.4
// JDK, oh well...
}
throw e;
}
}
}
/**
* Returns the locale used for the
* {@link Configuration#getTemplate(String, Locale)} call.
* The base implementation simply returns the locale setting of the
* configuration. Override this method to provide different behaviour, i.e.
* to use the locale indicated in the request.
*/
protected Locale deduceLocale(
String templatePath, HttpServletRequest request, HttpServletResponse response) {
return config.getLocale();
}
protected TemplateModel createModel(ObjectWrapper wrapper,
ServletContext servletContext,
final HttpServletRequest request,
final HttpServletResponse response) throws TemplateModelException {
try {
AllHttpScopesHashModel params = new AllHttpScopesHashModel(wrapper, servletContext, request);
// Create hash model wrapper for servlet context (the application)
ServletContextHashModel servletContextModel =
(ServletContextHashModel) servletContext.getAttribute(
ATTR_APPLICATION_MODEL);
if (servletContextModel == null)
{
servletContextModel = new ServletContextHashModel(this, wrapper);
servletContext.setAttribute(
ATTR_APPLICATION_MODEL,
servletContextModel);
TaglibFactory taglibs = new TaglibFactory(servletContext);
servletContext.setAttribute(
ATTR_JSP_TAGLIBS_MODEL,
taglibs);
initializeServletContext(request, response);
}
params.putUnlistedModel(KEY_APPLICATION, servletContextModel);
params.putUnlistedModel(KEY_APPLICATION_PRIVATE, servletContextModel);
params.putUnlistedModel(KEY_JSP_TAGLIBS, (TemplateModel)servletContext.getAttribute(ATTR_JSP_TAGLIBS_MODEL));
// Create hash model wrapper for session
HttpSessionHashModel sessionModel;
HttpSession session = request.getSession(false);
if(session != null) {
sessionModel = (HttpSessionHashModel) session.getAttribute(ATTR_SESSION_MODEL);
if (sessionModel == null || sessionModel.isOrphaned(session)) {
sessionModel = new HttpSessionHashModel(session, wrapper);
initializeSessionAndInstallModel(request, response,
sessionModel, session);
}
}
else {
sessionModel = new HttpSessionHashModel(this, request, response, wrapper);
}
params.putUnlistedModel(KEY_SESSION, sessionModel);
// Create hash model wrapper for request
HttpRequestHashModel requestModel =
(HttpRequestHashModel) request.getAttribute(ATTR_REQUEST_MODEL);
if (requestModel == null || requestModel.getRequest() != request)
{
requestModel = new HttpRequestHashModel(request, response, wrapper);
request.setAttribute(ATTR_REQUEST_MODEL, requestModel);
request.setAttribute(
ATTR_REQUEST_PARAMETERS_MODEL,
createRequestParametersHashModel(request));
}
params.putUnlistedModel(KEY_REQUEST, requestModel);
params.putUnlistedModel(KEY_INCLUDE, new IncludePage(request, response));
params.putUnlistedModel(KEY_REQUEST_PRIVATE, requestModel);
// Create hash model wrapper for request parameters
HttpRequestParametersHashModel requestParametersModel =
(HttpRequestParametersHashModel) request.getAttribute(
ATTR_REQUEST_PARAMETERS_MODEL);
params.putUnlistedModel(KEY_REQUEST_PARAMETERS, requestParametersModel);
return params;
} catch (ServletException e) {
throw new TemplateModelException(e);
} catch (IOException e) {
throw new TemplateModelException(e);
}
}
void initializeSessionAndInstallModel(HttpServletRequest request,
HttpServletResponse response, HttpSessionHashModel sessionModel,
HttpSession session)
throws ServletException, IOException
{
session.setAttribute(ATTR_SESSION_MODEL, sessionModel);
initializeSession(request, response);
}
/**
* Maps the request URL to a template path that is passed to
* {@link Configuration#getTemplate(String, Locale)}. You can override it
* (i.e. to provide advanced rewriting capabilities), but you are strongly
* encouraged to call the overridden method first, then only modify its
* return value.
* @param request the currently processed request
* @return a String representing the template path
*/
protected String requestUrlToTemplatePath(HttpServletRequest request)
{
// First, see if it's an included request
String includeServletPath = (String) request.getAttribute("javax.servlet.include.servlet_path");
if(includeServletPath != null)
{
// Try path info; only if that's null (servlet is mapped to an
// URL extension instead of to prefix) use servlet path.
String includePathInfo = (String) request.getAttribute("javax.servlet.include.path_info");
return includePathInfo == null ? includeServletPath : includePathInfo;
}
// Seems that the servlet was not called as the result of a
// RequestDispatcher.include(...). Try pathInfo then servletPath again,
// only now directly on the request object:
String path = request.getPathInfo();
if (path != null) return path;
path = request.getServletPath();
if (path != null) return path;
// Seems that it's a servlet mapped with prefix, and there was no extra path info.
return "";
}
/**
* Called as the first step in request processing, before the templating mechanism
* is put to work. By default does nothing and returns false. This method is
* typically overridden to manage serving of non-template resources (i.e. images)
* that reside in the template directory.
* @param request the HTTP request
* @param response the HTTP response
* @return true to indicate this method has processed the request entirely,
* and that the further request processing should not take place.
*/
protected boolean preprocessRequest(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
return false;
}
/**
* This method is called from {@link #init()} to create the
* FreeMarker configuration object that this servlet will use
* for template loading. This is a hook that allows you
* to custom-configure the configuration object in a subclass.
* The default implementation returns a new {@link Configuration}
* instance.
*/
protected Configuration createConfiguration() {
return new Configuration();
}
/**
* This method is called from {@link #init()} to create the
* FreeMarker object wrapper object that this servlet will use
* for adapting request, session, and servlet context attributes into
* template models.. This is a hook that allows you
* to custom-configure the wrapper object in a subclass.
* The default implementation returns a wrapper that depends on the value
* of <code>ObjectWrapper</code> init parameter. If <code>simple</code> is
* specified, {@link ObjectWrapper#SIMPLE_WRAPPER} is used; if <code>jython</code>
* is specified, {@link freemarker.ext.jython.JythonWrapper} is used. In
* every other case {@link ObjectWrapper#DEFAULT_WRAPPER} is used.
*/
protected ObjectWrapper createObjectWrapper() {
String wrapper = getServletConfig().getInitParameter(DEPR_INITPARAM_OBJECT_WRAPPER);
if (wrapper != null) { // BC
if (getInitParameter(Configurable.OBJECT_WRAPPER_KEY) != null) {
throw new RuntimeException("Conflicting init-params: "
+ Configurable.OBJECT_WRAPPER_KEY + " and "
+ DEPR_INITPARAM_OBJECT_WRAPPER);
}
if (DEPR_INITPARAM_WRAPPER_BEANS.equals(wrapper)) {
return ObjectWrapper.BEANS_WRAPPER;
}
if(DEPR_INITPARAM_WRAPPER_SIMPLE.equals(wrapper)) {
return ObjectWrapper.SIMPLE_WRAPPER;
}
if(DEPR_INITPARAM_WRAPPER_JYTHON.equals(wrapper)) {
// Avoiding compile-time dependency on Jython package
try {
return (ObjectWrapper) Class.forName("freemarker.ext.jython.JythonWrapper")
.newInstance();
} catch (InstantiationException e) {
throw new InstantiationError(e.getMessage());
} catch (IllegalAccessException e) {
throw new IllegalAccessError(e.getMessage());
} catch (ClassNotFoundException e) {
throw new NoClassDefFoundError(e.getMessage());
}
}
// return BeansWrapper.getDefaultInstance();
return ObjectWrapper.DEFAULT_WRAPPER;
} else {
wrapper = getInitParameter(Configurable.OBJECT_WRAPPER_KEY);
if (wrapper == null) {
// return BeansWrapper.getDefaultInstance();
return ObjectWrapper.DEFAULT_WRAPPER;
} else {
try {
config.setSetting(Configurable.OBJECT_WRAPPER_KEY, wrapper);
} catch (TemplateException e) {
throw new RuntimeException(e.toString());
}
return config.getObjectWrapper();
}
}
}
protected ObjectWrapper getObjectWrapper() {
return wrapper;
}
protected final String getTemplatePath() {
return templatePath;
}
protected HttpRequestParametersHashModel createRequestParametersHashModel(HttpServletRequest request) {
return new HttpRequestParametersHashModel(request);
}
/**
* Called when servlet detects in a request processing that
* application-global (that is, ServletContext-specific) attributes are not yet
* set.
* This is a generic hook you might use in subclasses to perform a specific
* action on first request in the context. By default it does nothing.
* @param request the actual HTTP request
* @param response the actual HTTP response
*/
protected void initializeServletContext(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
}
/**
* Called when servlet detects in a request processing that session-global
* (that is, HttpSession-specific) attributes are not yet set.
* This is a generic hook you might use in subclasses to perform a specific
* action on first request in the session. By default it does nothing. It
* is only invoked on newly created sessions; it's not invoked when a
* replicated session is reinstantiated in another servlet container.
*
* @param request the actual HTTP request
* @param response the actual HTTP response
*/
protected void initializeSession(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
}
/**
* Called before the execution is passed to template.process().
* This is a generic hook you might use in subclasses to perform a specific
* action before the template is processed. By default does nothing.
* A typical action to perform here is to inject application-specific
* objects into the model root
*
* <p>Example: Expose the Serlvet context path as "baseDir" for all templates:
*
*<pre>
* ((SimpleHash) data).put("baseDir", request.getContextPath() + "/");
* return true;
*</pre>
*
* @param request the actual HTTP request
* @param response the actual HTTP response
* @param template the template that will get executed
* @param data the data that will be passed to the template. By default this will be
* an {@link AllHttpScopesHashModel} (which is a {@link freemarker.template.SimpleHash} subclass).
* Thus, you can add new variables to the data-model with the
* {@link freemarker.template.SimpleHash#put(String, Object)} subclass) method.
* @return true to process the template, false to suppress template processing.
*/
protected boolean preTemplateProcess(
HttpServletRequest request,
HttpServletResponse response,
Template template,
TemplateModel data)
throws ServletException, IOException
{
return true;
}
/**
* Called after the execution returns from template.process().
* This is a generic hook you might use in subclasses to perform a specific
* action after the template is processed. It will be invoked even if the
* template processing throws an exception. By default does nothing.
* @param request the actual HTTP request
* @param response the actual HTTP response
* @param template the template that was executed
* @param data the data that was passed to the template
*/
protected void postTemplateProcess(
HttpServletRequest request,
HttpServletResponse response,
Template template,
TemplateModel data)
throws ServletException, IOException
{
}
/**
* Returns the {@link freemarker.template.Configuration} object used by this servlet.
* Please don't forget that {@link freemarker.template.Configuration} is not thread-safe
* when you modify it.
*/
protected Configuration getConfiguration() {
return config;
}
/**
* If the parameter "nocache" was set to true, generate a set of headers
* that will advise the HTTP client not to cache the returned page.
*/
private void setBrowserCachingPolicy(HttpServletResponse res)
{
if (nocache)
{
// HTTP/1.1 + IE extensions
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, "
+ "post-check=0, pre-check=0");
// HTTP/1.0
res.setHeader("Pragma", "no-cache");
// Last resort for those that ignore all of the above
res.setHeader("Expires", EXPIRATION_DATE);
}
}
}