663 lines
19 KiB
Java
663 lines
19 KiB
Java
/*
|
|
* 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.jasper.runtime;
|
|
|
|
import java.io.IOException;
|
|
import java.io.Writer;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Enumeration;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import javax.el.ELContext;
|
|
import javax.el.ELResolver;
|
|
import javax.el.EvaluationListener;
|
|
import javax.el.FunctionMapper;
|
|
import javax.el.ImportHandler;
|
|
import javax.el.VariableMapper;
|
|
import javax.servlet.Servlet;
|
|
import javax.servlet.ServletConfig;
|
|
import javax.servlet.ServletContext;
|
|
import javax.servlet.ServletException;
|
|
import javax.servlet.ServletRequest;
|
|
import javax.servlet.ServletResponse;
|
|
import javax.servlet.http.HttpSession;
|
|
import javax.servlet.jsp.JspApplicationContext;
|
|
import javax.servlet.jsp.JspContext;
|
|
import javax.servlet.jsp.JspFactory;
|
|
import javax.servlet.jsp.JspWriter;
|
|
import javax.servlet.jsp.PageContext;
|
|
import javax.servlet.jsp.el.ELException;
|
|
import javax.servlet.jsp.el.ExpressionEvaluator;
|
|
import javax.servlet.jsp.el.VariableResolver;
|
|
import javax.servlet.jsp.tagext.BodyContent;
|
|
import javax.servlet.jsp.tagext.JspTag;
|
|
import javax.servlet.jsp.tagext.VariableInfo;
|
|
|
|
import org.apache.jasper.compiler.Localizer;
|
|
|
|
/**
|
|
* Implementation of a JSP Context Wrapper.
|
|
*
|
|
* The JSP Context Wrapper is a JspContext created and maintained by a tag
|
|
* handler implementation. It wraps the Invoking JSP Context, that is, the
|
|
* JspContext instance passed to the tag handler by the invoking page via
|
|
* setJspContext().
|
|
*
|
|
* @author Kin-man Chung
|
|
* @author Jan Luehe
|
|
* @author Jacob Hookom
|
|
*/
|
|
@SuppressWarnings("deprecation") // Have to support old JSP EL API
|
|
public class JspContextWrapper extends PageContext implements VariableResolver {
|
|
|
|
private final JspTag jspTag;
|
|
|
|
// Invoking JSP context
|
|
private final PageContext invokingJspCtxt;
|
|
|
|
private final transient HashMap<String, Object> pageAttributes;
|
|
|
|
// ArrayList of NESTED scripting variables
|
|
private final ArrayList<String> nestedVars;
|
|
|
|
// ArrayList of AT_BEGIN scripting variables
|
|
private final ArrayList<String> atBeginVars;
|
|
|
|
// ArrayList of AT_END scripting variables
|
|
private final ArrayList<String> atEndVars;
|
|
|
|
private final Map<String,String> aliases;
|
|
|
|
private final HashMap<String, Object> originalNestedVars;
|
|
|
|
private ServletContext servletContext = null;
|
|
|
|
private ELContext elContext = null;
|
|
|
|
private final PageContext rootJspCtxt;
|
|
|
|
public JspContextWrapper(JspTag jspTag, JspContext jspContext,
|
|
ArrayList<String> nestedVars, ArrayList<String> atBeginVars,
|
|
ArrayList<String> atEndVars, Map<String,String> aliases) {
|
|
this.jspTag = jspTag;
|
|
this.invokingJspCtxt = (PageContext) jspContext;
|
|
if (jspContext instanceof JspContextWrapper) {
|
|
rootJspCtxt = ((JspContextWrapper)jspContext).rootJspCtxt;
|
|
}
|
|
else {
|
|
rootJspCtxt = invokingJspCtxt;
|
|
}
|
|
this.nestedVars = nestedVars;
|
|
this.atBeginVars = atBeginVars;
|
|
this.atEndVars = atEndVars;
|
|
this.pageAttributes = new HashMap<>(16);
|
|
this.aliases = aliases;
|
|
|
|
if (nestedVars != null) {
|
|
this.originalNestedVars = new HashMap<>(nestedVars.size());
|
|
} else {
|
|
this.originalNestedVars = null;
|
|
}
|
|
syncBeginTagFile();
|
|
}
|
|
|
|
@Override
|
|
public void initialize(Servlet servlet, ServletRequest request,
|
|
ServletResponse response, String errorPageURL,
|
|
boolean needsSession, int bufferSize, boolean autoFlush)
|
|
throws IOException, IllegalStateException, IllegalArgumentException {
|
|
}
|
|
|
|
@Override
|
|
public Object getAttribute(String name) {
|
|
|
|
if (name == null) {
|
|
throw new NullPointerException(Localizer
|
|
.getMessage("jsp.error.attribute.null_name"));
|
|
}
|
|
|
|
return pageAttributes.get(name);
|
|
}
|
|
|
|
@Override
|
|
public Object getAttribute(String name, int scope) {
|
|
|
|
if (name == null) {
|
|
throw new NullPointerException(Localizer
|
|
.getMessage("jsp.error.attribute.null_name"));
|
|
}
|
|
|
|
if (scope == PAGE_SCOPE) {
|
|
return pageAttributes.get(name);
|
|
}
|
|
|
|
return rootJspCtxt.getAttribute(name, scope);
|
|
}
|
|
|
|
@Override
|
|
public void setAttribute(String name, Object value) {
|
|
|
|
if (name == null) {
|
|
throw new NullPointerException(Localizer
|
|
.getMessage("jsp.error.attribute.null_name"));
|
|
}
|
|
|
|
if (value != null) {
|
|
pageAttributes.put(name, value);
|
|
} else {
|
|
removeAttribute(name, PAGE_SCOPE);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setAttribute(String name, Object value, int scope) {
|
|
|
|
if (name == null) {
|
|
throw new NullPointerException(Localizer
|
|
.getMessage("jsp.error.attribute.null_name"));
|
|
}
|
|
|
|
if (scope == PAGE_SCOPE) {
|
|
if (value != null) {
|
|
pageAttributes.put(name, value);
|
|
} else {
|
|
removeAttribute(name, PAGE_SCOPE);
|
|
}
|
|
} else {
|
|
rootJspCtxt.setAttribute(name, value, scope);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Object findAttribute(String name) {
|
|
|
|
if (name == null) {
|
|
throw new NullPointerException(Localizer
|
|
.getMessage("jsp.error.attribute.null_name"));
|
|
}
|
|
|
|
Object o = pageAttributes.get(name);
|
|
if (o == null) {
|
|
o = rootJspCtxt.getAttribute(name, REQUEST_SCOPE);
|
|
if (o == null) {
|
|
if (getSession() != null) {
|
|
o = rootJspCtxt.getAttribute(name, SESSION_SCOPE);
|
|
}
|
|
if (o == null) {
|
|
o = rootJspCtxt.getAttribute(name, APPLICATION_SCOPE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return o;
|
|
}
|
|
|
|
@Override
|
|
public void removeAttribute(String name) {
|
|
|
|
if (name == null) {
|
|
throw new NullPointerException(Localizer
|
|
.getMessage("jsp.error.attribute.null_name"));
|
|
}
|
|
|
|
pageAttributes.remove(name);
|
|
rootJspCtxt.removeAttribute(name, REQUEST_SCOPE);
|
|
if (getSession() != null) {
|
|
rootJspCtxt.removeAttribute(name, SESSION_SCOPE);
|
|
}
|
|
rootJspCtxt.removeAttribute(name, APPLICATION_SCOPE);
|
|
}
|
|
|
|
@Override
|
|
public void removeAttribute(String name, int scope) {
|
|
|
|
if (name == null) {
|
|
throw new NullPointerException(Localizer
|
|
.getMessage("jsp.error.attribute.null_name"));
|
|
}
|
|
|
|
if (scope == PAGE_SCOPE) {
|
|
pageAttributes.remove(name);
|
|
} else {
|
|
rootJspCtxt.removeAttribute(name, scope);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getAttributesScope(String name) {
|
|
|
|
if (name == null) {
|
|
throw new NullPointerException(Localizer
|
|
.getMessage("jsp.error.attribute.null_name"));
|
|
}
|
|
|
|
if (pageAttributes.get(name) != null) {
|
|
return PAGE_SCOPE;
|
|
} else {
|
|
return rootJspCtxt.getAttributesScope(name);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Enumeration<String> getAttributeNamesInScope(int scope) {
|
|
if (scope == PAGE_SCOPE) {
|
|
return Collections.enumeration(pageAttributes.keySet());
|
|
}
|
|
|
|
return rootJspCtxt.getAttributeNamesInScope(scope);
|
|
}
|
|
|
|
@Override
|
|
public void release() {
|
|
invokingJspCtxt.release();
|
|
}
|
|
|
|
@Override
|
|
public JspWriter getOut() {
|
|
return rootJspCtxt.getOut();
|
|
}
|
|
|
|
@Override
|
|
public HttpSession getSession() {
|
|
return rootJspCtxt.getSession();
|
|
}
|
|
|
|
@Override
|
|
public Object getPage() {
|
|
return invokingJspCtxt.getPage();
|
|
}
|
|
|
|
@Override
|
|
public ServletRequest getRequest() {
|
|
return invokingJspCtxt.getRequest();
|
|
}
|
|
|
|
@Override
|
|
public ServletResponse getResponse() {
|
|
return rootJspCtxt.getResponse();
|
|
}
|
|
|
|
@Override
|
|
public Exception getException() {
|
|
return invokingJspCtxt.getException();
|
|
}
|
|
|
|
@Override
|
|
public ServletConfig getServletConfig() {
|
|
return invokingJspCtxt.getServletConfig();
|
|
}
|
|
|
|
@Override
|
|
public ServletContext getServletContext() {
|
|
if (servletContext == null) {
|
|
servletContext = rootJspCtxt.getServletContext();
|
|
}
|
|
return servletContext;
|
|
}
|
|
|
|
@Override
|
|
public void forward(String relativeUrlPath) throws ServletException,
|
|
IOException {
|
|
invokingJspCtxt.forward(relativeUrlPath);
|
|
}
|
|
|
|
@Override
|
|
public void include(String relativeUrlPath) throws ServletException,
|
|
IOException {
|
|
invokingJspCtxt.include(relativeUrlPath);
|
|
}
|
|
|
|
@Override
|
|
public void include(String relativeUrlPath, boolean flush)
|
|
throws ServletException, IOException {
|
|
invokingJspCtxt.include(relativeUrlPath, false);
|
|
}
|
|
|
|
@Override
|
|
@Deprecated
|
|
public VariableResolver getVariableResolver() {
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public BodyContent pushBody() {
|
|
return invokingJspCtxt.pushBody();
|
|
}
|
|
|
|
@Override
|
|
public JspWriter pushBody(Writer writer) {
|
|
return invokingJspCtxt.pushBody(writer);
|
|
}
|
|
|
|
@Override
|
|
public JspWriter popBody() {
|
|
return invokingJspCtxt.popBody();
|
|
}
|
|
|
|
@Override
|
|
@Deprecated
|
|
public ExpressionEvaluator getExpressionEvaluator() {
|
|
return invokingJspCtxt.getExpressionEvaluator();
|
|
}
|
|
|
|
@Override
|
|
public void handlePageException(Exception ex) throws IOException,
|
|
ServletException {
|
|
// Should never be called since handleException() called with a
|
|
// Throwable in the generated servlet.
|
|
handlePageException((Throwable) ex);
|
|
}
|
|
|
|
@Override
|
|
public void handlePageException(Throwable t) throws IOException,
|
|
ServletException {
|
|
invokingJspCtxt.handlePageException(t);
|
|
}
|
|
|
|
/**
|
|
* VariableResolver interface
|
|
*/
|
|
@Override
|
|
@Deprecated
|
|
public Object resolveVariable(String pName) throws ELException {
|
|
ELContext ctx = this.getELContext();
|
|
return ctx.getELResolver().getValue(ctx, null, pName);
|
|
}
|
|
|
|
/**
|
|
* Synchronize variables at begin of tag file
|
|
*/
|
|
public void syncBeginTagFile() {
|
|
saveNestedVariables();
|
|
}
|
|
|
|
/**
|
|
* Synchronize variables before fragment invocation
|
|
*/
|
|
public void syncBeforeInvoke() {
|
|
copyTagToPageScope(VariableInfo.NESTED);
|
|
copyTagToPageScope(VariableInfo.AT_BEGIN);
|
|
}
|
|
|
|
/**
|
|
* Synchronize variables at end of tag file
|
|
*/
|
|
public void syncEndTagFile() {
|
|
copyTagToPageScope(VariableInfo.AT_BEGIN);
|
|
copyTagToPageScope(VariableInfo.AT_END);
|
|
restoreNestedVariables();
|
|
}
|
|
|
|
/**
|
|
* Copies the variables of the given scope from the virtual page scope of
|
|
* this JSP context wrapper to the page scope of the invoking JSP context.
|
|
*
|
|
* @param scope
|
|
* variable scope (one of NESTED, AT_BEGIN, or AT_END)
|
|
*/
|
|
private void copyTagToPageScope(int scope) {
|
|
Iterator<String> iter = null;
|
|
|
|
switch (scope) {
|
|
case VariableInfo.NESTED:
|
|
if (nestedVars != null) {
|
|
iter = nestedVars.iterator();
|
|
}
|
|
break;
|
|
case VariableInfo.AT_BEGIN:
|
|
if (atBeginVars != null) {
|
|
iter = atBeginVars.iterator();
|
|
}
|
|
break;
|
|
case VariableInfo.AT_END:
|
|
if (atEndVars != null) {
|
|
iter = atEndVars.iterator();
|
|
}
|
|
break;
|
|
}
|
|
|
|
while ((iter != null) && iter.hasNext()) {
|
|
String varName = iter.next();
|
|
Object obj = getAttribute(varName);
|
|
varName = findAlias(varName);
|
|
if (obj != null) {
|
|
invokingJspCtxt.setAttribute(varName, obj);
|
|
} else {
|
|
invokingJspCtxt.removeAttribute(varName, PAGE_SCOPE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Saves the values of any NESTED variables that are present in the invoking
|
|
* JSP context, so they can later be restored.
|
|
*/
|
|
private void saveNestedVariables() {
|
|
if (nestedVars != null) {
|
|
for (String varName : nestedVars) {
|
|
varName = findAlias(varName);
|
|
Object obj = invokingJspCtxt.getAttribute(varName);
|
|
if (obj != null) {
|
|
originalNestedVars.put(varName, obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Restores the values of any NESTED variables in the invoking JSP context.
|
|
*/
|
|
private void restoreNestedVariables() {
|
|
if (nestedVars != null) {
|
|
for (String varName : nestedVars) {
|
|
varName = findAlias(varName);
|
|
Object obj = originalNestedVars.get(varName);
|
|
if (obj != null) {
|
|
invokingJspCtxt.setAttribute(varName, obj);
|
|
} else {
|
|
invokingJspCtxt.removeAttribute(varName, PAGE_SCOPE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks to see if the given variable name is used as an alias, and if so,
|
|
* returns the variable name for which it is used as an alias.
|
|
*
|
|
* @param varName
|
|
* The variable name to check
|
|
* @return The variable name for which varName is used as an alias, or
|
|
* varName if it is not being used as an alias
|
|
*/
|
|
private String findAlias(String varName) {
|
|
|
|
if (aliases == null)
|
|
return varName;
|
|
|
|
String alias = aliases.get(varName);
|
|
if (alias == null) {
|
|
return varName;
|
|
}
|
|
return alias;
|
|
}
|
|
|
|
@Override
|
|
public ELContext getELContext() {
|
|
if (elContext == null) {
|
|
elContext = new ELContextWrapper(rootJspCtxt.getELContext(), jspTag, this);
|
|
JspFactory factory = JspFactory.getDefaultFactory();
|
|
JspApplicationContext jspAppCtxt = factory.getJspApplicationContext(servletContext);
|
|
if (jspAppCtxt instanceof JspApplicationContextImpl) {
|
|
((JspApplicationContextImpl) jspAppCtxt).fireListeners(elContext);
|
|
}
|
|
}
|
|
return elContext;
|
|
}
|
|
|
|
|
|
static class ELContextWrapper extends ELContext {
|
|
|
|
private final ELContext wrapped;
|
|
private final JspTag jspTag;
|
|
private final PageContext pageContext;
|
|
private ImportHandler importHandler;
|
|
|
|
private ELContextWrapper(ELContext wrapped, JspTag jspTag, PageContext pageContext) {
|
|
this.wrapped = wrapped;
|
|
this.jspTag = jspTag;
|
|
this.pageContext = pageContext;
|
|
}
|
|
|
|
ELContext getWrappedELContext() {
|
|
return wrapped;
|
|
}
|
|
|
|
@Override
|
|
public void setPropertyResolved(boolean resolved) {
|
|
wrapped.setPropertyResolved(resolved);
|
|
}
|
|
|
|
@Override
|
|
public void setPropertyResolved(Object base, Object property) {
|
|
wrapped.setPropertyResolved(base, property);
|
|
}
|
|
|
|
@Override
|
|
public boolean isPropertyResolved() {
|
|
return wrapped.isPropertyResolved();
|
|
}
|
|
|
|
@Override
|
|
public void putContext(@SuppressWarnings("rawtypes") Class key, Object contextObject) {
|
|
wrapped.putContext(key, contextObject);
|
|
}
|
|
|
|
@Override
|
|
public Object getContext(@SuppressWarnings("rawtypes") Class key) {
|
|
if (key == JspContext.class) {
|
|
return pageContext;
|
|
}
|
|
return wrapped.getContext(key);
|
|
}
|
|
|
|
@Override
|
|
public ImportHandler getImportHandler() {
|
|
if (importHandler == null) {
|
|
importHandler = new ImportHandler();
|
|
if (jspTag instanceof JspSourceImports) {
|
|
Set<String> packageImports = ((JspSourceImports) jspTag).getPackageImports();
|
|
if (packageImports != null) {
|
|
for (String packageImport : packageImports) {
|
|
importHandler.importPackage(packageImport);
|
|
}
|
|
}
|
|
Set<String> classImports = ((JspSourceImports) jspTag).getClassImports();
|
|
if (classImports != null) {
|
|
for (String classImport : classImports) {
|
|
importHandler.importClass(classImport);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
return importHandler;
|
|
}
|
|
|
|
@Override
|
|
public Locale getLocale() {
|
|
return wrapped.getLocale();
|
|
}
|
|
|
|
@Override
|
|
public void setLocale(Locale locale) {
|
|
wrapped.setLocale(locale);
|
|
}
|
|
|
|
@Override
|
|
public void addEvaluationListener(EvaluationListener listener) {
|
|
wrapped.addEvaluationListener(listener);
|
|
}
|
|
|
|
@Override
|
|
public List<EvaluationListener> getEvaluationListeners() {
|
|
return wrapped.getEvaluationListeners();
|
|
}
|
|
|
|
@Override
|
|
public void notifyBeforeEvaluation(String expression) {
|
|
wrapped.notifyBeforeEvaluation(expression);
|
|
}
|
|
|
|
@Override
|
|
public void notifyAfterEvaluation(String expression) {
|
|
wrapped.notifyAfterEvaluation(expression);
|
|
}
|
|
|
|
@Override
|
|
public void notifyPropertyResolved(Object base, Object property) {
|
|
wrapped.notifyPropertyResolved(base, property);
|
|
}
|
|
|
|
@Override
|
|
public boolean isLambdaArgument(String name) {
|
|
return wrapped.isLambdaArgument(name);
|
|
}
|
|
|
|
@Override
|
|
public Object getLambdaArgument(String name) {
|
|
return wrapped.getLambdaArgument(name);
|
|
}
|
|
|
|
@Override
|
|
public void enterLambdaScope(Map<String, Object> arguments) {
|
|
wrapped.enterLambdaScope(arguments);
|
|
}
|
|
|
|
@Override
|
|
public void exitLambdaScope() {
|
|
wrapped.exitLambdaScope();
|
|
}
|
|
|
|
@Override
|
|
public Object convertToType(Object obj, Class<?> type) {
|
|
return wrapped.convertToType(obj, type);
|
|
}
|
|
|
|
@Override
|
|
public ELResolver getELResolver() {
|
|
return wrapped.getELResolver();
|
|
}
|
|
|
|
@Override
|
|
public FunctionMapper getFunctionMapper() {
|
|
return wrapped.getFunctionMapper();
|
|
}
|
|
|
|
@Override
|
|
public VariableMapper getVariableMapper() {
|
|
return wrapped.getVariableMapper();
|
|
}
|
|
}
|
|
}
|