freemarker.ext.servlet.IncludePage Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of freemarker Show documentation
Show all versions of freemarker Show documentation
FreeMarker is a "template engine"; a generic tool to generate text output based on templates.
/*
* 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.ext.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import freemarker.core.Environment;
import freemarker.core._DelayedFTLTypeDescription;
import freemarker.core._MiscTemplateException;
import freemarker.template.TemplateBooleanModel;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateScalarModel;
import freemarker.template.utility.DeepUnwrap;
/**
* A model that when invoked with a 'path' parameter will perform a servlet
* include. It also support an optional hash named 'params' which specifies
* request parameters for the include - its keys are strings, its values
* should be either strings or sequences of strings (for multiple valued
* parameters). A third optional parameter 'inherit_params' should be a boolean
* when specified, and it defaults to true when not specified. A value of true
* means that the include inherits the request parameters from the current
* request. In this case values in 'params' will get prepended to the existing
* values of parameters.
*/
public class IncludePage implements TemplateDirectiveModel {
private final HttpServletRequest request;
private final HttpServletResponse response;
public IncludePage(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
public void execute(final Environment env, Map params,
TemplateModel[] loopVars, TemplateDirectiveBody body)
throws TemplateException, IOException {
// Determine the path
final TemplateModel path = (TemplateModel) params.get("path");
if (path == null) {
throw new _MiscTemplateException(env, "Missing required parameter \"path\"");
}
if (!(path instanceof TemplateScalarModel)) {
throw new _MiscTemplateException(env,
"Expected a scalar model. \"path\" is instead ", new _DelayedFTLTypeDescription(path));
}
final String strPath = ((TemplateScalarModel) path).getAsString();
if (strPath == null) {
throw new _MiscTemplateException(env, "String value of \"path\" parameter is null");
}
// See whether we need to use a custom response (if we're inside a TTM
// or TDM or macro nested body, we'll need to as then the current
// FM environment writer is not identical to HTTP servlet response
// writer.
final Writer envOut = env.getOut();
final HttpServletResponse wrappedResponse;
if (envOut == response.getWriter()) {
// Don't bother wrapping if environment's writer is same as
// response writer
wrappedResponse = response;
} else {
final PrintWriter printWriter = (envOut instanceof PrintWriter) ?
(PrintWriter) envOut :
new PrintWriter(envOut);
// Otherwise, create a response wrapper that will pass the
// env writer, potentially first wrapping it in a print
// writer when it ain't one already.
wrappedResponse = new HttpServletResponseWrapper(response) {
@Override
public PrintWriter getWriter() {
return printWriter;
}
};
}
// Determine inherit_params value
final boolean inheritParams;
final TemplateModel inheritParamsModel = (TemplateModel) params.get("inherit_params");
if (inheritParamsModel == null) {
// defaults to true when not specified
inheritParams = true;
} else {
if (!(inheritParamsModel instanceof TemplateBooleanModel)) {
throw new _MiscTemplateException(env,
"\"inherit_params\" should be a boolean but it's a(n) ",
inheritParamsModel.getClass().getName(), " instead");
}
inheritParams = ((TemplateBooleanModel) inheritParamsModel).getAsBoolean();
}
// Get explicit params, if any
final TemplateModel paramsModel = (TemplateModel) params.get("params");
// Determine whether we need to wrap the request
final HttpServletRequest wrappedRequest;
if (paramsModel == null && inheritParams) {
// Inherit original request params & no params explicitly
// specified, so use the original request
wrappedRequest = request;
} else {
// In any other case, use a custom request wrapper
final Map paramsMap;
if (paramsModel != null) {
// Convert params to a Map
final Object unwrapped = DeepUnwrap.unwrap(paramsModel);
if (!(unwrapped instanceof Map)) {
throw new _MiscTemplateException(env,
"Expected \"params\" to unwrap into a java.util.Map. It unwrapped into ",
unwrapped.getClass().getName(), " instead.");
}
paramsMap = (Map) unwrapped;
} else {
paramsMap = Collections.EMPTY_MAP;
}
wrappedRequest = new CustomParamsRequest(request, paramsMap,
inheritParams);
}
// Finally, do the include
try {
request.getRequestDispatcher(strPath).include(wrappedRequest,
wrappedResponse);
} catch (ServletException e) {
throw new _MiscTemplateException(e, env);
}
}
private static final class CustomParamsRequest extends HttpServletRequestWrapper {
private final HashMap paramsMap;
private CustomParamsRequest(HttpServletRequest request, Map paramMap,
boolean inheritParams) {
super(request);
paramsMap = inheritParams ? new HashMap(request.getParameterMap()) : new HashMap();
for (Iterator it = paramMap.entrySet().iterator(); it.hasNext(); ) {
Map.Entry entry = (Map.Entry) it.next();
String name = String.valueOf(entry.getKey());
Object value = entry.getValue();
final String[] valueArray;
if (value == null) {
// Null values are explicitly added (so, among other
// things, we can hide inherited param values).
valueArray = new String[] { null };
} else if (value instanceof String[]) {
// String[] arrays are just passed through
valueArray = (String[]) value;
} else if (value instanceof Collection) {
// Collections are converted to String[], with
// String.valueOf() used on elements
Collection col = (Collection) value;
valueArray = new String[col.size()];
int i = 0;
for (Iterator it2 = col.iterator(); it2.hasNext(); ) {
valueArray[i++] = String.valueOf(it2.next());
}
} else if (value.getClass().isArray()) {
// Other array types are too converted to String[], with
// String.valueOf() used on elements
int len = Array.getLength(value);
valueArray = new String[len];
for (int i = 0; i < len; ++i) {
valueArray[i] = String.valueOf(Array.get(value, i));
}
} else {
// All other values (including strings) are converted to a
// single-element String[], with String.valueOf applied to
// the value.
valueArray = new String[] { String.valueOf(value) };
}
String[] existingParams = (String[]) paramsMap.get(name);
int el = existingParams == null ? 0 : existingParams.length;
if (el == 0) {
// No original params, just put our array
paramsMap.put(name, valueArray);
} else {
int vl = valueArray.length;
if (vl > 0) {
// Both original params and new params, prepend our
// params to original params
String[] newValueArray = new String[el + vl];
System.arraycopy(valueArray, 0, newValueArray, 0, vl);
System.arraycopy(existingParams, 0, newValueArray, vl, el);
paramsMap.put(name, newValueArray);
}
}
}
}
@Override
public String[] getParameterValues(String name) {
String[] value = ((String[]) paramsMap.get(name));
return value != null ? (String[]) value.clone() : null;
}
@Override
public String getParameter(String name) {
String[] values = (String[]) paramsMap.get(name);
return values != null && values.length > 0 ? values[0] : null;
}
@Override
public Enumeration getParameterNames() {
return Collections.enumeration(paramsMap.keySet());
}
@Override
public Map getParameterMap() {
HashMap clone = (HashMap) paramsMap.clone();
for (Iterator it = clone.entrySet().iterator(); it.hasNext(); ) {
Map.Entry entry = (Map.Entry) it.next();
entry.setValue(((String[]) entry.getValue()).clone());
}
return Collections.unmodifiableMap(clone);
}
}
}