This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,157 @@
/*
* 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;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Some constants and other global data that are used by the compiler and the runtime.
*
* @author Anil K. Vijendran
* @author Harish Prabandham
* @author Shawn Bayern
* @author Mark Roth
*/
public class Constants {
/**
* The base class of the generated servlets.
*/
public static final String JSP_SERVLET_BASE =
System.getProperty("org.apache.jasper.Constants.JSP_SERVLET_BASE", "org.apache.jasper.runtime.HttpJspBase");
/**
* _jspService is the name of the method that is called by
* HttpJspBase.service(). This is where most of the code generated
* from JSPs go.
*/
public static final String SERVICE_METHOD_NAME =
System.getProperty("org.apache.jasper.Constants.SERVICE_METHOD_NAME", "_jspService");
/**
* These classes/packages are automatically imported by the
* generated code.
*/
private static final String[] PRIVATE_STANDARD_IMPORTS = {
"javax.servlet.*",
"javax.servlet.http.*",
"javax.servlet.jsp.*"
};
public static final List<String> STANDARD_IMPORTS =
Collections.unmodifiableList(Arrays.asList(PRIVATE_STANDARD_IMPORTS));
/**
* ServletContext attribute for classpath. This is tomcat specific.
* Other servlet engines may choose to support this attribute if they
* want to have this JSP engine running on them.
*/
public static final String SERVLET_CLASSPATH =
System.getProperty("org.apache.jasper.Constants.SERVLET_CLASSPATH", "org.apache.catalina.jsp_classpath");
/**
* Default size of the JSP buffer.
*/
public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
/**
* Default size for the tag buffers.
*/
public static final int DEFAULT_TAG_BUFFER_SIZE = 512;
/**
* Default tag handler pool size.
*/
public static final int MAX_POOL_SIZE = 5;
/**
* The query parameter that causes the JSP engine to just
* pregenerated the servlet but not invoke it.
*/
public static final String PRECOMPILE =
System.getProperty("org.apache.jasper.Constants.PRECOMPILE", "jsp_precompile");
/**
* The default package name for compiled jsp pages.
*/
public static final String JSP_PACKAGE_NAME =
System.getProperty("org.apache.jasper.Constants.JSP_PACKAGE_NAME", "org.apache.jsp");
/**
* The default package name for tag handlers generated from tag files
*/
public static final String TAG_FILE_PACKAGE_NAME =
System.getProperty("org.apache.jasper.Constants.TAG_FILE_PACKAGE_NAME", "org.apache.jsp.tag");
/**
* Default URLs to download the plugin for Netscape and IE.
*/
public static final String NS_PLUGIN_URL =
"http://java.sun.com/products/plugin/";
public static final String IE_PLUGIN_URL =
"http://java.sun.com/products/plugin/1.2.2/jinstall-1_2_2-win.cab#Version=1,2,2,0";
/**
* Prefix to use for generated temporary variable names
*/
public static final String TEMP_VARIABLE_NAME_PREFIX =
System.getProperty("org.apache.jasper.Constants.TEMP_VARIABLE_NAME_PREFIX", "_jspx_temp");
/**
* Has security been turned on?
*/
public static final boolean IS_SECURITY_ENABLED =
(System.getSecurityManager() != null);
public static final boolean USE_INSTANCE_MANAGER_FOR_TAGS =
Boolean.parseBoolean(System.getProperty("org.apache.jasper.Constants.USE_INSTANCE_MANAGER_FOR_TAGS", "false"));
/**
* The name of the path parameter used to pass the session identifier
* back and forth with the client.
*/
public static final String SESSION_PARAMETER_NAME =
System.getProperty("org.apache.catalina.SESSION_PARAMETER_NAME",
"jsessionid");
/**
* Name of the system property containing
* the tomcat product installation path
*/
public static final String CATALINA_HOME_PROP = "catalina.home";
/**
* Name of the ServletContext init-param that determines if the XML parsers
* used for *.tld files will be validating or not.
* <p>
* This must be kept in sync with org.apache.catalina.Globals
*/
public static final String XML_VALIDATION_TLD_INIT_PARAM =
"org.apache.jasper.XML_VALIDATE_TLD";
/**
* Name of the ServletContext init-param that determines if the XML parsers
* will block the resolution of external entities.
* <p>
* This must be kept in sync with org.apache.catalina.Globals
*/
public static final String XML_BLOCK_EXTERNAL_INIT_PARAM =
"org.apache.jasper.XML_BLOCK_EXTERNAL";
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
/*
* 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;
/**
* Base class for all exceptions generated by the JSP engine. Makes it
* convenient to catch just this at the top-level.
*
* @author Anil K. Vijendran
*/
public class JasperException extends javax.servlet.ServletException {
private static final long serialVersionUID = 1L;
public JasperException(String reason) {
super(reason);
}
/**
* Creates a JasperException with the embedded exception and the reason for
* throwing a JasperException.
* @param reason The exception message
* @param exception The root cause
*/
public JasperException(String reason, Throwable exception) {
super(reason, exception);
}
/**
* Creates a JasperException with the embedded exception.
* @param exception The root cause
*/
public JasperException(Throwable exception) {
super(exception);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,256 @@
/*
* 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;
import java.io.File;
import java.util.Map;
import javax.servlet.jsp.tagext.TagLibraryInfo;
import org.apache.jasper.compiler.JspConfig;
import org.apache.jasper.compiler.TagPluginManager;
import org.apache.jasper.compiler.TldCache;
/**
* A class to hold all init parameters specific to the JSP engine.
*
* @author Anil K. Vijendran
* @author Hans Bergsten
* @author Pierre Delisle
*/
public interface Options {
/**
* Returns true if Jasper issues a compilation error instead of a runtime
* Instantiation error if the class attribute specified in useBean action
* is invalid.
* @return <code>true</code> to get an error
*/
public boolean getErrorOnUseBeanInvalidClassAttribute();
/**
* @return <code>true</code> to keep the generated source
*/
public boolean getKeepGenerated();
/**
* @return <code>true</code> if tag handler pooling is enabled,
* <code>false</code> otherwise.
*/
public boolean isPoolingEnabled();
/**
* @return <code>true</code> if HTML mapped Servlets are supported.
*/
public boolean getMappedFile();
/**
* @return <code>true</code> if debug information in included
* in compiled classes.
*/
public boolean getClassDebugInfo();
/**
* @return background compile thread check interval in seconds
*/
public int getCheckInterval();
/**
* Main development flag, which enables detailed error reports with
* sources, as well automatic recompilation of JSPs and tag files.
* This setting should usually be <code>false</code> when running
* in production.
* @return <code>true</code> if Jasper is in development mode
*/
public boolean getDevelopment();
/**
* @return <code>true</code> to include a source fragment in exception
* messages.
*/
public boolean getDisplaySourceFragment();
/**
* @return <code>true</code> to suppress generation of SMAP info for
* JSR45 debugging.
*/
public boolean isSmapSuppressed();
/**
* This setting is ignored if suppressSmap() is <code>true</code>.
* @return <code>true</code> to write SMAP info for JSR45 debugging to a
* file.
*/
public boolean isSmapDumped();
/**
* @return <code>true</code> to remove template text that consists entirely
* of whitespace
*/
public boolean getTrimSpaces();
/**
* Gets the class-id value that is sent to Internet Explorer when using
* &lt;jsp:plugin&gt; tags.
* @return Class-id value
*/
public String getIeClassId();
/**
* @return the work folder
*/
public File getScratchDir();
/**
* @return the classpath used to compile generated Servlets
*/
public String getClassPath();
/**
* Compiler to use.
*
* <p>
* If <code>null</code> (the default), the java compiler from Eclipse JDT
* project, bundled with Tomcat, will be used. Otherwise, the
* <code>javac</code> task from Apache Ant will be used to call an external
* java compiler and the value of this option will be passed to it. See
* Apache Ant documentation for the possible values.
* @return the compiler name
*/
public String getCompiler();
/**
* @return the compiler target VM, e.g. 1.8.
*/
public String getCompilerTargetVM();
/**
* @return the compiler source VM, e.g. 1.8.
*/
public String getCompilerSourceVM();
/**
* @return Jasper Java compiler class to use.
*/
public String getCompilerClassName();
/**
* The cache that maps URIs, resource paths and parsed TLD files for the
* various tag libraries 'exposed' by the web application.
* A tag library is 'exposed' either explicitly in
* web.xml or implicitly via the uri tag in the TLD
* of a taglib deployed in a jar file (WEB-INF/lib).
*
* @return the instance of the TldLocationsCache
* for the web-application.
*/
public TldCache getTldCache();
/**
* @return Java platform encoding to generate the JSP page servlet.
*/
public String getJavaEncoding();
/**
* The boolean flag to tell Ant whether to fork JSP page compilations.
*
* <p>
* Is used only when Jasper uses an external java compiler (wrapped through
* a <code>javac</code> Apache Ant task).
* @return <code>true</code> to fork a process during compilation
*/
public boolean getFork();
/**
* @return JSP configuration information specified in web.xml.
*/
public JspConfig getJspConfig();
/**
* @return <code>true</code> to generate a X-Powered-By response header.
*/
public boolean isXpoweredBy();
/**
* @return a Tag Plugin Manager
*/
public TagPluginManager getTagPluginManager();
/**
* Indicates whether text strings are to be generated as char arrays.
*
* @return <code>true</code> if text strings are to be generated as char
* arrays, <code>false</code> otherwise
*/
public boolean genStringAsCharArray();
/**
* @return modification test interval.
*/
public int getModificationTestInterval();
/**
* @return <code>true</code> if re-compile will occur on a failure.
*/
public boolean getRecompileOnFail();
/**
* @return <code>true</code> is caching is enabled
* (used for precompilation).
*/
public boolean isCaching();
/**
* The web-application wide cache for the TagLibraryInfo tag library
* descriptors, used if {@link #isCaching()} returns <code>true</code>.
*
* <p>
* Using this cache avoids the cost of repeating the parsing of a tag
* library descriptor XML file (performed by TagLibraryInfoImpl.parseTLD).
* </p>
*
* @return the Map(String uri, TagLibraryInfo tld) instance.
*/
public Map<String, TagLibraryInfo> getCache();
/**
* The maximum number of loaded jsps per web-application. If there are more
* jsps loaded, they will be unloaded. If unset or less than 0, no jsps
* are unloaded.
* @return The JSP count
*/
public int getMaxLoadedJsps();
/**
* @return the idle time in seconds after which a JSP is unloaded.
* If unset or less or equal than 0, no jsps are unloaded.
*/
public int getJspIdleTimeout();
/**
* @return {@code true} if the quote escaping required by section JSP.1.6 of
* the JSP specification should be applied to scriplet expression.
*/
public boolean getStrictQuoteEscaping();
/**
* @return {@code true} if EL expressions used within attributes should have
* the quoting rules in JSP.1.6 applied to the expression.
*/
public boolean getQuoteAttributeEL();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,340 @@
/*
* 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.compiler;
/**
* Converts a JSP attribute value into the unquoted equivalent. The attribute
* may contain EL expressions, in which case care needs to be taken to avoid any
* ambiguities. For example, consider the attribute values "${1+1}" and
* "\${1+1}". After unquoting, both appear as "${1+1}" but the first should
* evaluate to "2" and the second to "${1+1}". Literal \, $ and # need special
* treatment to ensure there is no ambiguity. The JSP attribute unquoting
* covers \\, \", \', \$, \#, %\&gt;, &lt;\%, &amp;apos; and &amp;quot;
*/
public class AttributeParser {
/**
* Parses the provided input String as a JSP attribute and returns an
* unquoted value.
*
* @param input The input.
* @param quote The quote character for the attribute or 0 for
* scripting expressions.
* @param isELIgnored Is expression language being ignored on the page
* where the JSP attribute is defined.
* @param isDeferredSyntaxAllowedAsLiteral
* Are deferred expressions treated as literals?
* @param strict Should the rules of JSP.1.6 for escaping of quotes
* be strictly applied?
* @param quoteAttributeEL Should the rules of JSP.1.6 for escaping in
* attributes be applied to EL in attribute values?
* @return An unquoted JSP attribute that, if it contains
* expression language can be safely passed to the EL
* processor without fear of ambiguity.
*/
public static String getUnquoted(String input, char quote,
boolean isELIgnored, boolean isDeferredSyntaxAllowedAsLiteral,
boolean strict, boolean quoteAttributeEL) {
return new AttributeParser(input, quote, isELIgnored,
isDeferredSyntaxAllowedAsLiteral, strict, quoteAttributeEL).getUnquoted();
}
/* The quoted input string. */
private final String input;
/* The quote used for the attribute - null for scripting expressions. */
private final char quote;
/* Is expression language being ignored - affects unquoting. \$ and \# are
* treated as literals rather than quoted values. */
private final boolean isELIgnored;
/* Are deferred expression treated as literals */
private final boolean isDeferredSyntaxAllowedAsLiteral;
/* If a quote appears that matches quote, must it always be escaped? See
* JSP.1.6.
*/
private final boolean strict;
private final boolean quoteAttributeEL;
/* The type ($ or #) of expression. Literals have a type of null. */
private final char type;
/* The length of the quoted input string. */
private final int size;
/* Tracks the current position of the parser in the input String. */
private int i = 0;
/* Indicates if the last character returned by nextChar() was escaped. */
private boolean lastChEscaped = false;
/* The unquoted result. */
private final StringBuilder result;
/**
* For test purposes.
* @param input
* @param quote
* @param strict
*/
private AttributeParser(String input, char quote,
boolean isELIgnored, boolean isDeferredSyntaxAllowedAsLiteral,
boolean strict, boolean quoteAttributeEL) {
this.input = input;
this.quote = quote;
this.isELIgnored = isELIgnored;
this.isDeferredSyntaxAllowedAsLiteral =
isDeferredSyntaxAllowedAsLiteral;
this.strict = strict;
this.quoteAttributeEL = quoteAttributeEL;
this.type = getType(input);
this.size = input.length();
result = new StringBuilder(size);
}
/*
* Work through input looking for literals and expressions until the input
* has all been read.
*/
private String getUnquoted() {
while (i < size) {
parseLiteral();
parseEL();
}
return result.toString();
}
/*
* This method gets the next unquoted character and looks for
* - literals that need to be converted for EL processing
* \ -> type{'\\'}
* $ -> type{'$'}
* # -> type{'#'}
* - start of EL
* ${
* #{
* Note all the examples above *do not* include the escaping required to use
* the values in Java code.
*/
private void parseLiteral() {
boolean foundEL = false;
while (i < size && !foundEL) {
char ch = nextChar();
if (!isELIgnored && ch == '\\') {
if (type == 0) {
result.append("\\");
} else {
result.append(type);
result.append("{'\\\\'}");
}
} else if (!isELIgnored && ch == '$' && lastChEscaped){
if (type == 0) {
result.append("\\$");
} else {
result.append(type);
result.append("{'$'}");
}
} else if (!isELIgnored && ch == '#' && lastChEscaped){
// Note if isDeferredSyntaxAllowedAsLiteral==true, \# will
// not be treated as an escape
if (type == 0) {
result.append("\\#");
} else {
result.append(type);
result.append("{'#'}");
}
} else if (ch == type){
if (i < size) {
char next = input.charAt(i);
if (next == '{') {
foundEL = true;
// Move back to start of EL
i--;
} else {
result.append(ch);
}
} else {
result.append(ch);
}
} else {
result.append(ch);
}
}
}
/*
* Once inside EL, no need to unquote or convert anything. The EL is
* terminated by '}'. The only other valid location for '}' is inside a
* StringLiteral. The literals are delimited by '\'' or '\"'. The only other
* valid location for '\'' or '\"' is also inside a StringLiteral. A quote
* character inside a StringLiteral must be escaped if the same quote
* character is used to delimit the StringLiteral.
*/
private void parseEL() {
boolean endEL = false;
boolean insideLiteral = false;
char literalQuote = 0;
while (i < size && !endEL) {
char ch;
if (quoteAttributeEL) {
ch = nextChar();
} else {
ch = input.charAt(i++);
}
if (ch == '\'' || ch == '\"') {
if (insideLiteral) {
if (literalQuote == ch) {
insideLiteral = false;
}
} else {
insideLiteral = true;
literalQuote = ch;
}
result.append(ch);
} else if (ch == '\\') {
result.append(ch);
if (insideLiteral && size < i) {
if (quoteAttributeEL) {
ch = nextChar();
} else {
ch = input.charAt(i++);
}
result.append(ch);
}
} else if (ch == '}') {
if (!insideLiteral) {
endEL = true;
}
result.append(ch);
} else {
result.append(ch);
}
}
}
/*
* Returns the next unquoted character and sets the lastChEscaped flag to
* indicate if it was quoted/escaped or not.
* &apos; is always unquoted to '
* &quot; is always unquoted to "
* \" is always unquoted to "
* \' is always unquoted to '
* \\ is always unquoted to \
* \$ is unquoted to $ if EL is not being ignored
* \# is unquoted to # if EL is not being ignored
* <\% is always unquoted to <%
* %\> is always unquoted to %>
*/
private char nextChar() {
lastChEscaped = false;
char ch = input.charAt(i);
if (ch == '&') {
if (i + 5 < size && input.charAt(i + 1) == 'a' &&
input.charAt(i + 2) == 'p' && input.charAt(i + 3) == 'o' &&
input.charAt(i + 4) == 's' && input.charAt(i + 5) == ';') {
ch = '\'';
i += 6;
} else if (i + 5 < size && input.charAt(i + 1) == 'q' &&
input.charAt(i + 2) == 'u' && input.charAt(i + 3) == 'o' &&
input.charAt(i + 4) == 't' && input.charAt(i + 5) == ';') {
ch = '\"';
i += 6;
} else {
++i;
}
} else if (ch == '\\' && i + 1 < size) {
ch = input.charAt(i + 1);
if (ch == '\\' || ch == '\"' || ch == '\'' ||
(!isELIgnored &&
(ch == '$' ||
(!isDeferredSyntaxAllowedAsLiteral &&
ch == '#')))) {
i += 2;
lastChEscaped = true;
} else {
ch = '\\';
++i;
}
} else if (ch == '<' && (i + 2 < size) && input.charAt(i + 1) == '\\' &&
input.charAt(i + 2) == '%') {
// Note this is a hack since nextChar only returns a single char
// It is safe since <% does not require special treatment for EL
// or for literals
result.append('<');
i+=3;
return '%';
} else if (ch == '%' && i + 2 < size && input.charAt(i + 1) == '\\' &&
input.charAt(i + 2) == '>') {
// Note this is a hack since nextChar only returns a single char
// It is safe since %> does not require special treatment for EL
// or for literals
result.append('%');
i+=3;
return '>';
} else if (ch == quote && strict) {
String msg = Localizer.getMessage("jsp.error.attribute.noescape",
input, ""+ quote);
throw new IllegalArgumentException(msg);
} else {
++i;
}
return ch;
}
/*
* Determines the type of expression by looking for the first unquoted ${
* or #{.
*/
private char getType(String value) {
if (value == null) {
return 0;
}
if (isELIgnored) {
return 0;
}
int j = 0;
int len = value.length();
char current;
while (j < len) {
current = value.charAt(j);
if (current == '\\') {
// Escape character - skip a character
j++;
} else if (current == '#' && !isDeferredSyntaxAllowedAsLiteral) {
if (j < (len -1) && value.charAt(j + 1) == '{') {
return '#';
}
} else if (current == '$') {
if (j < (len - 1) && value.charAt(j + 1) == '{') {
return '$';
}
}
j++;
}
return 0;
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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.compiler;
import java.util.HashMap;
import org.apache.jasper.JasperException;
/**
* Repository of {page, request, session, application}-scoped beans
*
* @author Mandar Raje
* @author Remy Maucherat
*/
public class BeanRepository {
private final HashMap<String, String> beanTypes;
private final ClassLoader loader;
private final ErrorDispatcher errDispatcher;
/**
* Constructor.
* @param loader The class loader
* @param err The error dispatcher that will be used to report errors
*/
public BeanRepository(ClassLoader loader, ErrorDispatcher err) {
this.loader = loader;
this.errDispatcher = err;
beanTypes = new HashMap<>();
}
public void addBean(Node.UseBean n, String s, String type, String scope)
throws JasperException {
if (!(scope == null || scope.equals("page") || scope.equals("request")
|| scope.equals("session") || scope.equals("application"))) {
errDispatcher.jspError(n, "jsp.error.usebean.badScope");
}
beanTypes.put(s, type);
}
public Class<?> getBeanType(String bean)
throws JasperException {
Class<?> clazz = null;
try {
clazz = loader.loadClass(beanTypes.get(bean));
} catch (ClassNotFoundException ex) {
throw new JasperException (ex);
}
return clazz;
}
public boolean checkVariable(String bean) {
return beanTypes.containsKey(bean);
}
}

View File

@@ -0,0 +1,217 @@
/*
* 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.compiler;
import org.apache.jasper.JasperException;
/**
* Collect info about the page and nodes, and make them available through
* the PageInfo object.
*
* @author Kin-man Chung
* @author Mark Roth
*/
class Collector {
/**
* A visitor for collecting information on the page and the body of
* the custom tags.
*/
private static class CollectVisitor extends Node.Visitor {
private boolean scriptingElementSeen = false;
private boolean usebeanSeen = false;
private boolean includeActionSeen = false;
private boolean paramActionSeen = false;
private boolean setPropertySeen = false;
private boolean hasScriptingVars = false;
@Override
public void visit(Node.ParamAction n) throws JasperException {
if (n.getValue().isExpression()) {
scriptingElementSeen = true;
}
paramActionSeen = true;
}
@Override
public void visit(Node.IncludeAction n) throws JasperException {
if (n.getPage().isExpression()) {
scriptingElementSeen = true;
}
includeActionSeen = true;
visitBody(n);
}
@Override
public void visit(Node.ForwardAction n) throws JasperException {
if (n.getPage().isExpression()) {
scriptingElementSeen = true;
}
visitBody(n);
}
@Override
public void visit(Node.SetProperty n) throws JasperException {
if (n.getValue() != null && n.getValue().isExpression()) {
scriptingElementSeen = true;
}
setPropertySeen = true;
}
@Override
public void visit(Node.UseBean n) throws JasperException {
if (n.getBeanName() != null && n.getBeanName().isExpression()) {
scriptingElementSeen = true;
}
usebeanSeen = true;
visitBody(n);
}
@Override
public void visit(Node.PlugIn n) throws JasperException {
if (n.getHeight() != null && n.getHeight().isExpression()) {
scriptingElementSeen = true;
}
if (n.getWidth() != null && n.getWidth().isExpression()) {
scriptingElementSeen = true;
}
visitBody(n);
}
@Override
public void visit(Node.CustomTag n) throws JasperException {
// Check to see what kinds of element we see as child elements
checkSeen( n.getChildInfo(), n );
}
/**
* Check all child nodes for various elements and update the given
* ChildInfo object accordingly. Visits body in the process.
*/
private void checkSeen( Node.ChildInfo ci, Node n )
throws JasperException
{
// save values collected so far
boolean scriptingElementSeenSave = scriptingElementSeen;
scriptingElementSeen = false;
boolean usebeanSeenSave = usebeanSeen;
usebeanSeen = false;
boolean includeActionSeenSave = includeActionSeen;
includeActionSeen = false;
boolean paramActionSeenSave = paramActionSeen;
paramActionSeen = false;
boolean setPropertySeenSave = setPropertySeen;
setPropertySeen = false;
boolean hasScriptingVarsSave = hasScriptingVars;
hasScriptingVars = false;
// Scan attribute list for expressions
if( n instanceof Node.CustomTag ) {
Node.CustomTag ct = (Node.CustomTag)n;
Node.JspAttribute[] attrs = ct.getJspAttributes();
for (int i = 0; attrs != null && i < attrs.length; i++) {
if (attrs[i].isExpression()) {
scriptingElementSeen = true;
break;
}
}
}
visitBody(n);
if( (n instanceof Node.CustomTag) && !hasScriptingVars) {
Node.CustomTag ct = (Node.CustomTag)n;
hasScriptingVars = ct.getVariableInfos().length > 0 ||
ct.getTagVariableInfos().length > 0;
}
// Record if the tag element and its body contains any scriptlet.
ci.setScriptless(! scriptingElementSeen);
ci.setHasUseBean(usebeanSeen);
ci.setHasIncludeAction(includeActionSeen);
ci.setHasParamAction(paramActionSeen);
ci.setHasSetProperty(setPropertySeen);
ci.setHasScriptingVars(hasScriptingVars);
// Propagate value of scriptingElementSeen up.
scriptingElementSeen = scriptingElementSeen || scriptingElementSeenSave;
usebeanSeen = usebeanSeen || usebeanSeenSave;
setPropertySeen = setPropertySeen || setPropertySeenSave;
includeActionSeen = includeActionSeen || includeActionSeenSave;
paramActionSeen = paramActionSeen || paramActionSeenSave;
hasScriptingVars = hasScriptingVars || hasScriptingVarsSave;
}
@Override
public void visit(Node.JspElement n) throws JasperException {
if (n.getNameAttribute().isExpression())
scriptingElementSeen = true;
Node.JspAttribute[] attrs = n.getJspAttributes();
for (int i = 0; i < attrs.length; i++) {
if (attrs[i].isExpression()) {
scriptingElementSeen = true;
break;
}
}
visitBody(n);
}
@Override
public void visit(Node.JspBody n) throws JasperException {
checkSeen( n.getChildInfo(), n );
}
@Override
public void visit(Node.NamedAttribute n) throws JasperException {
checkSeen( n.getChildInfo(), n );
}
@Override
public void visit(Node.Declaration n) throws JasperException {
scriptingElementSeen = true;
}
@Override
public void visit(Node.Expression n) throws JasperException {
scriptingElementSeen = true;
}
@Override
public void visit(Node.Scriptlet n) throws JasperException {
scriptingElementSeen = true;
}
private void updatePageInfo(PageInfo pageInfo) {
pageInfo.setScriptless(! scriptingElementSeen);
}
}
public static void collect(Compiler compiler, Node.Nodes page)
throws JasperException {
CollectVisitor collectVisitor = new CollectVisitor();
page.visit(collectVisitor);
collectVisitor.updatePageInfo(compiler.getPageInfo());
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,120 @@
/*
* 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.compiler;
import org.apache.jasper.JasperException;
/**
* Default implementation of ErrorHandler interface.
*
* @author Jan Luehe
*/
class DefaultErrorHandler implements ErrorHandler {
/*
* Processes the given JSP parse error.
*
* @param fname Name of the JSP file in which the parse error occurred
* @param line Parse error line number
* @param column Parse error column number
* @param errMsg Parse error message
* @param exception Parse exception
*/
@Override
public void jspError(String fname, int line, int column, String errMsg,
Exception ex) throws JasperException {
throw new JasperException(fname + " (" +
Localizer.getMessage("jsp.error.location",
Integer.toString(line), Integer.toString(column)) +
") " + errMsg, ex);
}
/*
* Processes the given JSP parse error.
*
* @param errMsg Parse error message
* @param exception Parse exception
*/
@Override
public void jspError(String errMsg, Exception ex) throws JasperException {
throw new JasperException(errMsg, ex);
}
/*
* Processes the given javac compilation errors.
*
* @param details Array of JavacErrorDetail instances corresponding to the
* compilation errors
*/
@Override
public void javacError(JavacErrorDetail[] details) throws JasperException {
if (details == null) {
return;
}
Object[] args = null;
StringBuilder buf = new StringBuilder();
for (int i=0; i < details.length; i++) {
if (details[i].getJspBeginLineNumber() >= 0) {
args = new Object[] {
Integer.valueOf(details[i].getJspBeginLineNumber()),
details[i].getJspFileName() };
buf.append(System.lineSeparator());
buf.append(System.lineSeparator());
buf.append(Localizer.getMessage("jsp.error.single.line.number",
args));
buf.append(System.lineSeparator());
buf.append(details[i].getErrorMessage());
buf.append(System.lineSeparator());
buf.append(details[i].getJspExtract());
} else {
args = new Object[] {
Integer.valueOf(details[i].getJavaLineNumber()),
details[i].getJavaFileName() };
buf.append(System.lineSeparator());
buf.append(System.lineSeparator());
buf.append(Localizer.getMessage("jsp.error.java.line.number",
args));
buf.append(System.lineSeparator());
buf.append(details[i].getErrorMessage());
}
}
buf.append(System.lineSeparator());
buf.append(System.lineSeparator());
buf.append("Stacktrace:");
throw new JasperException(
Localizer.getMessage("jsp.error.unable.compile") + ": " + buf);
}
/**
* Processes the given javac error report and exception.
*
* @param errorReport Compilation error report
* @param exception Compilation exception
*/
@Override
public void javacError(String errorReport, Exception exception)
throws JasperException {
throw new JasperException(
Localizer.getMessage("jsp.error.unable.compile"), exception);
}
}

View File

@@ -0,0 +1,331 @@
/*
* 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.compiler;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.jsp.tagext.FunctionInfo;
import org.apache.jasper.Constants;
import org.apache.jasper.JasperException;
import org.apache.tomcat.util.security.PrivilegedGetTccl;
/**
* This class generates functions mappers for the EL expressions in the page.
* Instead of a global mapper, a mapper is used for each call to EL
* evaluator, thus avoiding the prefix overlapping and redefinition
* issues.
*
* @author Kin-man Chung
*/
public class ELFunctionMapper {
private int currFunc = 0;
private StringBuilder ds; // Contains codes to initialize the functions mappers.
private StringBuilder ss; // Contains declarations of the functions mappers.
/**
* Creates the functions mappers for all EL expressions in the JSP page.
*
* @param page The current compilation unit.
* @throws JasperException EL error
*/
public static void map(Node.Nodes page)
throws JasperException {
ELFunctionMapper map = new ELFunctionMapper();
map.ds = new StringBuilder();
map.ss = new StringBuilder();
page.visit(map.new ELFunctionVisitor());
// Append the declarations to the root node
String ds = map.ds.toString();
if (ds.length() > 0) {
Node root = page.getRoot();
@SuppressWarnings("unused")
Node unused = new Node.Declaration(map.ss.toString(), null, root);
unused = new Node.Declaration(
"static {\n" + ds + "}\n", null, root);
}
}
/**
* A visitor for the page. The places where EL is allowed are scanned
* for functions, and if found functions mappers are created.
*/
private class ELFunctionVisitor extends Node.Visitor {
/**
* Use a global name map to facilitate reuse of function maps.
* The key used is prefix:function:uri.
*/
private final HashMap<String, String> gMap = new HashMap<>();
@Override
public void visit(Node.ParamAction n) throws JasperException {
doMap(n.getValue());
visitBody(n);
}
@Override
public void visit(Node.IncludeAction n) throws JasperException {
doMap(n.getPage());
visitBody(n);
}
@Override
public void visit(Node.ForwardAction n) throws JasperException {
doMap(n.getPage());
visitBody(n);
}
@Override
public void visit(Node.SetProperty n) throws JasperException {
doMap(n.getValue());
visitBody(n);
}
@Override
public void visit(Node.UseBean n) throws JasperException {
doMap(n.getBeanName());
visitBody(n);
}
@Override
public void visit(Node.PlugIn n) throws JasperException {
doMap(n.getHeight());
doMap(n.getWidth());
visitBody(n);
}
@Override
public void visit(Node.JspElement n) throws JasperException {
Node.JspAttribute[] attrs = n.getJspAttributes();
for (int i = 0; attrs != null && i < attrs.length; i++) {
doMap(attrs[i]);
}
doMap(n.getNameAttribute());
visitBody(n);
}
@Override
public void visit(Node.UninterpretedTag n) throws JasperException {
Node.JspAttribute[] attrs = n.getJspAttributes();
for (int i = 0; attrs != null && i < attrs.length; i++) {
doMap(attrs[i]);
}
visitBody(n);
}
@Override
public void visit(Node.CustomTag n) throws JasperException {
Node.JspAttribute[] attrs = n.getJspAttributes();
for (int i = 0; attrs != null && i < attrs.length; i++) {
doMap(attrs[i]);
}
visitBody(n);
}
@Override
public void visit(Node.ELExpression n) throws JasperException {
doMap(n.getEL());
}
private void doMap(Node.JspAttribute attr)
throws JasperException {
if (attr != null) {
doMap(attr.getEL());
}
}
/**
* Creates function mappers, if needed, from ELNodes
*/
private void doMap(ELNode.Nodes el)
throws JasperException {
// Only care about functions in ELNode's
class Fvisitor extends ELNode.Visitor {
private final List<ELNode.Function> funcs = new ArrayList<>();
private final Set<String> keySet = new HashSet<>();
@Override
public void visit(ELNode.Function n) throws JasperException {
String key = n.getPrefix() + ":" + n.getName();
if (keySet.add(key)) {
funcs.add(n);
}
}
}
if (el == null) {
return;
}
// First locate all unique functions in this EL
Fvisitor fv = new Fvisitor();
el.visit(fv);
List<ELNode.Function> functions = fv.funcs;
if (functions.size() == 0) {
return;
}
// Reuse a previous map if possible
String decName = matchMap(functions);
if (decName != null) {
el.setMapName(decName);
return;
}
// Generate declaration for the map statically
decName = getMapName();
ss.append("private static org.apache.jasper.runtime.ProtectedFunctionMapper " + decName + ";\n");
ds.append(" " + decName + "= ");
ds.append("org.apache.jasper.runtime.ProtectedFunctionMapper");
// Special case if there is only one function in the map
String funcMethod = null;
if (functions.size() == 1) {
funcMethod = ".getMapForFunction";
} else {
ds.append(".getInstance();\n");
funcMethod = " " + decName + ".mapFunction";
}
// Setup arguments for either getMapForFunction or mapFunction
for (ELNode.Function f : functions) {
FunctionInfo funcInfo = f.getFunctionInfo();
String fnQName = f.getPrefix() + ":" + f.getName();
if (funcInfo == null) {
// Added via Lambda or ImportHandler. EL will expect a
// function mapper even if one isn't used so just pass null
ds.append(funcMethod + "(null, null, null, null);\n");
} else {
ds.append(funcMethod + "(\"" + fnQName + "\", " +
getCanonicalName(funcInfo.getFunctionClass()) +
".class, " + '\"' + f.getMethodName() + "\", " +
"new Class[] {");
String params[] = f.getParameters();
for (int k = 0; k < params.length; k++) {
if (k != 0) {
ds.append(", ");
}
int iArray = params[k].indexOf('[');
if (iArray < 0) {
ds.append(params[k] + ".class");
}
else {
String baseType = params[k].substring(0, iArray);
ds.append("java.lang.reflect.Array.newInstance(");
ds.append(baseType);
ds.append(".class,");
// Count the number of array dimension
int aCount = 0;
for (int jj = iArray; jj < params[k].length(); jj++ ) {
if (params[k].charAt(jj) == '[') {
aCount++;
}
}
if (aCount == 1) {
ds.append("0).getClass()");
} else {
ds.append("new int[" + aCount + "]).getClass()");
}
}
}
ds.append("});\n");
}
// Put the current name in the global function map
gMap.put(fnQName + ':' + f.getUri(), decName);
}
el.setMapName(decName);
}
/**
* Find the name of the function mapper for an EL. Reuse a
* previously generated one if possible.
* @param functions A List of ELNode.Function instances that
* represents the functions in an EL
* @return A previous generated function mapper name that can be used
* by this EL; null if none found.
*/
private String matchMap(List<ELNode.Function> functions) {
String mapName = null;
for (ELNode.Function f : functions) {
String temName = gMap.get(f.getPrefix() + ':' + f.getName() +
':' + f.getUri());
if (temName == null) {
return null;
}
if (mapName == null) {
mapName = temName;
} else if (!temName.equals(mapName)) {
// If not all in the previous match, then no match.
return null;
}
}
return mapName;
}
/*
* @return A unique name for a function mapper.
*/
private String getMapName() {
return "_jspx_fnmap_" + currFunc++;
}
/**
* Convert a binary class name into a canonical one that can be used
* when generating Java source code.
*
* @param className Binary class name
* @return Canonical equivalent
*/
private String getCanonicalName(String className) throws JasperException {
Class<?> clazz;
ClassLoader tccl;
if (Constants.IS_SECURITY_ENABLED) {
PrivilegedAction<ClassLoader> pa = new PrivilegedGetTccl();
tccl = AccessController.doPrivileged(pa);
} else {
tccl = Thread.currentThread().getContextClassLoader();
}
try {
clazz = Class.forName(className, false, tccl);
} catch (ClassNotFoundException e) {
throw new JasperException(e);
}
return clazz.getCanonicalName();
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.compiler;
import org.apache.jasper.JspCompilationContext;
/**
* Defines the interface for the expression language interpreter. This allows
* users to provide custom EL interpreter implementations that can optimise
* EL processing for an application by , for example, performing code generation
* for simple expressions.
*/
public interface ELInterpreter {
/**
* Returns the string representing the code that will be inserted into the
* servlet generated for JSP. The default implementation creates a call to
* {@link org.apache.jasper.runtime.PageContextImpl#proprietaryEvaluate(
* String, Class, javax.servlet.jsp.PageContext,
* org.apache.jasper.runtime.ProtectedFunctionMapper)} but other
* implementations may produce more optimised code.
* @param context The compilation context
* @param isTagFile <code>true</code> if in a tag file rather than a JSP
* @param expression a String containing zero or more "${}" expressions
* @param expectedType the expected type of the interpreted result
* @param fnmapvar Variable pointing to a function map.
* @return a String representing a call to the EL interpreter.
*/
public String interpreterCall(JspCompilationContext context,
boolean isTagFile, String expression,
Class<?> expectedType, String fnmapvar);
}

View File

@@ -0,0 +1,107 @@
/*
* 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.compiler;
import javax.servlet.ServletContext;
import org.apache.jasper.JspCompilationContext;
/**
* Provides {@link ELInterpreter} instances for JSP compilation.
*
* The search order is as follows:
* <ol>
* <li>ELInterpreter instance or implementation class name provided as a
* ServletContext attribute</li>
* <li>Implementation class named in a ServletContext initialisation parameter
* </li>
* <li>Default implementation</li>
* </ol>
*/
public class ELInterpreterFactory {
public static final String EL_INTERPRETER_CLASS_NAME =
ELInterpreter.class.getName();
private static final ELInterpreter DEFAULT_INSTANCE =
new DefaultELInterpreter();
/**
* Obtain the correct EL Interpreter for the given web application.
* @param context The Servlet context
* @return the EL interpreter
* @throws Exception If an error occurs creating the interpreter
*/
public static ELInterpreter getELInterpreter(ServletContext context)
throws Exception {
ELInterpreter result = null;
// Search for an implementation
// 1. ServletContext attribute (set by application or cached by a
// previous call to this method).
Object attribute = context.getAttribute(EL_INTERPRETER_CLASS_NAME);
if (attribute instanceof ELInterpreter) {
return (ELInterpreter) attribute;
} else if (attribute instanceof String) {
result = createInstance(context, (String) attribute);
}
// 2. ServletContext init parameter
if (result == null) {
String className =
context.getInitParameter(EL_INTERPRETER_CLASS_NAME);
if (className != null) {
result = createInstance(context, className);
}
}
// 3. Default
if (result == null) {
result = DEFAULT_INSTANCE;
}
// Cache the result for next time
context.setAttribute(EL_INTERPRETER_CLASS_NAME, result);
return result;
}
private static ELInterpreter createInstance(ServletContext context,
String className) throws Exception {
return (ELInterpreter) context.getClassLoader().loadClass(
className).getConstructor().newInstance();
}
private ELInterpreterFactory() {
// Utility class. Hide default constructor.
}
public static class DefaultELInterpreter implements ELInterpreter {
@Override
public String interpreterCall(JspCompilationContext context,
boolean isTagFile, String expression,
Class<?> expectedType, String fnmapvar) {
return JspUtil.interpreterCall(isTagFile, expression, expectedType,
fnmapvar);
}
}
}

View File

@@ -0,0 +1,270 @@
/*
* 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.compiler;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.servlet.jsp.tagext.FunctionInfo;
import org.apache.jasper.JasperException;
/**
* This class defines internal representation for an EL Expression
*
* It currently only defines functions. It can be expanded to define
* all the components of an EL expression, if need to.
*
* @author Kin-man Chung
*/
abstract class ELNode {
public abstract void accept(Visitor v) throws JasperException;
/**
* Represents an EL expression: anything in ${ and }.
*/
public static class Root extends ELNode {
private final ELNode.Nodes expr;
private final char type;
Root(ELNode.Nodes expr, char type) {
this.expr = expr;
this.type = type;
}
@Override
public void accept(Visitor v) throws JasperException {
v.visit(this);
}
public ELNode.Nodes getExpression() {
return expr;
}
public char getType() {
return type;
}
}
/**
* Represents text outside of EL expression.
*/
public static class Text extends ELNode {
private final String text;
Text(String text) {
this.text = text;
}
@Override
public void accept(Visitor v) throws JasperException {
v.visit(this);
}
public String getText() {
return text;
}
}
/**
* Represents anything in EL expression, other than functions, including
* function arguments etc
*/
public static class ELText extends ELNode {
private final String text;
ELText(String text) {
this.text = text;
}
@Override
public void accept(Visitor v) throws JasperException {
v.visit(this);
}
public String getText() {
return text;
}
}
/**
* Represents a function
* Currently only include the prefix and function name, but not its
* arguments.
*/
public static class Function extends ELNode {
private final String prefix;
private final String name;
private final String originalText;
private String uri;
private FunctionInfo functionInfo;
private String methodName;
private String[] parameters;
Function(String prefix, String name, String originalText) {
this.prefix = prefix;
this.name = name;
this.originalText = originalText;
}
@Override
public void accept(Visitor v) throws JasperException {
v.visit(this);
}
public String getPrefix() {
return prefix;
}
public String getName() {
return name;
}
public String getOriginalText() {
return originalText;
}
public void setUri(String uri) {
this.uri = uri;
}
public String getUri() {
return uri;
}
public void setFunctionInfo(FunctionInfo f) {
this.functionInfo = f;
}
public FunctionInfo getFunctionInfo() {
return functionInfo;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public String getMethodName() {
return methodName;
}
public void setParameters(String[] parameters) {
this.parameters = parameters;
}
public String[] getParameters() {
return parameters;
}
}
/**
* An ordered list of ELNode.
*/
public static class Nodes {
/* Name used for creating a map for the functions in this
EL expression, for communication to Generator.
*/
private String mapName = null; // The function map associated this EL
private final List<ELNode> list;
public Nodes() {
list = new ArrayList<>();
}
public void add(ELNode en) {
list.add(en);
}
/**
* Visit the nodes in the list with the supplied visitor.
*
* @param v The visitor used
*
* @throws JasperException if an error occurs while visiting a node
*/
public void visit(Visitor v) throws JasperException {
for (ELNode n : list) {
n.accept(v);
}
}
public Iterator<ELNode> iterator() {
return list.iterator();
}
public boolean isEmpty() {
return list.size() == 0;
}
/**
* @return true if the expression contains a ${...}
*/
public boolean containsEL() {
for (ELNode n : list) {
if (n instanceof Root) {
return true;
}
}
return false;
}
public void setMapName(String name) {
this.mapName = name;
}
public String getMapName() {
return mapName;
}
}
/*
* A visitor class for traversing ELNodes
*/
public static class Visitor {
public void visit(Root n) throws JasperException {
n.getExpression().visit(this);
}
@SuppressWarnings("unused")
public void visit(Function n) throws JasperException {
// NOOP by default
}
@SuppressWarnings("unused")
public void visit(Text n) throws JasperException {
// NOOP by default
}
@SuppressWarnings("unused")
public void visit(ELText n) throws JasperException {
// NOOP by default
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,217 @@
/*
* 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.compiler;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
/*
* The BoM detection is derived from:
* https://svn.us.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/xmlparser/XMLEncodingDetector.java?annotate=1742248
*
* The prolog is always at least as specific as the BOM therefore any encoding
* specified in the prolog should take priority over the BOM.
*/
class EncodingDetector {
private static final XMLInputFactory XML_INPUT_FACTORY;
static {
XML_INPUT_FACTORY = XMLInputFactory.newInstance();
}
private final String encoding;
private final int skip;
private final boolean encodingSpecifiedInProlog;
/*
* TODO: Refactor Jasper InputStream creation and handling so the
* InputStream passed to this method is buffered and therefore saves
* on multiple opening and re-opening of the same file.
*/
EncodingDetector(InputStream is) throws IOException {
// Keep buffer size to a minimum here. BoM will be no more than 4 bytes
// so that is the maximum we need to buffer
BufferedInputStream bis = new BufferedInputStream(is, 4);
bis.mark(4);
BomResult bomResult = processBom(bis);
// Reset the stream back to the start to allow the XML prolog detection
// to work. Skip any BoM we discovered.
bis.reset();
for (int i = 0; i < bomResult.skip; i++) {
bis.read();
}
String prologEncoding = getPrologEncoding(bis);
if (prologEncoding == null) {
encodingSpecifiedInProlog = false;
encoding = bomResult.encoding;
} else {
encodingSpecifiedInProlog = true;
encoding = prologEncoding;
}
skip = bomResult.skip;
}
String getEncoding() {
return encoding;
}
int getSkip() {
return skip;
}
boolean isEncodingSpecifiedInProlog() {
return encodingSpecifiedInProlog;
}
private String getPrologEncoding(InputStream stream) {
String encoding = null;
try {
XMLStreamReader xmlStreamReader = XML_INPUT_FACTORY.createXMLStreamReader(stream);
encoding = xmlStreamReader.getCharacterEncodingScheme();
} catch (XMLStreamException e) {
// Ignore
}
return encoding;
}
private BomResult processBom(InputStream stream) {
// Read first four bytes (or as many are available) and determine
// encoding
try {
final byte[] b4 = new byte[4];
int count = 0;
int singleByteRead;
while (count < 4) {
singleByteRead = stream.read();
if (singleByteRead == -1) {
break;
}
b4[count] = (byte) singleByteRead;
count++;
}
return parseBom(b4, count);
} catch (IOException ioe) {
// Failed.
return new BomResult("UTF-8", 0);
}
}
private BomResult parseBom(byte[] b4, int count) {
if (count < 2) {
return new BomResult("UTF-8", 0);
}
// UTF-16, with BOM
int b0 = b4[0] & 0xFF;
int b1 = b4[1] & 0xFF;
if (b0 == 0xFE && b1 == 0xFF) {
// UTF-16, big-endian
return new BomResult("UTF-16BE", 2);
}
if (b0 == 0xFF && b1 == 0xFE) {
// UTF-16, little-endian
return new BomResult("UTF-16LE", 2);
}
// default to UTF-8 if we don't have enough bytes to make a
// good determination of the encoding
if (count < 3) {
return new BomResult("UTF-8", 0);
}
// UTF-8 with a BOM
int b2 = b4[2] & 0xFF;
if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) {
return new BomResult("UTF-8", 3);
}
// default to UTF-8 if we don't have enough bytes to make a
// good determination of the encoding
if (count < 4) {
return new BomResult("UTF-8", 0);
}
// Other encodings. No BOM. Try and ID encoding.
int b3 = b4[3] & 0xFF;
if (b0 == 0x00 && b1 == 0x00 && b2 == 0x00 && b3 == 0x3C) {
// UCS-4, big endian (1234)
return new BomResult("ISO-10646-UCS-4", 0);
}
if (b0 == 0x3C && b1 == 0x00 && b2 == 0x00 && b3 == 0x00) {
// UCS-4, little endian (4321)
return new BomResult("ISO-10646-UCS-4", 0);
}
if (b0 == 0x00 && b1 == 0x00 && b2 == 0x3C && b3 == 0x00) {
// UCS-4, unusual octet order (2143)
// REVISIT: What should this be?
return new BomResult("ISO-10646-UCS-4", 0);
}
if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x00) {
// UCS-4, unusual octet order (3412)
// REVISIT: What should this be?
return new BomResult("ISO-10646-UCS-4", 0);
}
if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) {
// UTF-16, big-endian, no BOM
// (or could turn out to be UCS-2...
// REVISIT: What should this be?
return new BomResult("UTF-16BE", 0);
}
if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) {
// UTF-16, little-endian, no BOM
// (or could turn out to be UCS-2...
return new BomResult("UTF-16LE", 0);
}
if (b0 == 0x4C && b1 == 0x6F && b2 == 0xA7 && b3 == 0x94) {
// EBCDIC
// a la xerces1, return CP037 instead of EBCDIC here
return new BomResult("CP037", 0);
}
// default encoding
return new BomResult("UTF-8", 0);
}
private static class BomResult {
public final String encoding;
public final int skip;
public BomResult(String encoding, int skip) {
this.encoding = encoding;
this.skip = skip;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,77 @@
/*
* 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.compiler;
import org.apache.jasper.JasperException;
/**
* Interface for handling JSP parse and javac compilation errors.
*
* An implementation of this interface may be registered with the
* ErrorDispatcher by setting the XXX initialization parameter in the JSP
* page compiler and execution servlet in Catalina's web.xml file to the
* implementation's fully qualified class name.
*
* @author Jan Luehe
* @author Kin-man Chung
*/
public interface ErrorHandler {
/**
* Processes the given JSP parse error.
*
* @param fname Name of the JSP file in which the parse error occurred
* @param line Parse error line number
* @param column Parse error column number
* @param msg Parse error message
* @param exception Parse exception
* @throws JasperException An error occurred
*/
public void jspError(String fname, int line, int column, String msg,
Exception exception) throws JasperException;
/**
* Processes the given JSP parse error.
*
* @param msg Parse error message
* @param exception Parse exception
* @throws JasperException An error occurred
*/
public void jspError(String msg, Exception exception)
throws JasperException;
/**
* Processes the given javac compilation errors.
*
* @param details Array of JavacErrorDetail instances corresponding to the
* compilation errors
* @throws JasperException An error occurred
*/
public void javacError(JavacErrorDetail[] details)
throws JasperException;
/**
* Processes the given javac error report and exception.
*
* @param errorReport Compilation error report
* @param exception Compilation exception
* @throws JasperException An error occurred
*/
public void javacError(String errorReport, Exception exception)
throws JasperException;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,206 @@
/*
* 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.compiler;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Set;
import java.util.Vector;
import javax.servlet.ServletContext;
import javax.servlet.jsp.tagext.FunctionInfo;
import javax.servlet.jsp.tagext.TagFileInfo;
import javax.servlet.jsp.tagext.TagInfo;
import javax.servlet.jsp.tagext.TagLibraryInfo;
import org.apache.jasper.Constants;
import org.apache.jasper.JasperException;
import org.apache.jasper.JspCompilationContext;
import org.apache.tomcat.util.descriptor.tld.ImplicitTldRuleSet;
import org.apache.tomcat.util.descriptor.tld.TaglibXml;
import org.apache.tomcat.util.descriptor.tld.TldParser;
import org.apache.tomcat.util.descriptor.tld.TldResourcePath;
import org.xml.sax.SAXException;
/**
* Class responsible for generating an implicit tag library containing tag
* handlers corresponding to the tag files in "/WEB-INF/tags/" or a
* subdirectory of it.
*
* @author Jan Luehe
*/
class ImplicitTagLibraryInfo extends TagLibraryInfo {
private static final String WEB_INF_TAGS = "/WEB-INF/tags";
private static final String TAG_FILE_SUFFIX = ".tag";
private static final String TAGX_FILE_SUFFIX = ".tagx";
private static final String TAGS_SHORTNAME = "tags";
private static final String TLIB_VERSION = "1.0";
private static final String JSP_VERSION = "2.0";
private static final String IMPLICIT_TLD = "implicit.tld";
// Maps tag names to tag file paths
private final Hashtable<String,String> tagFileMap;
private final ParserController pc;
private final PageInfo pi;
private final Vector<TagFileInfo> vec;
public ImplicitTagLibraryInfo(JspCompilationContext ctxt,
ParserController pc,
PageInfo pi,
String prefix,
String tagdir,
ErrorDispatcher err) throws JasperException {
super(prefix, null);
this.pc = pc;
this.pi = pi;
this.tagFileMap = new Hashtable<>();
this.vec = new Vector<>();
// Implicit tag libraries have no functions:
this.functions = new FunctionInfo[0];
tlibversion = TLIB_VERSION;
jspversion = JSP_VERSION;
if (!tagdir.startsWith(WEB_INF_TAGS)) {
err.jspError("jsp.error.invalid.tagdir", tagdir);
}
// Determine the value of the <short-name> subelement of the
// "imaginary" <taglib> element
if (tagdir.equals(WEB_INF_TAGS)
|| tagdir.equals( WEB_INF_TAGS + "/")) {
shortname = TAGS_SHORTNAME;
} else {
shortname = tagdir.substring(WEB_INF_TAGS.length());
shortname = shortname.replace('/', '-');
}
// Populate mapping of tag names to tag file paths
Set<String> dirList = ctxt.getResourcePaths(tagdir);
if (dirList != null) {
for (String path : dirList) {
if (path.endsWith(TAG_FILE_SUFFIX)
|| path.endsWith(TAGX_FILE_SUFFIX)) {
/*
* Use the filename of the tag file, without the .tag or
* .tagx extension, respectively, as the <name> subelement
* of the "imaginary" <tag-file> element
*/
String suffix = path.endsWith(TAG_FILE_SUFFIX) ?
TAG_FILE_SUFFIX : TAGX_FILE_SUFFIX;
String tagName = path.substring(path.lastIndexOf('/') + 1);
tagName = tagName.substring(0,
tagName.lastIndexOf(suffix));
tagFileMap.put(tagName, path);
} else if (path.endsWith(IMPLICIT_TLD)) {
TaglibXml taglibXml;
try {
URL url = ctxt.getResource(path);
TldResourcePath resourcePath = new TldResourcePath(url, path);
ServletContext servletContext = ctxt.getServletContext();
boolean validate = Boolean.parseBoolean(
servletContext.getInitParameter(
Constants.XML_VALIDATION_TLD_INIT_PARAM));
String blockExternalString = servletContext.getInitParameter(
Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
boolean blockExternal;
if (blockExternalString == null) {
blockExternal = true;
} else {
blockExternal = Boolean.parseBoolean(blockExternalString);
}
TldParser parser = new TldParser(true, validate,
new ImplicitTldRuleSet(), blockExternal);
taglibXml = parser.parse(resourcePath);
} catch (IOException | SAXException e) {
err.jspError(e);
// unreached
throw new JasperException(e);
}
this.tlibversion = taglibXml.getTlibVersion();
this.jspversion = taglibXml.getJspVersion();
try {
double version = Double.parseDouble(this.jspversion);
if (version < 2.0) {
err.jspError("jsp.error.invalid.implicit.version", path);
}
} catch (NumberFormatException e) {
err.jspError("jsp.error.invalid.implicit.version", path);
}
// Add implicit TLD to dependency list
if (pi != null) {
pi.addDependant(path, ctxt.getLastModified(path));
}
}
}
}
}
/**
* Checks to see if the given tag name maps to a tag file path,
* and if so, parses the corresponding tag file.
*
* @return The TagFileInfo corresponding to the given tag name, or null if
* the given tag name is not implemented as a tag file
*/
@Override
public TagFileInfo getTagFile(String shortName) {
TagFileInfo tagFile = super.getTagFile(shortName);
if (tagFile == null) {
String path = tagFileMap.get(shortName);
if (path == null) {
return null;
}
TagInfo tagInfo = null;
try {
tagInfo = TagFileProcessor.parseTagFileDirectives(pc,
shortName,
path,
null,
this);
} catch (JasperException je) {
throw new RuntimeException(je.toString(), je);
}
tagFile = new TagFileInfo(shortName, path, tagInfo);
vec.addElement(tagFile);
this.tagFiles = new TagFileInfo[vec.size()];
vec.copyInto(this.tagFiles);
}
return tagFile;
}
@Override
public TagLibraryInfo[] getTagLibraryInfos() {
Collection<TagLibraryInfo> coll = pi.getTaglibs();
return coll.toArray(new TagLibraryInfo[0]);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,50 @@
/*
* 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.compiler;
import javax.servlet.ServletContext;
import org.apache.tomcat.JarScanner;
import org.apache.tomcat.util.scan.StandardJarScanner;
/**
* Provide a mechanism for Jasper to obtain a reference to the JarScanner
* implementation.
*/
public class JarScannerFactory {
private JarScannerFactory() {
// Don't want any instances so hide the default constructor.
}
/**
* Obtain the {@link JarScanner} associated with the specified {@link
* ServletContext}. It is obtained via a context parameter.
* @param ctxt The Servlet context
* @return a scanner instance
*/
public static JarScanner getJarScanner(ServletContext ctxt) {
JarScanner jarScanner =
(JarScanner) ctxt.getAttribute(JarScanner.class.getName());
if (jarScanner == null) {
ctxt.log(Localizer.getMessage("jsp.warning.noJarScanner"));
jarScanner = new StandardJarScanner();
}
return jarScanner;
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.compiler;
import javax.servlet.jsp.tagext.TagAttributeInfo;
import javax.servlet.jsp.tagext.TagExtraInfo;
import javax.servlet.jsp.tagext.TagInfo;
import javax.servlet.jsp.tagext.TagLibraryInfo;
import javax.servlet.jsp.tagext.TagVariableInfo;
/**
* TagInfo extension used by tag handlers that are implemented via tag files.
* This class provides access to the name of the Map used to store the
* dynamic attribute names and values passed to the custom action invocation.
* This information is used by the code generator.
*/
class JasperTagInfo extends TagInfo {
private final String dynamicAttrsMapName;
public JasperTagInfo(String tagName,
String tagClassName,
String bodyContent,
String infoString,
TagLibraryInfo taglib,
TagExtraInfo tagExtraInfo,
TagAttributeInfo[] attributeInfo,
String displayName,
String smallIcon,
String largeIcon,
TagVariableInfo[] tvi,
String mapName) {
super(tagName, tagClassName, bodyContent, infoString, taglib,
tagExtraInfo, attributeInfo, displayName, smallIcon, largeIcon,
tvi);
this.dynamicAttrsMapName = mapName;
}
public String getDynamicAttributesMapName() {
return dynamicAttrsMapName;
}
@Override
public boolean hasDynamicAttributes() {
return dynamicAttrsMapName != null;
}
}

View File

@@ -0,0 +1,231 @@
/*
* 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.compiler;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.apache.jasper.JspCompilationContext;
import org.apache.tomcat.Jar;
/**
* Class providing details about a javac compilation error.
*
* @author Jan Luehe
* @author Kin-man Chung
*/
public class JavacErrorDetail {
private final String javaFileName;
private final int javaLineNum;
private String jspFileName;
private int jspBeginLineNum;
private final StringBuilder errMsg;
private String jspExtract = null;
/**
* Constructor.
*
* @param javaFileName The name of the Java file in which the
* compilation error occurred
* @param javaLineNum The compilation error line number
* @param errMsg The compilation error message
*/
public JavacErrorDetail(String javaFileName,
int javaLineNum,
StringBuilder errMsg) {
this(javaFileName, javaLineNum, null, -1, errMsg, null);
}
/**
* Constructor.
*
* @param javaFileName The name of the Java file in which the
* compilation error occurred
* @param javaLineNum The compilation error line number
* @param jspFileName The name of the JSP file from which the Java source
* file was generated
* @param jspBeginLineNum The start line number of the JSP element
* responsible for the compilation error
* @param errMsg The compilation error message
* @param ctxt The compilation context
*/
public JavacErrorDetail(String javaFileName,
int javaLineNum,
String jspFileName,
int jspBeginLineNum,
StringBuilder errMsg,
JspCompilationContext ctxt) {
this.javaFileName = javaFileName;
this.javaLineNum = javaLineNum;
this.errMsg = errMsg;
this.jspFileName = jspFileName;
// Note: this.jspBeginLineNum is set at the end of this method as it may
// be modified (corrected) during the execution of this method
if (jspBeginLineNum > 0 && ctxt != null) {
InputStream is = null;
try {
Jar tagJar = ctxt.getTagFileJar();
if (tagJar != null) {
// Strip leading '/'
String entryName = jspFileName.substring(1);
is = tagJar.getInputStream(entryName);
this.jspFileName = tagJar.getURL(entryName);
} else {
is = ctxt.getResourceAsStream(jspFileName);
}
// Read both files in, so we can inspect them
String[] jspLines = readFile(is);
try (FileInputStream fis = new FileInputStream(ctxt.getServletJavaFileName())) {
String[] javaLines = readFile(fis);
if (jspLines.length < jspBeginLineNum) {
// Avoid ArrayIndexOutOfBoundsException
// Probably bug 48498 but could be some other cause
jspExtract = Localizer.getMessage("jsp.error.bug48498");
return;
}
// If the line contains the opening of a multi-line scriptlet
// block, then the JSP line number we got back is probably
// faulty. Scan forward to match the java line...
if (jspLines[jspBeginLineNum-1].lastIndexOf("<%") >
jspLines[jspBeginLineNum-1].lastIndexOf("%>")) {
String javaLine = javaLines[javaLineNum-1].trim();
for (int i=jspBeginLineNum-1; i<jspLines.length; i++) {
if (jspLines[i].indexOf(javaLine) != -1) {
// Update jsp line number
jspBeginLineNum = i+1;
break;
}
}
}
// copy out a fragment of JSP to display to the user
StringBuilder fragment = new StringBuilder(1024);
int startIndex = Math.max(0, jspBeginLineNum-1-3);
int endIndex = Math.min(
jspLines.length-1, jspBeginLineNum-1+3);
for (int i=startIndex;i<=endIndex; ++i) {
fragment.append(i+1);
fragment.append(": ");
fragment.append(jspLines[i]);
fragment.append(System.lineSeparator());
}
jspExtract = fragment.toString();
}
} catch (IOException ioe) {
// Can't read files - ignore
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ignore) {
// Ignore
}
}
}
}
this.jspBeginLineNum = jspBeginLineNum;
}
/**
* Gets the name of the Java source file in which the compilation error
* occurred.
*
* @return Java source file name
*/
public String getJavaFileName() {
return this.javaFileName;
}
/**
* Gets the compilation error line number.
*
* @return Compilation error line number
*/
public int getJavaLineNumber() {
return this.javaLineNum;
}
/**
* Gets the name of the JSP file from which the Java source file was
* generated.
*
* @return JSP file from which the Java source file was generated.
*/
public String getJspFileName() {
return this.jspFileName;
}
/**
* Gets the start line number (in the JSP file) of the JSP element
* responsible for the compilation error.
*
* @return Start line number of the JSP element responsible for the
* compilation error
*/
public int getJspBeginLineNumber() {
return this.jspBeginLineNum;
}
/**
* Gets the compilation error message.
*
* @return Compilation error message
*/
public String getErrorMessage() {
return this.errMsg.toString();
}
/**
* Gets the extract of the JSP that corresponds to this message.
*
* @return Extract of JSP where error occurred
*/
public String getJspExtract() {
return this.jspExtract;
}
/**
* Reads a text file from an input stream into a String[]. Used to read in
* the JSP and generated Java file when generating error messages.
*/
private String[] readFile(InputStream s) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(s));
List<String> lines = new ArrayList<>();
String line;
while ( (line = reader.readLine()) != null ) {
lines.add(line);
}
return lines.toArray( new String[lines.size()] );
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
/*
* 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.compiler;
import java.text.MessageFormat;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import org.apache.jasper.runtime.ExceptionUtils;
/**
* Class responsible for converting error codes to corresponding localized
* error messages.
*
* @author Jan Luehe
*/
public class Localizer {
private static ResourceBundle bundle;
static {
try {
bundle = ResourceBundle.getBundle("org.apache.jasper.resources.LocalStrings");
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
}
/*
* Returns the localized error message corresponding to the given error
* code.
*
* If the given error code is not defined in the resource bundle for
* localized error messages, it is used as the error message.
*
* @param errCode Error code to localize
*
* @return Localized error message
*/
public static String getMessage(String errCode) {
String errMsg = errCode;
try {
if (bundle != null) {
errMsg = bundle.getString(errCode);
}
} catch (MissingResourceException e) {
}
return errMsg;
}
/*
* Returns the localized error message corresponding to the given error
* code.
*
* If the given error code is not defined in the resource bundle for
* localized error messages, it is used as the error message.
*
* @param errCode Error code to localize
* @param args Arguments for parametric replacement
*
* @return Localized error message
*/
public static String getMessage(String errCode, Object... args) {
String errMsg = getMessage(errCode);
if (args != null && args.length > 0) {
MessageFormat formatter = new MessageFormat(errMsg);
errMsg = formatter.format(args);
}
return errMsg;
}
}

View File

@@ -0,0 +1,145 @@
/*
* 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.compiler;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.jasper.JspCompilationContext;
/**
* Mark represents a point in the JSP input.
*
* @author Anil K. Vijendran
*/
final class Mark {
// position within current stream
int cursor, line, col;
// current stream
char[] stream = null;
// name of the current file
private String fileName;
private JspCompilationContext ctxt;
/**
* Constructor
*
* @param reader JspReader this mark belongs to
* @param inStream current stream for this mark
* @param name JSP file name
*/
Mark(JspReader reader, char[] inStream, String name) {
this.ctxt = reader.getJspCompilationContext();
this.stream = inStream;
this.cursor = 0;
this.line = 1;
this.col = 1;
this.fileName = name;
}
/**
* Constructor
*/
Mark(Mark other) {
init(other, false);
}
void update(int cursor, int line, int col) {
this.cursor = cursor;
this.line = line;
this.col = col;
}
void init(Mark other, boolean singleFile) {
this.cursor = other.cursor;
this.line = other.line;
this.col = other.col;
if (!singleFile) {
this.ctxt = other.ctxt;
this.stream = other.stream;
this.fileName = other.fileName;
}
}
/**
* Constructor
*/
Mark(JspCompilationContext ctxt, String filename, int line, int col) {
this.ctxt = ctxt;
this.stream = null;
this.cursor = 0;
this.line = line;
this.col = col;
this.fileName = filename;
}
public int getLineNumber() {
return line;
}
public int getColumnNumber() {
return col;
}
@Override
public String toString() {
return getFile()+"("+line+","+col+")";
}
public String getFile() {
return this.fileName;
}
/**
* Gets the URL of the resource with which this Mark is associated
*
* @return URL of the resource with which this Mark is associated
*
* @exception MalformedURLException if the resource pathname is incorrect
*/
public URL getURL() throws MalformedURLException {
return ctxt.getResource(getFile());
}
@Override
public boolean equals(Object other) {
if (other instanceof Mark) {
Mark m = (Mark) other;
return this.cursor == m.cursor && this.line == m.line && this.col == m.col;
}
return false;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + col;
result = prime * result + cursor;
result = prime * result + line;
return result;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,157 @@
/*
* 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.compiler;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.jsp.tagext.TagVariableInfo;
import javax.servlet.jsp.tagext.VariableInfo;
import org.apache.jasper.JasperException;
/**
* Class responsible for determining the scripting variables that every
* custom action needs to declare.
*
* @author Jan Luehe
*/
class ScriptingVariabler {
private static final Integer MAX_SCOPE = Integer.valueOf(Integer.MAX_VALUE);
/*
* Assigns an identifier (of type integer) to every custom tag, in order
* to help identify, for every custom tag, the scripting variables that it
* needs to declare.
*/
private static class CustomTagCounter extends Node.Visitor {
private int count;
private Node.CustomTag parent;
@Override
public void visit(Node.CustomTag n) throws JasperException {
n.setCustomTagParent(parent);
Node.CustomTag tmpParent = parent;
parent = n;
visitBody(n);
parent = tmpParent;
n.setNumCount(Integer.valueOf(count++));
}
}
/*
* For every custom tag, determines the scripting variables it needs to
* declare.
*/
private static class ScriptingVariableVisitor extends Node.Visitor {
private final ErrorDispatcher err;
private final Map<String, Integer> scriptVars;
public ScriptingVariableVisitor(ErrorDispatcher err) {
this.err = err;
scriptVars = new HashMap<>();
}
@Override
public void visit(Node.CustomTag n) throws JasperException {
setScriptingVars(n, VariableInfo.AT_BEGIN);
setScriptingVars(n, VariableInfo.NESTED);
visitBody(n);
setScriptingVars(n, VariableInfo.AT_END);
}
private void setScriptingVars(Node.CustomTag n, int scope)
throws JasperException {
TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
VariableInfo[] varInfos = n.getVariableInfos();
if (tagVarInfos.length == 0 && varInfos.length == 0) {
return;
}
List<Object> vec = new ArrayList<>();
Integer ownRange = null;
Node.CustomTag parent = n.getCustomTagParent();
if (scope == VariableInfo.AT_BEGIN
|| scope == VariableInfo.AT_END) {
if (parent == null)
ownRange = MAX_SCOPE;
else
ownRange = parent.getNumCount();
} else {
// NESTED
ownRange = n.getNumCount();
}
if (varInfos.length > 0) {
for (int i=0; i<varInfos.length; i++) {
if (varInfos[i].getScope() != scope
|| !varInfos[i].getDeclare()) {
continue;
}
String varName = varInfos[i].getVarName();
Integer currentRange = scriptVars.get(varName);
if (currentRange == null ||
ownRange.compareTo(currentRange) > 0) {
scriptVars.put(varName, ownRange);
vec.add(varInfos[i]);
}
}
} else {
for (int i=0; i<tagVarInfos.length; i++) {
if (tagVarInfos[i].getScope() != scope
|| !tagVarInfos[i].getDeclare()) {
continue;
}
String varName = tagVarInfos[i].getNameGiven();
if (varName == null) {
varName = n.getTagData().getAttributeString(
tagVarInfos[i].getNameFromAttribute());
if (varName == null) {
err.jspError(n,
"jsp.error.scripting.variable.missing_name",
tagVarInfos[i].getNameFromAttribute());
}
}
Integer currentRange = scriptVars.get(varName);
if (currentRange == null ||
ownRange.compareTo(currentRange) > 0) {
scriptVars.put(varName, ownRange);
vec.add(tagVarInfos[i]);
}
}
}
n.setScriptingVars(vec, scope);
}
}
public static void set(Node.Nodes page, ErrorDispatcher err)
throws JasperException {
page.visit(new CustomTagCounter());
page.visit(new ScriptingVariableVisitor(err));
}
}

View File

@@ -0,0 +1,170 @@
/*
* 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.compiler;
import java.io.PrintWriter;
/**
* This is what is used to generate servlets.
*
* @author Anil K. Vijendran
* @author Kin-man Chung
*/
public class ServletWriter implements AutoCloseable {
private static final int TAB_WIDTH = 2;
private static final String SPACES = " ";
/**
* Current indent level.
*/
private int indent = 0;
private int virtual_indent = 0;
/**
* The sink writer.
*/
private final PrintWriter writer;
/**
* Servlet line numbers start from 1.
*/
private int javaLine = 1;
public ServletWriter(PrintWriter writer) {
this.writer = writer;
}
@Override
public void close() {
writer.close();
}
// -------------------- Access informations --------------------
public int getJavaLine() {
return javaLine;
}
// -------------------- Formatting --------------------
public void pushIndent() {
virtual_indent += TAB_WIDTH;
if (virtual_indent >= 0 && virtual_indent <= SPACES.length())
indent = virtual_indent;
}
public void popIndent() {
virtual_indent -= TAB_WIDTH;
if (virtual_indent >= 0 && virtual_indent <= SPACES.length())
indent = virtual_indent;
}
/**
* Prints the given string followed by '\n'
* @param s The string
*/
public void println(String s) {
javaLine++;
writer.println(s);
}
/**
* Prints a '\n'
*/
public void println() {
javaLine++;
writer.println("");
}
/**
* Prints the current indention
*/
public void printin() {
writer.print(SPACES.substring(0, indent));
}
/**
* Prints the current indention, followed by the given string
* @param s The string
*/
public void printin(String s) {
writer.print(SPACES.substring(0, indent));
writer.print(s);
}
/**
* Prints the current indention, and then the string, and a '\n'.
* @param s The string
*/
public void printil(String s) {
javaLine++;
writer.print(SPACES.substring(0, indent));
writer.println(s);
}
/**
* Prints the given char.
*
* Use println() to print a '\n'.
* @param c The char
*/
public void print(char c) {
writer.print(c);
}
/**
* Prints the given int.
* @param i The int
*/
public void print(int i) {
writer.print(i);
}
/**
* Prints the given string.
*
* The string must not contain any '\n', otherwise the line count will be
* off.
* @param s The string
*/
public void print(String s) {
writer.print(s);
}
/**
* Prints the given string.
*
* If the string spans multiple lines, the line count will be adjusted
* accordingly.
* @param s The string
*/
public void printMultiLn(String s) {
int index = 0;
// look for hidden newlines inside strings
while ((index=s.indexOf('\n',index)) > -1 ) {
javaLine++;
index++;
}
writer.print(s);
}
}

View File

@@ -0,0 +1,194 @@
/*
* 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.compiler;
import java.util.ArrayList;
import java.util.List;
/**
* Represents a source map (SMAP), which serves to associate lines
* of the input JSP file(s) to lines in the generated servlet in the
* final .class file, according to the JSR-045 spec.
*
* @author Shawn Bayern
*/
public class SmapGenerator {
//*********************************************************************
// Overview
/*
* The SMAP syntax is reasonably straightforward. The purpose of this
* class is currently twofold:
* - to provide a simple but low-level Java interface to build
* a logical SMAP
* - to serialize this logical SMAP for eventual inclusion directly
* into a .class file.
*/
//*********************************************************************
// Private state
private String outputFileName;
private String defaultStratum = "Java";
private final List<SmapStratum> strata = new ArrayList<>();
private final List<String> embedded = new ArrayList<>();
private boolean doEmbedded = true;
//*********************************************************************
// Methods for adding mapping data
/**
* Sets the filename (without path information) for the generated
* source file. E.g., "foo$jsp.java".
* @param x The file name
*/
public synchronized void setOutputFileName(String x) {
outputFileName = x;
}
/**
* Sets the default and only stratum for the smap.
*
* @param stratum the SmapStratum object to add
*/
public synchronized void setStratum(SmapStratum stratum) {
addStratum(stratum, true);
}
/**
* Adds the given SmapStratum object, representing a Stratum with
* logically associated FileSection and LineSection blocks, to
* the current SmapGenerator. If <code>defaultStartum</code> is true, this
* stratum is made the default stratum, overriding any previously
* set default.
*
* @param stratum the SmapStratum object to add
* @param defaultStratum if <code>true</code>, this SmapStratum is
* considered to represent the default SMAP stratum unless
* overwritten
*
* @deprecated Use {@link #setStratum(SmapStratum)}
*/
@Deprecated
public synchronized void addStratum(SmapStratum stratum,
boolean defaultStratum) {
strata.add(stratum);
if (defaultStratum)
this.defaultStratum = stratum.getStratumName();
}
/**
* Adds the given string as an embedded SMAP with the given stratum name.
*
* @param smap the SMAP to embed
* @param stratumName the name of the stratum output by the compilation
* that produced the <code>smap</code> to be embedded
*
* @deprecated Unused. This will be removed in Tomcat 9.0.x
*/
@Deprecated
public synchronized void addSmap(String smap, String stratumName) {
embedded.add("*O " + stratumName + "\n"
+ smap
+ "*C " + stratumName + "\n");
}
/**
* Instructs the SmapGenerator whether to actually print any embedded
* SMAPs or not. Intended for situations without an SMAP resolver.
*
* @param status If <code>false</code>, ignore any embedded SMAPs.
*
* @deprecated Unused. Will be removed in Tomcat 9.0.x
*/
@Deprecated
public void setDoEmbedded(boolean status) {
doEmbedded = status;
}
//*********************************************************************
// Methods for serializing the logical SMAP
public synchronized String getString() {
// check state and initialize buffer
if (outputFileName == null)
throw new IllegalStateException();
StringBuilder out = new StringBuilder();
// start the SMAP
out.append("SMAP\n");
out.append(outputFileName + '\n');
out.append(defaultStratum + '\n');
// include embedded SMAPs
if (doEmbedded) {
int nEmbedded = embedded.size();
for (int i = 0; i < nEmbedded; i++) {
out.append(embedded.get(i));
}
}
// print our StratumSections, FileSections, and LineSections
int nStrata = strata.size();
for (int i = 0; i < nStrata; i++) {
SmapStratum s = strata.get(i);
out.append(s.getString());
}
// end the SMAP
out.append("*E\n");
return out.toString();
}
@Override
public String toString() { return getString(); }
//*********************************************************************
// For testing (and as an example of use)...
@SuppressWarnings("deprecation")
public static void main(String args[]) {
SmapGenerator g = new SmapGenerator();
g.setOutputFileName("foo.java");
SmapStratum s = new SmapStratum();
s.addFile("foo.jsp");
s.addFile("bar.jsp", "/foo/foo/bar.jsp");
s.addLineData(1, "foo.jsp", 1, 1, 1);
s.addLineData(2, "foo.jsp", 1, 6, 1);
s.addLineData(3, "foo.jsp", 2, 10, 5);
s.addLineData(20, "bar.jsp", 1, 30, 1);
g.addStratum(s, true);
System.out.print(g);
System.out.println("---");
SmapGenerator embedded = new SmapGenerator();
embedded.setOutputFileName("blargh.tier2");
s = new SmapStratum("Tier2");
s.addFile("1.tier2");
s.addLineData(1, "1.tier2", 1, 1, 1);
embedded.addStratum(s, true);
g.addSmap(embedded.toString(), "JSP");
System.out.println(g);
}
}

View File

@@ -0,0 +1,349 @@
/*
* 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.compiler;
import java.util.ArrayList;
import java.util.List;
/**
* Represents the line and file mappings associated with a JSR-045
* "stratum".
*
* @author Jayson Falkner
* @author Shawn Bayern
*/
public class SmapStratum {
//*********************************************************************
// Class for storing LineInfo data
/**
* Represents a single LineSection in an SMAP, associated with
* a particular stratum.
*/
private static class LineInfo {
private int inputStartLine = -1;
private int outputStartLine = -1;
private int lineFileID = 0;
private int inputLineCount = 1;
private int outputLineIncrement = 1;
private boolean lineFileIDSet = false;
public void setInputStartLine(int inputStartLine) {
if (inputStartLine < 0)
throw new IllegalArgumentException("" + inputStartLine);
this.inputStartLine = inputStartLine;
}
public void setOutputStartLine(int outputStartLine) {
if (outputStartLine < 0)
throw new IllegalArgumentException("" + outputStartLine);
this.outputStartLine = outputStartLine;
}
/**
* Sets lineFileID. Should be called only when different from
* that of prior LineInfo object (in any given context) or 0
* if the current LineInfo has no (logical) predecessor.
* <code>LineInfo</code> will print this file number no matter what.
*
* @param lineFileID The new line file ID
*/
public void setLineFileID(int lineFileID) {
if (lineFileID < 0)
throw new IllegalArgumentException("" + lineFileID);
this.lineFileID = lineFileID;
this.lineFileIDSet = true;
}
public void setInputLineCount(int inputLineCount) {
if (inputLineCount < 0)
throw new IllegalArgumentException("" + inputLineCount);
this.inputLineCount = inputLineCount;
}
public void setOutputLineIncrement(int outputLineIncrement) {
if (outputLineIncrement < 0)
throw new IllegalArgumentException("" + outputLineIncrement);
this.outputLineIncrement = outputLineIncrement;
}
/**
* @return the current LineInfo as a String, print all values only when
* appropriate (but LineInfoID if and only if it's been
* specified, as its necessity is sensitive to context).
*/
public String getString() {
if (inputStartLine == -1 || outputStartLine == -1)
throw new IllegalStateException();
StringBuilder out = new StringBuilder();
out.append(inputStartLine);
if (lineFileIDSet)
out.append("#" + lineFileID);
if (inputLineCount != 1)
out.append("," + inputLineCount);
out.append(":" + outputStartLine);
if (outputLineIncrement != 1)
out.append("," + outputLineIncrement);
out.append('\n');
return out.toString();
}
@Override
public String toString() {
return getString();
}
}
//*********************************************************************
// Private state
private final String stratumName;
private final List<String> fileNameList;
private final List<String> filePathList;
private final List<LineInfo> lineData;
private int lastFileID;
//*********************************************************************
// Constructor
/**
* Constructs a new SmapStratum object with the stratum name JSP.
*/
public SmapStratum() {
this("JSP");
}
/**
* Constructs a new SmapStratum object for the given stratum name
* (e.g., JSP).
*
* @param stratumName the name of the stratum (e.g., JSP)
*
* @deprecated Use the no-arg constructor
*/
@Deprecated
public SmapStratum(String stratumName) {
this.stratumName = stratumName;
fileNameList = new ArrayList<>();
filePathList = new ArrayList<>();
lineData = new ArrayList<>();
lastFileID = 0;
}
//*********************************************************************
// Methods to add mapping information
/**
* Adds record of a new file, by filename.
*
* @param filename the filename to add, unqualified by path.
*/
public void addFile(String filename) {
addFile(filename, filename);
}
/**
* Adds record of a new file, by filename and path. The path
* may be relative to a source compilation path.
*
* @param filename the filename to add, unqualified by path
* @param filePath the path for the filename, potentially relative
* to a source compilation path
*/
public void addFile(String filename, String filePath) {
int pathIndex = filePathList.indexOf(filePath);
if (pathIndex == -1) {
fileNameList.add(filename);
filePathList.add(filePath);
}
}
/**
* Combines consecutive LineInfos wherever possible
*/
public void optimizeLineSection() {
/* Some debugging code
for (int i = 0; i < lineData.size(); i++) {
LineInfo li = (LineInfo)lineData.get(i);
System.out.print(li.toString());
}
*/
//Incorporate each LineInfo into the previous LineInfo's
//outputLineIncrement, if possible
int i = 0;
while (i < lineData.size() - 1) {
LineInfo li = lineData.get(i);
LineInfo liNext = lineData.get(i + 1);
if (!liNext.lineFileIDSet
&& liNext.inputStartLine == li.inputStartLine
&& liNext.inputLineCount == 1
&& li.inputLineCount == 1
&& liNext.outputStartLine
== li.outputStartLine
+ li.inputLineCount * li.outputLineIncrement) {
li.setOutputLineIncrement(
liNext.outputStartLine
- li.outputStartLine
+ liNext.outputLineIncrement);
lineData.remove(i + 1);
} else {
i++;
}
}
//Incorporate each LineInfo into the previous LineInfo's
//inputLineCount, if possible
i = 0;
while (i < lineData.size() - 1) {
LineInfo li = lineData.get(i);
LineInfo liNext = lineData.get(i + 1);
if (!liNext.lineFileIDSet
&& liNext.inputStartLine == li.inputStartLine + li.inputLineCount
&& liNext.outputLineIncrement == li.outputLineIncrement
&& liNext.outputStartLine
== li.outputStartLine
+ li.inputLineCount * li.outputLineIncrement) {
li.setInputLineCount(li.inputLineCount + liNext.inputLineCount);
lineData.remove(i + 1);
} else {
i++;
}
}
}
/**
* Adds complete information about a simple line mapping. Specify
* all the fields in this method; the back-end machinery takes care
* of printing only those that are necessary in the final SMAP.
* (My view is that fields are optional primarily for spatial efficiency,
* not for programmer convenience. Could always add utility methods
* later.)
*
* @param inputStartLine starting line in the source file
* (SMAP <code>InputStartLine</code>)
* @param inputFileName the filepath (or name) from which the input comes
* (yields SMAP <code>LineFileID</code>) Use unqualified names
* carefully, and only when they uniquely identify a file.
* @param inputLineCount the number of lines in the input to map
* (SMAP <code>LineFileCount</code>)
* @param outputStartLine starting line in the output file
* (SMAP <code>OutputStartLine</code>)
* @param outputLineIncrement number of output lines to map to each
* input line (SMAP <code>OutputLineIncrement</code>). <i>Given the
* fact that the name starts with "output", I continuously have
* the subconscious urge to call this field
* <code>OutputLineExcrement</code>.</i>
*/
public void addLineData(
int inputStartLine,
String inputFileName,
int inputLineCount,
int outputStartLine,
int outputLineIncrement) {
// check the input - what are you doing here??
int fileIndex = filePathList.indexOf(inputFileName);
if (fileIndex == -1) // still
throw new IllegalArgumentException(
"inputFileName: " + inputFileName);
//Jasper incorrectly SMAPs certain Nodes, giving them an
//outputStartLine of 0. This can cause a fatal error in
//optimizeLineSection, making it impossible for Jasper to
//compile the JSP. Until we can fix the underlying
//SMAPping problem, we simply ignore the flawed SMAP entries.
if (outputStartLine == 0)
return;
// build the LineInfo
LineInfo li = new LineInfo();
li.setInputStartLine(inputStartLine);
li.setInputLineCount(inputLineCount);
li.setOutputStartLine(outputStartLine);
li.setOutputLineIncrement(outputLineIncrement);
if (fileIndex != lastFileID)
li.setLineFileID(fileIndex);
lastFileID = fileIndex;
// save it
lineData.add(li);
}
//*********************************************************************
// Methods to retrieve information
/**
* @return the name of the stratum.
*
* @deprecated Unused. This will be removed in Tomcat 9.0.x
*/
@Deprecated
public String getStratumName() {
return stratumName;
}
/**
* @return the given stratum as a String: a StratumSection,
* followed by at least one FileSection and at least one LineSection.
*/
public String getString() {
// check state and initialize buffer
if (fileNameList.size() == 0 || lineData.size() == 0)
return null;
StringBuilder out = new StringBuilder();
// print StratumSection
out.append("*S " + stratumName + "\n");
// print FileSection
out.append("*F\n");
int bound = fileNameList.size();
for (int i = 0; i < bound; i++) {
if (filePathList.get(i) != null) {
out.append("+ " + i + " " + fileNameList.get(i) + "\n");
// Source paths must be relative, not absolute, so we
// remove the leading "/", if one exists.
String filePath = filePathList.get(i);
if (filePath.startsWith("/")) {
filePath = filePath.substring(1);
}
out.append(filePath + "\n");
} else {
out.append(i + " " + fileNameList.get(i) + "\n");
}
}
// print LineSection
out.append("*L\n");
bound = lineData.size();
for (int i = 0; i < bound; i++) {
LineInfo li = lineData.get(i);
out.append(li.getString());
}
return out.toString();
}
@Override
public String toString() {
return getString();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,116 @@
/*
* 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.compiler;
public interface TagConstants {
public static final String JSP_URI = "http://java.sun.com/JSP/Page";
public static final String DIRECTIVE_ACTION = "directive.";
public static final String ROOT_ACTION = "root";
public static final String JSP_ROOT_ACTION = "jsp:root";
public static final String PAGE_DIRECTIVE_ACTION = "directive.page";
public static final String JSP_PAGE_DIRECTIVE_ACTION = "jsp:directive.page";
public static final String INCLUDE_DIRECTIVE_ACTION = "directive.include";
public static final String JSP_INCLUDE_DIRECTIVE_ACTION = "jsp:directive.include";
public static final String DECLARATION_ACTION = "declaration";
public static final String JSP_DECLARATION_ACTION = "jsp:declaration";
public static final String SCRIPTLET_ACTION = "scriptlet";
public static final String JSP_SCRIPTLET_ACTION = "jsp:scriptlet";
public static final String EXPRESSION_ACTION = "expression";
public static final String JSP_EXPRESSION_ACTION = "jsp:expression";
public static final String USE_BEAN_ACTION = "useBean";
public static final String JSP_USE_BEAN_ACTION = "jsp:useBean";
public static final String SET_PROPERTY_ACTION = "setProperty";
public static final String JSP_SET_PROPERTY_ACTION = "jsp:setProperty";
public static final String GET_PROPERTY_ACTION = "getProperty";
public static final String JSP_GET_PROPERTY_ACTION = "jsp:getProperty";
public static final String INCLUDE_ACTION = "include";
public static final String JSP_INCLUDE_ACTION = "jsp:include";
public static final String FORWARD_ACTION = "forward";
public static final String JSP_FORWARD_ACTION = "jsp:forward";
public static final String PARAM_ACTION = "param";
public static final String JSP_PARAM_ACTION = "jsp:param";
public static final String PARAMS_ACTION = "params";
public static final String JSP_PARAMS_ACTION = "jsp:params";
public static final String PLUGIN_ACTION = "plugin";
public static final String JSP_PLUGIN_ACTION = "jsp:plugin";
public static final String FALLBACK_ACTION = "fallback";
public static final String JSP_FALLBACK_ACTION = "jsp:fallback";
public static final String TEXT_ACTION = "text";
public static final String JSP_TEXT_ACTION = "jsp:text";
public static final String JSP_TEXT_ACTION_END = "</jsp:text>";
public static final String ATTRIBUTE_ACTION = "attribute";
public static final String JSP_ATTRIBUTE_ACTION = "jsp:attribute";
public static final String BODY_ACTION = "body";
public static final String JSP_BODY_ACTION = "jsp:body";
public static final String ELEMENT_ACTION = "element";
public static final String JSP_ELEMENT_ACTION = "jsp:element";
public static final String OUTPUT_ACTION = "output";
public static final String JSP_OUTPUT_ACTION = "jsp:output";
public static final String TAGLIB_DIRECTIVE_ACTION = "taglib";
public static final String JSP_TAGLIB_DIRECTIVE_ACTION = "jsp:taglib";
/*
* Tag Files
*/
public static final String INVOKE_ACTION = "invoke";
public static final String JSP_INVOKE_ACTION = "jsp:invoke";
public static final String DOBODY_ACTION = "doBody";
public static final String JSP_DOBODY_ACTION = "jsp:doBody";
/*
* Tag File Directives
*/
public static final String TAG_DIRECTIVE_ACTION = "directive.tag";
public static final String JSP_TAG_DIRECTIVE_ACTION = "jsp:directive.tag";
public static final String ATTRIBUTE_DIRECTIVE_ACTION = "directive.attribute";
public static final String JSP_ATTRIBUTE_DIRECTIVE_ACTION = "jsp:directive.attribute";
public static final String VARIABLE_DIRECTIVE_ACTION = "directive.variable";
public static final String JSP_VARIABLE_DIRECTIVE_ACTION = "jsp:directive.variable";
/*
* Directive attributes
*/
public static final String URN_JSPTAGDIR = "urn:jsptagdir:";
public static final String URN_JSPTLD = "urn:jsptld:";
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,393 @@
/*
* 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.compiler;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.jsp.tagext.FunctionInfo;
import javax.servlet.jsp.tagext.PageData;
import javax.servlet.jsp.tagext.TagAttributeInfo;
import javax.servlet.jsp.tagext.TagExtraInfo;
import javax.servlet.jsp.tagext.TagFileInfo;
import javax.servlet.jsp.tagext.TagInfo;
import javax.servlet.jsp.tagext.TagLibraryInfo;
import javax.servlet.jsp.tagext.TagLibraryValidator;
import javax.servlet.jsp.tagext.TagVariableInfo;
import javax.servlet.jsp.tagext.ValidationMessage;
import org.apache.jasper.JasperException;
import org.apache.jasper.JspCompilationContext;
import org.apache.tomcat.Jar;
import org.apache.tomcat.util.descriptor.tld.TagFileXml;
import org.apache.tomcat.util.descriptor.tld.TagXml;
import org.apache.tomcat.util.descriptor.tld.TaglibXml;
import org.apache.tomcat.util.descriptor.tld.TldResourcePath;
import org.apache.tomcat.util.descriptor.tld.ValidatorXml;
/**
* Implementation of the TagLibraryInfo class from the JSP spec.
*
* @author Anil K. Vijendran
* @author Mandar Raje
* @author Pierre Delisle
* @author Kin-man Chung
* @author Jan Luehe
*/
class TagLibraryInfoImpl extends TagLibraryInfo implements TagConstants {
private final JspCompilationContext ctxt;
private final PageInfo pi;
private final ErrorDispatcher err;
private final ParserController parserController;
private static void print(String name, String value, PrintWriter w) {
if (value != null) {
w.print(name + " = {\n\t");
w.print(value);
w.print("\n}\n");
}
}
@Override
public String toString() {
StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw);
print("tlibversion", tlibversion, out);
print("jspversion", jspversion, out);
print("shortname", shortname, out);
print("urn", urn, out);
print("info", info, out);
print("uri", uri, out);
print("tagLibraryValidator", "" + tagLibraryValidator, out);
for (TagInfo tag : tags) {
out.println(tag.toString());
}
for (TagFileInfo tagFile : tagFiles) {
out.println(tagFile.toString());
}
for (FunctionInfo function : functions) {
out.println(function.toString());
}
return sw.toString();
}
public TagLibraryInfoImpl(JspCompilationContext ctxt, ParserController pc,
PageInfo pi, String prefix, String uriIn,
TldResourcePath tldResourcePath, ErrorDispatcher err)
throws JasperException {
super(prefix, uriIn);
this.ctxt = ctxt;
this.parserController = pc;
this.pi = pi;
this.err = err;
if (tldResourcePath == null) {
// The URI points to the TLD itself or to a JAR file in which the TLD is stored
tldResourcePath = generateTldResourcePath(uri, ctxt);
}
try (Jar jar = tldResourcePath.openJar()) {
// Add the dependencies on the TLD to the referencing page
PageInfo pageInfo = ctxt.createCompiler().getPageInfo();
if (pageInfo != null) {
// If the TLD is in a JAR, that JAR may not be part of the web
// application
String path = tldResourcePath.getWebappPath();
if (path != null) {
// Add TLD (jar==null) / JAR (jar!=null) file to dependency list
// 2nd parameter is null since the path is always relative
// to the root of the web application even if we are
// processing a reference from a tag packaged in a JAR.
pageInfo.addDependant(path, ctxt.getLastModified(path, null));
}
if (jar != null) {
if (path == null) {
// JAR not in the web application so add it directly
URL jarUrl = jar.getJarFileURL();
long lastMod = -1;
URLConnection urlConn = null;
try {
urlConn = jarUrl.openConnection();
lastMod = urlConn.getLastModified();
} catch (IOException ioe) {
throw new JasperException(ioe);
} finally {
if (urlConn != null) {
try {
urlConn.getInputStream().close();
} catch (IOException e) {
// Ignore
}
}
}
pageInfo.addDependant(jarUrl.toExternalForm(),
Long.valueOf(lastMod));
}
// Add TLD within the JAR to the dependency list
String entryName = tldResourcePath.getEntryName();
try {
pageInfo.addDependant(jar.getURL(entryName),
Long.valueOf(jar.getLastModified(entryName)));
} catch (IOException ioe) {
throw new JasperException(ioe);
}
}
}
// Get the representation of the TLD
if (tldResourcePath.getUrl() == null) {
err.jspError("jsp.error.tld.missing", prefix, uri);
}
TaglibXml taglibXml =
ctxt.getOptions().getTldCache().getTaglibXml(tldResourcePath);
if (taglibXml == null) {
err.jspError("jsp.error.tld.missing", prefix, uri);
}
// Populate the TagLibraryInfo attributes
// Never null. jspError always throws an Exception
// Slightly convoluted so the @SuppressWarnings has minimal scope
@SuppressWarnings("null")
String v = taglibXml.getJspVersion();
this.jspversion = v;
this.tlibversion = taglibXml.getTlibVersion();
this.shortname = taglibXml.getShortName();
this.urn = taglibXml.getUri();
this.info = taglibXml.getInfo();
this.tagLibraryValidator = createValidator(taglibXml.getValidator());
List<TagInfo> tagInfos = new ArrayList<>();
for (TagXml tagXml : taglibXml.getTags()) {
tagInfos.add(createTagInfo(tagXml));
}
List<TagFileInfo> tagFileInfos = new ArrayList<>();
for (TagFileXml tagFileXml : taglibXml.getTagFiles()) {
tagFileInfos.add(createTagFileInfo(tagFileXml, jar));
}
Set<String> names = new HashSet<>();
List<FunctionInfo> functionInfos = taglibXml.getFunctions();
// TODO Move this validation to the parsing stage
for (FunctionInfo functionInfo : functionInfos) {
String name = functionInfo.getName();
if (!names.add(name)) {
err.jspError("jsp.error.tld.fn.duplicate.name", name, uri);
}
}
if (tlibversion == null) {
err.jspError("jsp.error.tld.mandatory.element.missing", "tlib-version", uri);
}
if (jspversion == null) {
err.jspError("jsp.error.tld.mandatory.element.missing", "jsp-version", uri);
}
this.tags = tagInfos.toArray(new TagInfo[tagInfos.size()]);
this.tagFiles = tagFileInfos.toArray(new TagFileInfo[tagFileInfos.size()]);
this.functions = functionInfos.toArray(new FunctionInfo[functionInfos.size()]);
} catch (IOException ioe) {
throw new JasperException(ioe);
}
}
@Override
public TagLibraryInfo[] getTagLibraryInfos() {
Collection<TagLibraryInfo> coll = pi.getTaglibs();
return coll.toArray(new TagLibraryInfo[coll.size()]);
}
/*
* @param uri The uri of the TLD
* @param ctxt The compilation context
*
* @return the location of the TLD identified by the uri
*/
private TldResourcePath generateTldResourcePath(String uri,
JspCompilationContext ctxt) throws JasperException {
// TODO: this matches the current implementation but the URL logic looks fishy
// map URI to location per JSP 7.3.6.2
if (uri.indexOf(':') != -1) {
// abs_uri, this was not found in the taglibMap so raise an error
err.jspError("jsp.error.taglibDirective.absUriCannotBeResolved", uri);
} else if (uri.charAt(0) != '/') {
// noroot_rel_uri, resolve against the current JSP page
uri = ctxt.resolveRelativeUri(uri);
try {
// Can't use RequestUtils.normalize since that package is not
// available to Jasper.
uri = (new URI(uri)).normalize().toString();
if (uri.startsWith("../")) {
// Trying to go outside context root
err.jspError("jsp.error.taglibDirective.uriInvalid", uri);
}
} catch (URISyntaxException e) {
err.jspError("jsp.error.taglibDirective.uriInvalid", uri);
}
}
URL url = null;
try {
url = ctxt.getResource(uri);
} catch (Exception ex) {
err.jspError("jsp.error.tld.unable_to_get_jar", uri, ex.toString());
}
if (uri.endsWith(".jar")) {
if (url == null) {
err.jspError("jsp.error.tld.missing_jar", uri);
}
return new TldResourcePath(url, uri, "META-INF/taglib.tld");
} else if (uri.startsWith("/WEB-INF/lib/") || uri.startsWith("/WEB-INF/classes/") ||
(uri.startsWith("/WEB-INF/tags/") && uri.endsWith(".tld")&& !uri.endsWith("implicit.tld"))) {
err.jspError("jsp.error.tld.invalid_tld_file", uri);
}
return new TldResourcePath(url, uri);
}
private TagInfo createTagInfo(TagXml tagXml) throws JasperException {
String teiClassName = tagXml.getTeiClass();
TagExtraInfo tei = null;
if (teiClassName != null && !teiClassName.isEmpty()) {
try {
Class<?> teiClass = ctxt.getClassLoader().loadClass(teiClassName);
tei = (TagExtraInfo) teiClass.getConstructor().newInstance();
} catch (Exception e) {
err.jspError(e, "jsp.error.teiclass.instantiation", teiClassName);
}
}
List<TagAttributeInfo> attributeInfos = tagXml.getAttributes();
List<TagVariableInfo> variableInfos = tagXml.getVariables();
return new TagInfo(tagXml.getName(),
tagXml.getTagClass(),
tagXml.getBodyContent(),
tagXml.getInfo(),
this,
tei,
attributeInfos.toArray(new TagAttributeInfo[attributeInfos.size()]),
tagXml.getDisplayName(),
tagXml.getSmallIcon(),
tagXml.getLargeIcon(),
variableInfos.toArray(new TagVariableInfo[variableInfos.size()]),
tagXml.hasDynamicAttributes());
}
private TagFileInfo createTagFileInfo(TagFileXml tagFileXml, Jar jar) throws JasperException {
String name = tagFileXml.getName();
String path = tagFileXml.getPath();
if (path == null) {
// path is required
err.jspError("jsp.error.tagfile.missingPath");
} else if (!path.startsWith("/META-INF/tags") && !path.startsWith("/WEB-INF/tags")) {
err.jspError("jsp.error.tagfile.illegalPath", path);
}
TagInfo tagInfo =
TagFileProcessor.parseTagFileDirectives(parserController, name, path, jar, this);
return new TagFileInfo(name, path, tagInfo);
}
private TagLibraryValidator createValidator(ValidatorXml validatorXml) throws JasperException {
if (validatorXml == null) {
return null;
}
String validatorClass = validatorXml.getValidatorClass();
if (validatorClass == null || validatorClass.isEmpty()) {
return null;
}
Map<String,Object> initParams = new Hashtable<>();
initParams.putAll(validatorXml.getInitParams());
try {
Class<?> tlvClass = ctxt.getClassLoader().loadClass(validatorClass);
TagLibraryValidator tlv = (TagLibraryValidator) tlvClass.getConstructor().newInstance();
tlv.setInitParameters(initParams);
return tlv;
} catch (Exception e) {
err.jspError(e, "jsp.error.tlvclass.instantiation", validatorClass);
return null;
}
}
// *********************************************************************
// Until javax.servlet.jsp.tagext.TagLibraryInfo is fixed
/**
* The instance (if any) for the TagLibraryValidator class.
*
* @return The TagLibraryValidator instance, if any.
*/
public TagLibraryValidator getTagLibraryValidator() {
return tagLibraryValidator;
}
/**
* Translation-time validation of the XML document associated with the JSP
* page. This is a convenience method on the associated TagLibraryValidator
* class.
*
* @param thePage
* The JSP page object
* @return A string indicating whether the page is valid or not.
*/
public ValidationMessage[] validate(PageData thePage) {
TagLibraryValidator tlv = getTagLibraryValidator();
if (tlv == null)
return null;
String uri = getURI();
if (uri.startsWith("/")) {
uri = URN_JSPTLD + uri;
}
return tlv.validate(getPrefixString(), uri, thePage);
}
private TagLibraryValidator tagLibraryValidator;
}

View File

@@ -0,0 +1,291 @@
/*
* 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.compiler;
import java.io.IOException;
import java.net.URL;
import java.security.AccessController;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import org.apache.jasper.Constants;
import org.apache.jasper.JasperException;
import org.apache.jasper.compiler.tagplugin.TagPlugin;
import org.apache.jasper.compiler.tagplugin.TagPluginContext;
import org.apache.tomcat.util.descriptor.tagplugin.TagPluginParser;
import org.apache.tomcat.util.security.PrivilegedGetTccl;
import org.apache.tomcat.util.security.PrivilegedSetTccl;
import org.xml.sax.SAXException;
/**
* Manages tag plugin optimizations.
*
* @author Kin-man Chung
*/
public class TagPluginManager {
private static final String META_INF_JASPER_TAG_PLUGINS_XML =
"META-INF/org.apache.jasper/tagPlugins.xml";
private static final String TAG_PLUGINS_XML = "/WEB-INF/tagPlugins.xml";
private final ServletContext ctxt;
private HashMap<String, TagPlugin> tagPlugins;
private boolean initialized = false;
public TagPluginManager(ServletContext ctxt) {
this.ctxt = ctxt;
}
public void apply(Node.Nodes page, ErrorDispatcher err, PageInfo pageInfo)
throws JasperException {
init(err);
if (!tagPlugins.isEmpty()) {
page.visit(new NodeVisitor(this, pageInfo));
}
}
private void init(ErrorDispatcher err) throws JasperException {
if (initialized)
return;
String blockExternalString = ctxt.getInitParameter(
Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
boolean blockExternal;
if (blockExternalString == null) {
blockExternal = true;
} else {
blockExternal = Boolean.parseBoolean(blockExternalString);
}
TagPluginParser parser;
ClassLoader original;
if (Constants.IS_SECURITY_ENABLED) {
PrivilegedGetTccl pa = new PrivilegedGetTccl();
original = AccessController.doPrivileged(pa);
} else {
original = Thread.currentThread().getContextClassLoader();
}
try {
if (Constants.IS_SECURITY_ENABLED) {
PrivilegedSetTccl pa =
new PrivilegedSetTccl(TagPluginManager.class.getClassLoader());
AccessController.doPrivileged(pa);
} else {
Thread.currentThread().setContextClassLoader(
TagPluginManager.class.getClassLoader());
}
parser = new TagPluginParser(ctxt, blockExternal);
Enumeration<URL> urls =
ctxt.getClassLoader().getResources(META_INF_JASPER_TAG_PLUGINS_XML);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
parser.parse(url);
}
URL url = ctxt.getResource(TAG_PLUGINS_XML);
if (url != null) {
parser.parse(url);
}
} catch (IOException | SAXException e) {
throw new JasperException(e);
} finally {
if (Constants.IS_SECURITY_ENABLED) {
PrivilegedSetTccl pa = new PrivilegedSetTccl(original);
AccessController.doPrivileged(pa);
} else {
Thread.currentThread().setContextClassLoader(original);
}
}
Map<String, String> plugins = parser.getPlugins();
tagPlugins = new HashMap<>(plugins.size());
for (Map.Entry<String, String> entry : plugins.entrySet()) {
try {
String tagClass = entry.getKey();
String pluginName = entry.getValue();
Class<?> pluginClass = ctxt.getClassLoader().loadClass(pluginName);
TagPlugin plugin = (TagPlugin) pluginClass.getConstructor().newInstance();
tagPlugins.put(tagClass, plugin);
} catch (Exception e) {
err.jspError(e);
}
}
initialized = true;
}
/**
* Invoke tag plugin for the given custom tag, if a plugin exists for
* the custom tag's tag handler.
* <p/>
* The given custom tag node will be manipulated by the plugin.
*/
private void invokePlugin(Node.CustomTag n, PageInfo pageInfo) {
TagPlugin tagPlugin = tagPlugins.get(n.getTagHandlerClass().getName());
if (tagPlugin == null) {
return;
}
TagPluginContext tagPluginContext = new TagPluginContextImpl(n, pageInfo);
n.setTagPluginContext(tagPluginContext);
tagPlugin.doTag(tagPluginContext);
}
private static class NodeVisitor extends Node.Visitor {
private final TagPluginManager manager;
private final PageInfo pageInfo;
public NodeVisitor(TagPluginManager manager, PageInfo pageInfo) {
this.manager = manager;
this.pageInfo = pageInfo;
}
@Override
public void visit(Node.CustomTag n) throws JasperException {
manager.invokePlugin(n, pageInfo);
visitBody(n);
}
}
private static class TagPluginContextImpl implements TagPluginContext {
private final Node.CustomTag node;
private final PageInfo pageInfo;
private final HashMap<String, Object> pluginAttributes;
private Node.Nodes curNodes;
TagPluginContextImpl(Node.CustomTag n, PageInfo pageInfo) {
this.node = n;
this.pageInfo = pageInfo;
curNodes = new Node.Nodes();
n.setAtETag(curNodes);
curNodes = new Node.Nodes();
n.setAtSTag(curNodes);
n.setUseTagPlugin(true);
pluginAttributes = new HashMap<>();
}
@Override
public TagPluginContext getParentContext() {
Node parent = node.getParent();
if (!(parent instanceof Node.CustomTag)) {
return null;
}
return ((Node.CustomTag) parent).getTagPluginContext();
}
@Override
public void setPluginAttribute(String key, Object value) {
pluginAttributes.put(key, value);
}
@Override
public Object getPluginAttribute(String key) {
return pluginAttributes.get(key);
}
@Override
public boolean isScriptless() {
return node.getChildInfo().isScriptless();
}
@Override
public boolean isConstantAttribute(String attribute) {
Node.JspAttribute attr = getNodeAttribute(attribute);
if (attr == null)
return false;
return attr.isLiteral();
}
@Override
public String getConstantAttribute(String attribute) {
Node.JspAttribute attr = getNodeAttribute(attribute);
if (attr == null)
return null;
return attr.getValue();
}
@Override
public boolean isAttributeSpecified(String attribute) {
return getNodeAttribute(attribute) != null;
}
@Override
public String getTemporaryVariableName() {
return node.getRoot().nextTemporaryVariableName();
}
@Override
public void generateImport(String imp) {
pageInfo.addImport(imp);
}
@Override
public void generateDeclaration(String id, String text) {
if (pageInfo.isPluginDeclared(id)) {
return;
}
curNodes.add(new Node.Declaration(text, node.getStart(), null));
}
@Override
public void generateJavaSource(String sourceCode) {
curNodes.add(new Node.Scriptlet(sourceCode, node.getStart(),
null));
}
@Override
public void generateAttribute(String attributeName) {
curNodes.add(new Node.AttributeGenerator(node.getStart(),
attributeName,
node));
}
@Override
public void dontUseTagPlugin() {
node.setUseTagPlugin(false);
}
@Override
public void generateBody() {
// Since we'll generate the body anyway, this is really a nop,
// except for the fact that it lets us put the Java sources the
// plugins produce in the correct order (w.r.t the body).
curNodes = node.getAtETag();
}
@Override
public boolean isTagFile() {
return pageInfo.isTagFile();
}
private Node.JspAttribute getNodeAttribute(String attribute) {
Node.JspAttribute[] attrs = node.getJspAttributes();
for (int i = 0; attrs != null && i < attrs.length; i++) {
if (attrs[i].getName().equals(attribute)) {
return attrs[i];
}
}
return null;
}
}
}

View File

@@ -0,0 +1,124 @@
/*
* 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.compiler;
import org.apache.jasper.JasperException;
import org.apache.jasper.Options;
/**
*/
public class TextOptimizer {
/**
* A visitor to concatenate contiguous template texts.
*/
private static class TextCatVisitor extends Node.Visitor {
private static final String EMPTY_TEXT = "";
private final Options options;
private final PageInfo pageInfo;
private int textNodeCount = 0;
private Node.TemplateText firstTextNode = null;
private StringBuilder textBuffer;
public TextCatVisitor(Compiler compiler) {
options = compiler.getCompilationContext().getOptions();
pageInfo = compiler.getPageInfo();
}
@Override
public void doVisit(Node n) throws JasperException {
collectText();
}
/*
* The following directives are ignored in text concatenation
*/
@Override
public void visit(Node.PageDirective n) throws JasperException {
}
@Override
public void visit(Node.TagDirective n) throws JasperException {
}
@Override
public void visit(Node.TaglibDirective n) throws JasperException {
}
@Override
public void visit(Node.AttributeDirective n) throws JasperException {
}
@Override
public void visit(Node.VariableDirective n) throws JasperException {
}
/*
* Don't concatenate text across body boundaries
*/
@Override
public void visitBody(Node n) throws JasperException {
super.visitBody(n);
collectText();
}
@Override
public void visit(Node.TemplateText n) throws JasperException {
if ((options.getTrimSpaces() || pageInfo.isTrimDirectiveWhitespaces())
&& n.isAllSpace()) {
n.setText(EMPTY_TEXT);
return;
}
if (textNodeCount++ == 0) {
firstTextNode = n;
textBuffer = new StringBuilder(n.getText());
} else {
// Append text to text buffer
textBuffer.append(n.getText());
n.setText(EMPTY_TEXT);
}
}
/**
* This method breaks concatenation mode. As a side effect it copies
* the concatenated string to the first text node
*/
private void collectText() {
if (textNodeCount > 1) {
// Copy the text in buffer into the first template text node.
firstTextNode.setText(textBuffer.toString());
}
textNodeCount = 0;
}
}
public static void concatenate(Compiler compiler, Node.Nodes page)
throws JasperException {
TextCatVisitor v = new TextCatVisitor(compiler);
page.visit(v);
// Cleanup, in case the page ends with a template text
v.collectText();
}
}

View File

@@ -0,0 +1,188 @@
/*
* 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.compiler;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletContext;
import org.apache.jasper.Constants;
import org.apache.jasper.JasperException;
import org.apache.tomcat.Jar;
import org.apache.tomcat.util.descriptor.tld.TaglibXml;
import org.apache.tomcat.util.descriptor.tld.TldParser;
import org.apache.tomcat.util.descriptor.tld.TldResourcePath;
import org.xml.sax.SAXException;
/**
* This class caches parsed instances of TLD files to remove the need for the
* same TLD to be parsed for each JSP that references it. It does not protect
* against multiple threads processing the same, new TLD but it does ensure that
* each all threads will use the same TLD object after parsing.
*/
public class TldCache {
public static final String SERVLET_CONTEXT_ATTRIBUTE_NAME =
TldCache.class.getName();
private final ServletContext servletContext;
private final Map<String,TldResourcePath> uriTldResourcePathMap = new HashMap<>();
private final Map<TldResourcePath,TaglibXmlCacheEntry> tldResourcePathTaglibXmlMap =
new HashMap<>();
private final TldParser tldParser;
public static TldCache getInstance(ServletContext servletContext) {
if (servletContext == null) {
throw new IllegalArgumentException(Localizer.getMessage(
"org.apache.jasper.compiler.TldCache.servletContextNull"));
}
return (TldCache) servletContext.getAttribute(SERVLET_CONTEXT_ATTRIBUTE_NAME);
}
public TldCache(ServletContext servletContext,
Map<String, TldResourcePath> uriTldResourcePathMap,
Map<TldResourcePath, TaglibXml> tldResourcePathTaglibXmlMap) {
this.servletContext = servletContext;
this.uriTldResourcePathMap.putAll(uriTldResourcePathMap);
for (Entry<TldResourcePath, TaglibXml> entry : tldResourcePathTaglibXmlMap.entrySet()) {
TldResourcePath tldResourcePath = entry.getKey();
long lastModified[] = getLastModified(tldResourcePath);
TaglibXmlCacheEntry cacheEntry = new TaglibXmlCacheEntry(
entry.getValue(), lastModified[0], lastModified[1]);
this.tldResourcePathTaglibXmlMap.put(tldResourcePath, cacheEntry);
}
boolean validate = Boolean.parseBoolean(
servletContext.getInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM));
String blockExternalString = servletContext.getInitParameter(
Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
boolean blockExternal;
if (blockExternalString == null) {
blockExternal = true;
} else {
blockExternal = Boolean.parseBoolean(blockExternalString);
}
tldParser = new TldParser(true, validate, blockExternal);
}
public TldResourcePath getTldResourcePath(String uri) {
return uriTldResourcePathMap.get(uri);
}
public TaglibXml getTaglibXml(TldResourcePath tldResourcePath) throws JasperException {
TaglibXmlCacheEntry cacheEntry = tldResourcePathTaglibXmlMap.get(tldResourcePath);
if (cacheEntry == null) {
return null;
}
long lastModified[] = getLastModified(tldResourcePath);
if (lastModified[0] != cacheEntry.getWebAppPathLastModified() ||
lastModified[1] != cacheEntry.getEntryLastModified()) {
synchronized (cacheEntry) {
if (lastModified[0] != cacheEntry.getWebAppPathLastModified() ||
lastModified[1] != cacheEntry.getEntryLastModified()) {
// Re-parse TLD
TaglibXml updatedTaglibXml;
try {
updatedTaglibXml = tldParser.parse(tldResourcePath);
} catch (IOException | SAXException e) {
throw new JasperException(e);
}
cacheEntry.setTaglibXml(updatedTaglibXml);
cacheEntry.setWebAppPathLastModified(lastModified[0]);
cacheEntry.setEntryLastModified(lastModified[1]);
}
}
}
return cacheEntry.getTaglibXml();
}
private long[] getLastModified(TldResourcePath tldResourcePath) {
long[] result = new long[2];
result[0] = -1;
result[1] = -1;
try {
String webappPath = tldResourcePath.getWebappPath();
if (webappPath != null) {
// webappPath will be null for JARs containing TLDs that are on
// the class path but not part of the web application
URL url = servletContext.getResource(tldResourcePath.getWebappPath());
URLConnection conn = url.openConnection();
result[0] = conn.getLastModified();
if ("file".equals(url.getProtocol())) {
// Reading the last modified time opens an input stream so we
// need to make sure it is closed again otherwise the TLD file
// will be locked until GC runs.
conn.getInputStream().close();
}
}
try (Jar jar = tldResourcePath.openJar()) {
if (jar != null) {
result[1] = jar.getLastModified(tldResourcePath.getEntryName());
}
}
} catch (IOException e) {
// Ignore (shouldn't happen)
}
return result;
}
private static class TaglibXmlCacheEntry {
private volatile TaglibXml taglibXml;
private volatile long webAppPathLastModified;
private volatile long entryLastModified;
public TaglibXmlCacheEntry(TaglibXml taglibXml, long webAppPathLastModified,
long entryLastModified) {
this.taglibXml = taglibXml;
this.webAppPathLastModified = webAppPathLastModified;
this.entryLastModified = entryLastModified;
}
public TaglibXml getTaglibXml() {
return taglibXml;
}
public void setTaglibXml(TaglibXml taglibXml) {
this.taglibXml = taglibXml;
}
public long getWebAppPathLastModified() {
return webAppPathLastModified;
}
public void setWebAppPathLastModified(long webAppPathLastModified) {
this.webAppPathLastModified = webAppPathLastModified;
}
public long getEntryLastModified() {
return entryLastModified;
}
public void setEntryLastModified(long entryLastModified) {
this.entryLastModified = entryLastModified;
}
}
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More