Skip to content
Snippets Groups Projects
Commit 5213c6ac authored by Tor-Einar Skog's avatar Tor-Einar Skog
Browse files

SUCCESS TEST: Move Freemarker servlet parts into VIPS -> Refactor to Jakarta may be done!!

parent 667f7b3f
Branches freemarkertest
No related tags found
No related merge requests found
Showing
with 2956 additions and 1 deletion
/*
* 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.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import javax.servlet.ServletContext;
import freemarker.log.Logger;
import freemarker.template.Configuration;
import freemarker.template.utility.CollectionUtils;
import freemarker.template.utility.NullArgumentException;
import freemarker.template.utility.StringUtil;
/**
* A {@link TemplateLoader} that uses streams reachable through {@link ServletContext#getResource(String)} as its source
* of templates.
*/
public class WebappTemplateLoader implements TemplateLoader {
private static final Logger LOG = Logger.getLogger("freemarker.cache");
private final ServletContext servletContext;
private final String subdirPath;
private Boolean urlConnectionUsesCaches;
private boolean attemptFileAccess = true;
/**
* Creates a template loader that will use the specified servlet context to load the resources. It will use
* the base path of <code>"/"</code> meaning templates will be resolved relative to the servlet context root
* location.
*
* @param servletContext
* the servlet context whose {@link ServletContext#getResource(String)} will be used to load the
* templates.
*/
public WebappTemplateLoader(ServletContext servletContext) {
this(servletContext, "/");
}
/**
* Creates a template loader that will use the specified servlet context to load the resources. It will use the
* specified base path, which is interpreted relatively to the context root (does not mater if you start it with "/"
* or not). Path components should be separated by forward slashes independently of the separator character used by
* the underlying operating system.
*
* @param servletContext
* the servlet context whose {@link ServletContext#getResource(String)} will be used to load the
* templates.
* @param subdirPath
* the base path to template resources.
*/
public WebappTemplateLoader(ServletContext servletContext, String subdirPath) {
NullArgumentException.check("servletContext", servletContext);
NullArgumentException.check("subdirPath", subdirPath);
subdirPath = subdirPath.replace('\\', '/');
if (!subdirPath.endsWith("/")) {
subdirPath += "/";
}
if (!subdirPath.startsWith("/")) {
subdirPath = "/" + subdirPath;
}
this.subdirPath = subdirPath;
this.servletContext = servletContext;
}
@Override
public Object findTemplateSource(String name) throws IOException {
String fullPath = subdirPath + name;
if (attemptFileAccess) {
// First try to open as plain file (to bypass servlet container resource caches).
try {
String realPath = servletContext.getRealPath(fullPath);
if (realPath != null) {
File file = new File(realPath);
if (file.canRead() && file.isFile()) {
return file;
}
}
} catch (SecurityException e) {
;// ignore
}
}
// If it fails, try to open it with servletContext.getResource.
URL url = null;
try {
url = servletContext.getResource(fullPath);
} catch (MalformedURLException e) {
LOG.warn("Could not retrieve resource " + StringUtil.jQuoteNoXSS(fullPath),
e);
return null;
}
return url == null ? null : new URLTemplateSource(url, getURLConnectionUsesCaches());
}
@Override
public long getLastModified(Object templateSource) {
if (templateSource instanceof File) {
return ((File) templateSource).lastModified();
} else {
return ((URLTemplateSource) templateSource).lastModified();
}
}
@Override
public Reader getReader(Object templateSource, String encoding)
throws IOException {
if (templateSource instanceof File) {
return new InputStreamReader(
new FileInputStream((File) templateSource),
encoding);
} else {
return new InputStreamReader(
((URLTemplateSource) templateSource).getInputStream(),
encoding);
}
}
@Override
public void closeTemplateSource(Object templateSource) throws IOException {
if (templateSource instanceof File) {
// Do nothing.
} else {
((URLTemplateSource) templateSource).close();
}
}
/**
* Getter pair of {@link #setURLConnectionUsesCaches(Boolean)}.
*
* @since 2.3.21
*/
public Boolean getURLConnectionUsesCaches() {
return urlConnectionUsesCaches;
}
/**
* It does the same as {@link URLTemplateLoader#setURLConnectionUsesCaches(Boolean)}; see there.
*
* @since 2.3.21
*/
public void setURLConnectionUsesCaches(Boolean urlConnectionUsesCaches) {
this.urlConnectionUsesCaches = urlConnectionUsesCaches;
}
/**
* Show class name and some details that are useful in template-not-found errors.
*
* @since 2.3.21
*/
@Override
public String toString() {
return TemplateLoaderUtils.getClassNameForToString(this)
+ "(subdirPath=" + StringUtil.jQuote(subdirPath)
+ ", servletContext={contextPath=" + StringUtil.jQuote(getContextPath())
+ ", displayName=" + StringUtil.jQuote(servletContext.getServletContextName()) + "})";
}
/** Gets the context path if we are on Servlet 2.5+, or else returns failure description string. */
private String getContextPath() {
try {
Method m = servletContext.getClass().getMethod("getContextPath", CollectionUtils.EMPTY_CLASS_ARRAY);
return (String) m.invoke(servletContext, CollectionUtils.EMPTY_OBJECT_ARRAY);
} catch (Throwable e) {
return "[can't query before Serlvet 2.5]";
}
}
/**
* Getter pair of {@link #setAttemptFileAccess(boolean)}.
*
* @since 2.3.23
*/
public boolean getAttemptFileAccess() {
return attemptFileAccess;
}
/**
* Specifies that before loading templates with {@link ServletContext#getResource(String)}, it should try to load
* the template as {@link File}; default is {@code true}, though it's not always recommended anymore. This is a
* workaround for the case when the servlet container doesn't show template modifications after the template was
* already loaded earlier. But it's certainly better to counter this problem by disabling the URL connection cache
* with {@link #setURLConnectionUsesCaches(Boolean)}, which is also the default behavior with
* {@link Configuration#setIncompatibleImprovements(freemarker.template.Version) incompatible_improvements} 2.3.21
* and later.
*
* @since 2.3.23
*/
public void setAttemptFileAccess(boolean attemptLoadingFromFile) {
this.attemptFileAccess = attemptLoadingFromFile;
}
}
\ No newline at end of file
/*
* 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 no.nibio.freemarker.ext.servlet;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import freemarker.template.ObjectWrapper;
import freemarker.template.SimpleHash;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.utility.NullArgumentException;
/**
* An extension of SimpleHash that looks up keys in the hash, then in the
* request, session, and servlet context scopes. Makes "Application", "Session"
* and "Request" keys largely obsolete, however we keep them for backward
* compatibility (also, "Request" is required for proper operation of JSP
* taglibs).
* It is on purpose that we didn't override <tt>keys</tt> and <tt>values</tt>
* methods. That way, only those variables assigned into the hash directly by a
* subclass of <tt>FreemarkerServlet</tt> that overrides
* <tt>preTemplateProcess</tt>) are discovered as "page" variables by the FM
* JSP PageContext implementation.
*/
public class AllHttpScopesHashModel extends SimpleHash {
private final ServletContext context;
private final HttpServletRequest request;
private final Map unlistedModels = new HashMap();
/**
* Creates a new instance of AllHttpScopesHashModel for handling a single
* HTTP servlet request.
* @param objectWrapper the object wrapper to use; not {@code null}.
* @param context the servlet context of the web application
* @param request the HTTP servlet request being processed
*/
public AllHttpScopesHashModel(ObjectWrapper objectWrapper,
ServletContext context, HttpServletRequest request) {
super(objectWrapper);
NullArgumentException.check("wrapper", objectWrapper);
this.context = context;
this.request = request;
}
/**
* Stores a model in the hash so that it doesn't show up in <tt>keys()</tt>
* and <tt>values()</tt> methods. Used to put the Application, Session,
* Request, RequestParameters and JspTaglibs objects.
* @param key the key under which the model is stored
* @param model the stored model
*/
public void putUnlistedModel(String key, TemplateModel model) {
unlistedModels.put(key, model);
}
@Override
public TemplateModel get(String key) throws TemplateModelException {
// Lookup in page scope
TemplateModel model = super.get(key);
if (model != null) {
return model;
}
// Look in unlisted models
model = (TemplateModel) unlistedModels.get(key);
if (model != null) {
return model;
}
// Lookup in request scope
Object obj = request.getAttribute(key);
if (obj != null) {
return wrap(obj);
}
// Lookup in session scope
HttpSession session = request.getSession(false);
if (session != null) {
obj = session.getAttribute(key);
if (obj != null) {
return wrap(obj);
}
}
// Lookup in application scope
obj = context.getAttribute(key);
if (obj != null) {
return wrap(obj);
}
// return wrapper's null object (probably null).
return wrap(null);
}
}
This diff is collapsed.
/*
* 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 no.nibio.freemarker.ext.servlet;
import java.util.ArrayList;
import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import freemarker.template.ObjectWrapper;
import freemarker.template.ObjectWrapperAndUnwrapper;
import freemarker.template.SimpleCollection;
import freemarker.template.TemplateCollectionModel;
import freemarker.template.TemplateHashModelEx;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
/**
* TemplateHashModel wrapper for a HttpServletRequest attributes.
*/
public final class HttpRequestHashModel implements TemplateHashModelEx {
private final HttpServletRequest request;
private final HttpServletResponse response;
private final ObjectWrapper wrapper;
/**
* @param wrapper
* Should be an {@link ObjectWrapperAndUnwrapper}, or else some features might won't work properly. (It's
* declared as {@link ObjectWrapper} only for backward compatibility.)
*/
public HttpRequestHashModel(
HttpServletRequest request, ObjectWrapper wrapper) {
this(request, null, wrapper);
}
public HttpRequestHashModel(
HttpServletRequest request, HttpServletResponse response,
ObjectWrapper wrapper) {
this.request = request;
this.response = response;
this.wrapper = wrapper;
}
@Override
public TemplateModel get(String key) throws TemplateModelException {
return wrapper.wrap(request.getAttribute(key));
}
@Override
public boolean isEmpty() {
return !request.getAttributeNames().hasMoreElements();
}
@Override
public int size() {
int result = 0;
for (Enumeration enumeration = request.getAttributeNames(); enumeration.hasMoreElements(); ) {
enumeration.nextElement();
++result;
}
return result;
}
@Override
public TemplateCollectionModel keys() {
ArrayList keys = new ArrayList();
for (Enumeration enumeration = request.getAttributeNames(); enumeration.hasMoreElements(); ) {
keys.add(enumeration.nextElement());
}
return new SimpleCollection(keys.iterator());
}
@Override
public TemplateCollectionModel values() {
ArrayList values = new ArrayList();
for (Enumeration enumeration = request.getAttributeNames(); enumeration.hasMoreElements(); ) {
values.add(request.getAttribute((String) enumeration.nextElement()));
}
return new SimpleCollection(values.iterator(), wrapper);
}
public HttpServletRequest getRequest() {
return request;
}
public HttpServletResponse getResponse() {
return response;
}
public ObjectWrapper getObjectWrapper() {
return wrapper;
}
}
/*
* 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 no.nibio.freemarker.ext.servlet;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import freemarker.template.SimpleCollection;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateCollectionModel;
import freemarker.template.TemplateHashModelEx;
import freemarker.template.TemplateModel;
/**
* TemplateHashModel wrapper for a HttpServletRequest parameters.
*/
public class HttpRequestParametersHashModel
implements
TemplateHashModelEx {
private final HttpServletRequest request;
private List keys;
public HttpRequestParametersHashModel(HttpServletRequest request) {
this.request = request;
}
@Override
public TemplateModel get(String key) {
String value = request.getParameter(key);
return value == null ? null : new SimpleScalar(value);
}
@Override
public boolean isEmpty() {
return !request.getParameterNames().hasMoreElements();
}
@Override
public int size() {
return getKeys().size();
}
@Override
public TemplateCollectionModel keys() {
return new SimpleCollection(getKeys().iterator());
}
@Override
public TemplateCollectionModel values() {
final Iterator iter = getKeys().iterator();
return new SimpleCollection(
new Iterator() {
@Override
public boolean hasNext() {
return iter.hasNext();
}
@Override
public Object next() {
return request.getParameter((String) iter.next());
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
});
}
protected String transcode(String string) {
return string;
}
private synchronized List getKeys() {
if (keys == null) {
keys = new ArrayList();
for (Enumeration enumeration = request.getParameterNames(); enumeration.hasMoreElements(); ) {
keys.add(enumeration.nextElement());
}
}
return keys;
}
}
/*
* 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 no.nibio.freemarker.ext.servlet;
import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import freemarker.template.ObjectWrapper;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
/**
* TemplateHashModel wrapper for a HttpSession attributes.
*/
public final class HttpSessionHashModel implements TemplateHashModel, Serializable {
private static final long serialVersionUID = 1L;
private transient HttpSession session;
private transient final ObjectWrapper wrapper;
// These are required for lazy initializing session
private transient final FreemarkerServlet servlet;
private transient final HttpServletRequest request;
private transient final HttpServletResponse response;
/**
* Use this constructor when the session already exists.
* @param session the session
* @param wrapper an object wrapper used to wrap session attributes
*/
public HttpSessionHashModel(HttpSession session, ObjectWrapper wrapper) {
this.session = session;
this.wrapper = wrapper;
this.servlet = null;
this.request = null;
this.response = null;
}
/**
* Use this constructor when the session isn't already created. It is passed
* enough parameters so that the session can be properly initialized after
* it's detected that it was created.
* @param servlet the FreemarkerServlet that created this model. If the
* model is not created through FreemarkerServlet, leave this argument as
* null.
* @param request the actual request
* @param response the actual response
* @param wrapper an object wrapper used to wrap session attributes
*/
public HttpSessionHashModel(FreemarkerServlet servlet, HttpServletRequest request, HttpServletResponse response, ObjectWrapper wrapper) {
this.wrapper = wrapper;
this.servlet = servlet;
this.request = request;
this.response = response;
}
@Override
public TemplateModel get(String key) throws TemplateModelException {
checkSessionExistence();
return wrapper.wrap(session != null ? session.getAttribute(key) : null);
}
private void checkSessionExistence() throws TemplateModelException {
if (session == null && request != null) {
session = request.getSession(false);
if (session != null && servlet != null) {
try {
servlet.initializeSessionAndInstallModel(request, response,
this, session);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new TemplateModelException(e);
}
}
}
}
boolean isOrphaned(HttpSession currentSession) {
return (session != null && session != currentSession) ||
(session == null && request == null);
}
@Override
public boolean isEmpty()
throws TemplateModelException {
checkSessionExistence();
return session == null || !session.getAttributeNames().hasMoreElements();
}
}
/*
* 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 no.nibio.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;
}
@Override
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 ? 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);
}
}
}
/*
* 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 no.nibio.freemarker.ext.servlet;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import javax.servlet.ServletContext;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.cache.WebappTemplateLoader;
import freemarker.core._ObjectBuilderSettingEvaluator;
import freemarker.core._SettingEvaluationEnvironment;
import freemarker.log.Logger;
import freemarker.template.Configuration;
import freemarker.template.utility.StringUtil;
final class InitParamParser {
static final String TEMPLATE_PATH_PREFIX_CLASS = "class://";
static final String TEMPLATE_PATH_PREFIX_CLASSPATH = "classpath:";
static final String TEMPLATE_PATH_PREFIX_FILE = "file://";
static final String TEMPLATE_PATH_SETTINGS_BI_NAME = "settings";
private static final Logger LOG = Logger.getLogger("freemarker.servlet");
private InitParamParser() {
// Not to be instantiated
}
static TemplateLoader createTemplateLoader(
String templatePath, Configuration cfg, Class classLoaderClass, ServletContext srvCtx)
throws IOException {
final int settingAssignmentsStart = findTemplatePathSettingAssignmentsStart(templatePath);
String pureTemplatePath = (settingAssignmentsStart == -1 ? templatePath : templatePath.substring(0, settingAssignmentsStart))
.trim();
final TemplateLoader templateLoader;
if (pureTemplatePath.startsWith(TEMPLATE_PATH_PREFIX_CLASS)) {
String packagePath = pureTemplatePath.substring(TEMPLATE_PATH_PREFIX_CLASS.length());
packagePath = normalizeToAbsolutePackagePath(packagePath);
templateLoader = new ClassTemplateLoader(classLoaderClass, packagePath);
} else if (pureTemplatePath.startsWith(TEMPLATE_PATH_PREFIX_CLASSPATH)) {
// To be similar to Spring resource paths, we don't require "//":
String packagePath = pureTemplatePath.substring(TEMPLATE_PATH_PREFIX_CLASSPATH.length());
packagePath = normalizeToAbsolutePackagePath(packagePath);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
LOG.warn("No Thread Context Class Loader was found. Falling back to the class loader of "
+ classLoaderClass.getName() + ".");
classLoader = classLoaderClass.getClassLoader();
}
templateLoader = new ClassTemplateLoader(classLoader, packagePath);
} else if (pureTemplatePath.startsWith(TEMPLATE_PATH_PREFIX_FILE)) {
String filePath = pureTemplatePath.substring(TEMPLATE_PATH_PREFIX_FILE.length());
templateLoader = new FileTemplateLoader(new File(filePath));
} else if (pureTemplatePath.startsWith("[")
) {
if (!pureTemplatePath.endsWith("]")) {
// B.C. constraint: Can't throw any checked exceptions.
throw new TemplatePathParsingException("Failed to parse template path; closing \"]\" is missing.");
}
String commaSepItems = pureTemplatePath.substring(1, pureTemplatePath.length() - 1).trim();
List listItems = parseCommaSeparatedTemplatePaths(commaSepItems);
TemplateLoader[] templateLoaders = new TemplateLoader[listItems.size()];
for (int i = 0; i < listItems.size(); i++) {
String pathItem = (String) listItems.get(i);
templateLoaders[i] = createTemplateLoader(pathItem, cfg, classLoaderClass, srvCtx);
}
templateLoader = new MultiTemplateLoader(templateLoaders);
} else if (pureTemplatePath.startsWith("{")
) {
throw new TemplatePathParsingException("Template paths starting with \"{\" are reseved for future purposes");
} else {
templateLoader = new WebappTemplateLoader(srvCtx, pureTemplatePath);
}
if (settingAssignmentsStart != -1) {
try {
int nextPos = _ObjectBuilderSettingEvaluator.configureBean(
templatePath, templatePath.indexOf('(', settingAssignmentsStart) + 1, templateLoader,
_SettingEvaluationEnvironment.getCurrent());
if (nextPos != templatePath.length()) {
throw new TemplatePathParsingException("Template path should end after the setting list in: "
+ templatePath);
}
} catch (Exception e) {
throw new TemplatePathParsingException("Failed to set properties in: " + templatePath, e);
}
}
return templateLoader;
}
static String normalizeToAbsolutePackagePath(String path) {
while (path.startsWith("/")) {
path = path.substring(1);
}
return "/" + path;
}
static List/*<String>*/ parseCommaSeparatedList(String value) throws ParseException {
List/*<String>*/ valuesList = new ArrayList();
String[] values = StringUtil.split(value, ',');
for (int i = 0; i < values.length; i++) {
final String s = values[i].trim();
if (s.length() != 0) {
valuesList.add(s);
} else if (i != values.length - 1) {
throw new ParseException("Missing list item berfore a comma", -1);
}
}
return valuesList;
}
static List parseCommaSeparatedPatterns(String value) throws ParseException {
List/*<String>*/ values = parseCommaSeparatedList(value);
List/*<Pattern>*/ patterns = new ArrayList(values.size());
for (int i = 0; i < values.size(); i++) {
patterns.add(Pattern.compile((String) values.get(i)));
}
return patterns;
}
/**
* This is like {@link #parseCommaSeparatedList(String)}, but is not confused by commas inside
* {@code ?settings(...)} parts at the end of the items.
*/
static List parseCommaSeparatedTemplatePaths(String commaSepItems) {
List listItems;
listItems = new ArrayList();
while (commaSepItems.length() != 0) {
int itemSettingAssignmentsStart = findTemplatePathSettingAssignmentsStart(commaSepItems);
int pureItemEnd = itemSettingAssignmentsStart != -1 ? itemSettingAssignmentsStart : commaSepItems.length();
int prevComaIdx = commaSepItems.lastIndexOf(',', pureItemEnd - 1);
int itemStart = prevComaIdx != -1 ? prevComaIdx + 1 : 0;
final String item = commaSepItems.substring(itemStart).trim();
if (item.length() != 0) {
listItems.add(0, item);
} else if (listItems.size() > 0) {
throw new TemplatePathParsingException("Missing list item before a comma");
}
commaSepItems = prevComaIdx != -1 ? commaSepItems.substring(0, prevComaIdx).trim() : "";
}
return listItems;
}
/**
* @return -1 if there's no setting assignment.
*/
static int findTemplatePathSettingAssignmentsStart(String s) {
int pos = s.length() - 1;
// Skip WS
while (pos >= 0 && Character.isWhitespace(s.charAt(pos))) {
pos--;
}
// Skip `)`
if (pos < 0 || s.charAt(pos) != ')') return -1;
pos--;
// Skip `(...`
int parLevel = 1;
int mode = 0;
while (parLevel > 0) {
if (pos < 0) return -1;
char c = s.charAt(pos);
switch (mode) {
case 0: // 0: outside string literal
switch (c) {
case '(': parLevel--; break;
case ')': parLevel++; break;
case '\'': mode = 1; break;
case '"': mode = 2; break;
}
break;
case 1: // 1: inside '...'
if (c == '\'' && !(pos > 0 && s.charAt(pos - 1) == '\\')) {
mode = 0;
}
break;
case 2: // 2: inside "..."
if (c == '"' && !(pos > 0 && s.charAt(pos - 1) == '\\')) {
mode = 0;
}
break;
}
pos--;
}
// Skip WS
while (pos >= 0 && Character.isWhitespace(s.charAt(pos))) {
pos--;
}
int biNameEnd = pos + 1;
// Skip name chars
while (pos >= 0 && Character.isJavaIdentifierPart(s.charAt(pos))) {
pos--;
}
int biNameStart = pos + 1;
if (biNameStart == biNameEnd) {
return -1;
}
String biName = s.substring(biNameStart, biNameEnd);
// Skip WS
while (pos >= 0 && Character.isWhitespace(s.charAt(pos))) {
pos--;
}
// Skip `?`
if (pos < 0 || s.charAt(pos) != '?') return -1;
if (!biName.equals(TEMPLATE_PATH_SETTINGS_BI_NAME)) {
throw new TemplatePathParsingException(
StringUtil.jQuote(biName) + " is unexpected after the \"?\". "
+ "Expected \"" + TEMPLATE_PATH_SETTINGS_BI_NAME + "\".");
}
return pos;
}
private static final class TemplatePathParsingException extends RuntimeException {
public TemplatePathParsingException(String message, Throwable cause) {
super(message, cause);
}
public TemplatePathParsingException(String message) {
super(message);
}
}
}
/*
* 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 no.nibio.freemarker.ext.servlet;
import javax.servlet.GenericServlet;
import javax.servlet.ServletContext;
import freemarker.template.ObjectWrapper;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
/**
* TemplateHashModel wrapper for a ServletContext attributes.
*/
public final class ServletContextHashModel implements TemplateHashModel {
private final GenericServlet servlet;
private final ServletContext servletctx;
private final ObjectWrapper wrapper;
public ServletContextHashModel(
GenericServlet servlet, ObjectWrapper wrapper) {
this.servlet = servlet;
this.servletctx = servlet.getServletContext();
this.wrapper = wrapper;
}
/**
* @deprecated use
* {@link #ServletContextHashModel(GenericServlet, ObjectWrapper)} instead.
*/
@Deprecated
public ServletContextHashModel(
ServletContext servletctx, ObjectWrapper wrapper) {
this.servlet = null;
this.servletctx = servletctx;
this.wrapper = wrapper;
}
@Override
public TemplateModel get(String key) throws TemplateModelException {
return wrapper.wrap(servletctx.getAttribute(key));
}
@Override
public boolean isEmpty() {
return !servletctx.getAttributeNames().hasMoreElements();
}
/**
* Returns the underlying servlet. Can return null if this object was
* created using the deprecated constructor.
*/
public GenericServlet getServlet() {
return servlet;
}
}
<!--
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.
-->
<html>
<body>
<p>Servlet for legacy "Model 2" frameworks that allows using FreeMarker
templates instead of JSP as the MVC View
(see <a href="https://freemarker.apache.org/docs/pgui_misc_servlet.html" target="_blank">in the Manual</a>).</p>
</body>
</html>
...@@ -245,7 +245,7 @@ ...@@ -245,7 +245,7 @@
<!-- FreeMarker configuration --> <!-- FreeMarker configuration -->
<servlet> <servlet>
<servlet-name>freemarker</servlet-name> <servlet-name>freemarker</servlet-name>
<servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class> <servlet-class>no.nibio.freemarker.ext.servlet.FreemarkerServlet</servlet-class>
<!-- FreemarkerServlet settings: --> <!-- FreemarkerServlet settings: -->
<init-param> <init-param>
<param-name>TemplatePath</param-name> <param-name>TemplatePath</param-name>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment