530 lines
19 KiB
Java
530 lines
19 KiB
Java
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
* (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package org.apache.jasper.compiler;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.Vector;
|
|
|
|
import javax.servlet.ServletContext;
|
|
import javax.servlet.descriptor.JspConfigDescriptor;
|
|
import javax.servlet.descriptor.JspPropertyGroupDescriptor;
|
|
|
|
import org.apache.juli.logging.Log;
|
|
import org.apache.juli.logging.LogFactory;
|
|
|
|
/**
|
|
* Handles the jsp-config element in WEB_INF/web.xml. This is used
|
|
* for specifying the JSP configuration information on a JSP page
|
|
*
|
|
* @author Kin-man Chung
|
|
* @author Remy Maucherat
|
|
*/
|
|
|
|
public class JspConfig {
|
|
|
|
// Logger
|
|
private final Log log = LogFactory.getLog(JspConfig.class); // must not be static
|
|
|
|
private Vector<JspPropertyGroup> jspProperties = null;
|
|
private final ServletContext ctxt;
|
|
private volatile boolean initialized = false;
|
|
|
|
private static final String defaultIsXml = null; // unspecified
|
|
private String defaultIsELIgnored = null; // unspecified
|
|
private static final String defaultIsScriptingInvalid = null;
|
|
private String defaultDeferedSyntaxAllowedAsLiteral = null;
|
|
private static final String defaultTrimDirectiveWhitespaces = null;
|
|
private static final String defaultDefaultContentType = null;
|
|
private static final String defaultBuffer = null;
|
|
private static final String defaultErrorOnUndeclaredNamespace = "false";
|
|
private JspProperty defaultJspProperty;
|
|
|
|
public JspConfig(ServletContext ctxt) {
|
|
this.ctxt = ctxt;
|
|
}
|
|
|
|
private void processWebDotXml() {
|
|
|
|
// Very, very unlikely but just in case...
|
|
if (ctxt.getEffectiveMajorVersion() < 2) {
|
|
defaultIsELIgnored = "true";
|
|
defaultDeferedSyntaxAllowedAsLiteral = "true";
|
|
return;
|
|
}
|
|
if (ctxt.getEffectiveMajorVersion() == 2) {
|
|
if (ctxt.getEffectiveMinorVersion() < 5) {
|
|
defaultDeferedSyntaxAllowedAsLiteral = "true";
|
|
}
|
|
if (ctxt.getEffectiveMinorVersion() < 4) {
|
|
defaultIsELIgnored = "true";
|
|
return;
|
|
}
|
|
}
|
|
|
|
JspConfigDescriptor jspConfig = ctxt.getJspConfigDescriptor();
|
|
|
|
if (jspConfig == null) {
|
|
return;
|
|
}
|
|
|
|
jspProperties = new Vector<>();
|
|
Collection<JspPropertyGroupDescriptor> jspPropertyGroups =
|
|
jspConfig.getJspPropertyGroups();
|
|
|
|
for (JspPropertyGroupDescriptor jspPropertyGroup : jspPropertyGroups) {
|
|
|
|
Collection<String> urlPatterns = jspPropertyGroup.getUrlPatterns();
|
|
|
|
if (urlPatterns.size() == 0) {
|
|
continue;
|
|
}
|
|
|
|
JspProperty property = new JspProperty(jspPropertyGroup.getIsXml(),
|
|
jspPropertyGroup.getElIgnored(),
|
|
jspPropertyGroup.getScriptingInvalid(),
|
|
jspPropertyGroup.getPageEncoding(),
|
|
jspPropertyGroup.getIncludePreludes(),
|
|
jspPropertyGroup.getIncludeCodas(),
|
|
jspPropertyGroup.getDeferredSyntaxAllowedAsLiteral(),
|
|
jspPropertyGroup.getTrimDirectiveWhitespaces(),
|
|
jspPropertyGroup.getDefaultContentType(),
|
|
jspPropertyGroup.getBuffer(),
|
|
jspPropertyGroup.getErrorOnUndeclaredNamespace());
|
|
|
|
// Add one JspPropertyGroup for each URL Pattern. This makes
|
|
// the matching logic easier.
|
|
for (String urlPattern : urlPatterns) {
|
|
String path = null;
|
|
String extension = null;
|
|
|
|
if (urlPattern.indexOf('*') < 0) {
|
|
// Exact match
|
|
path = urlPattern;
|
|
} else {
|
|
int i = urlPattern.lastIndexOf('/');
|
|
String file;
|
|
if (i >= 0) {
|
|
path = urlPattern.substring(0,i+1);
|
|
file = urlPattern.substring(i+1);
|
|
} else {
|
|
file = urlPattern;
|
|
}
|
|
|
|
// pattern must be "*", or of the form "*.jsp"
|
|
if (file.equals("*")) {
|
|
extension = "*";
|
|
} else if (file.startsWith("*.")) {
|
|
extension = file.substring(file.indexOf('.')+1);
|
|
}
|
|
|
|
// The url patterns are reconstructed as the following:
|
|
// path != null, extension == null: / or /foo/bar.ext
|
|
// path == null, extension != null: *.ext
|
|
// path != null, extension == "*": /foo/*
|
|
boolean isStar = "*".equals(extension);
|
|
if ((path == null && (extension == null || isStar))
|
|
|| (path != null && !isStar)) {
|
|
if (log.isWarnEnabled()) {
|
|
log.warn(Localizer.getMessage(
|
|
"jsp.warning.bad.urlpattern.propertygroup",
|
|
urlPattern));
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
JspPropertyGroup propertyGroup =
|
|
new JspPropertyGroup(path, extension, property);
|
|
|
|
jspProperties.addElement(propertyGroup);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void init() {
|
|
|
|
if (!initialized) {
|
|
synchronized (this) {
|
|
if (!initialized) {
|
|
processWebDotXml();
|
|
defaultJspProperty = new JspProperty(defaultIsXml,
|
|
defaultIsELIgnored,
|
|
defaultIsScriptingInvalid,
|
|
null, null, null,
|
|
defaultDeferedSyntaxAllowedAsLiteral,
|
|
defaultTrimDirectiveWhitespaces,
|
|
defaultDefaultContentType,
|
|
defaultBuffer,
|
|
defaultErrorOnUndeclaredNamespace);
|
|
initialized = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Select the property group that has more restrictive url-pattern.
|
|
* In case of tie, select the first.
|
|
*/
|
|
@SuppressWarnings("null") // NPE not possible
|
|
private JspPropertyGroup selectProperty(JspPropertyGroup prev,
|
|
JspPropertyGroup curr) {
|
|
if (prev == null) {
|
|
return curr;
|
|
}
|
|
if (prev.getExtension() == null) {
|
|
// exact match
|
|
return prev;
|
|
}
|
|
if (curr.getExtension() == null) {
|
|
// exact match
|
|
return curr;
|
|
}
|
|
String prevPath = prev.getPath();
|
|
String currPath = curr.getPath();
|
|
if (prevPath == null && currPath == null) {
|
|
// Both specifies a *.ext, keep the first one
|
|
return prev;
|
|
}
|
|
if (prevPath == null && currPath != null) {
|
|
return curr;
|
|
}
|
|
if (prevPath != null && currPath == null) {
|
|
return prev;
|
|
}
|
|
if (prevPath.length() >= currPath.length()) {
|
|
return prev;
|
|
}
|
|
return curr;
|
|
}
|
|
|
|
|
|
/**
|
|
* Find a property that best matches the supplied resource.
|
|
* @param uri the resource supplied.
|
|
* @return a JspProperty indicating the best match, or some default.
|
|
*/
|
|
public JspProperty findJspProperty(String uri) {
|
|
|
|
init();
|
|
|
|
// JSP Configuration settings do not apply to tag files
|
|
if (jspProperties == null || uri.endsWith(".tag")
|
|
|| uri.endsWith(".tagx")) {
|
|
return defaultJspProperty;
|
|
}
|
|
|
|
String uriPath = null;
|
|
int index = uri.lastIndexOf('/');
|
|
if (index >=0 ) {
|
|
uriPath = uri.substring(0, index+1);
|
|
}
|
|
String uriExtension = null;
|
|
index = uri.lastIndexOf('.');
|
|
if (index >=0) {
|
|
uriExtension = uri.substring(index+1);
|
|
}
|
|
|
|
Collection<String> includePreludes = new ArrayList<>();
|
|
Collection<String> includeCodas = new ArrayList<>();
|
|
|
|
JspPropertyGroup isXmlMatch = null;
|
|
JspPropertyGroup elIgnoredMatch = null;
|
|
JspPropertyGroup scriptingInvalidMatch = null;
|
|
JspPropertyGroup pageEncodingMatch = null;
|
|
JspPropertyGroup deferedSyntaxAllowedAsLiteralMatch = null;
|
|
JspPropertyGroup trimDirectiveWhitespacesMatch = null;
|
|
JspPropertyGroup defaultContentTypeMatch = null;
|
|
JspPropertyGroup bufferMatch = null;
|
|
JspPropertyGroup errorOnUndeclaredNamespaceMatch = null;
|
|
|
|
for (JspPropertyGroup jpg : jspProperties) {
|
|
JspProperty jp = jpg.getJspProperty();
|
|
|
|
// (arrays will be the same length)
|
|
String extension = jpg.getExtension();
|
|
String path = jpg.getPath();
|
|
|
|
if (extension == null) {
|
|
// exact match pattern: /a/foo.jsp
|
|
if (!uri.equals(path)) {
|
|
// not matched;
|
|
continue;
|
|
}
|
|
} else {
|
|
// Matching patterns *.ext or /p/*
|
|
if (path != null && uriPath != null &&
|
|
! uriPath.startsWith(path)) {
|
|
// not matched
|
|
continue;
|
|
}
|
|
if (!extension.equals("*") &&
|
|
!extension.equals(uriExtension)) {
|
|
// not matched
|
|
continue;
|
|
}
|
|
}
|
|
// We have a match
|
|
// Add include-preludes and include-codas
|
|
if (jp.getIncludePrelude() != null) {
|
|
includePreludes.addAll(jp.getIncludePrelude());
|
|
}
|
|
if (jp.getIncludeCoda() != null) {
|
|
includeCodas.addAll(jp.getIncludeCoda());
|
|
}
|
|
|
|
// If there is a previous match for the same property, remember
|
|
// the one that is more restrictive.
|
|
if (jp.isXml() != null) {
|
|
isXmlMatch = selectProperty(isXmlMatch, jpg);
|
|
}
|
|
if (jp.isELIgnored() != null) {
|
|
elIgnoredMatch = selectProperty(elIgnoredMatch, jpg);
|
|
}
|
|
if (jp.isScriptingInvalid() != null) {
|
|
scriptingInvalidMatch =
|
|
selectProperty(scriptingInvalidMatch, jpg);
|
|
}
|
|
if (jp.getPageEncoding() != null) {
|
|
pageEncodingMatch = selectProperty(pageEncodingMatch, jpg);
|
|
}
|
|
if (jp.isDeferedSyntaxAllowedAsLiteral() != null) {
|
|
deferedSyntaxAllowedAsLiteralMatch =
|
|
selectProperty(deferedSyntaxAllowedAsLiteralMatch, jpg);
|
|
}
|
|
if (jp.isTrimDirectiveWhitespaces() != null) {
|
|
trimDirectiveWhitespacesMatch =
|
|
selectProperty(trimDirectiveWhitespacesMatch, jpg);
|
|
}
|
|
if (jp.getDefaultContentType() != null) {
|
|
defaultContentTypeMatch =
|
|
selectProperty(defaultContentTypeMatch, jpg);
|
|
}
|
|
if (jp.getBuffer() != null) {
|
|
bufferMatch = selectProperty(bufferMatch, jpg);
|
|
}
|
|
if (jp.isErrorOnUndeclaredNamespace() != null) {
|
|
errorOnUndeclaredNamespaceMatch =
|
|
selectProperty(errorOnUndeclaredNamespaceMatch, jpg);
|
|
}
|
|
}
|
|
|
|
|
|
String isXml = defaultIsXml;
|
|
String isELIgnored = defaultIsELIgnored;
|
|
String isScriptingInvalid = defaultIsScriptingInvalid;
|
|
String pageEncoding = null;
|
|
String isDeferedSyntaxAllowedAsLiteral =
|
|
defaultDeferedSyntaxAllowedAsLiteral;
|
|
String isTrimDirectiveWhitespaces = defaultTrimDirectiveWhitespaces;
|
|
String defaultContentType = defaultDefaultContentType;
|
|
String buffer = defaultBuffer;
|
|
String errorOnUndeclaredNamespace = defaultErrorOnUndeclaredNamespace;
|
|
|
|
if (isXmlMatch != null) {
|
|
isXml = isXmlMatch.getJspProperty().isXml();
|
|
}
|
|
if (elIgnoredMatch != null) {
|
|
isELIgnored = elIgnoredMatch.getJspProperty().isELIgnored();
|
|
}
|
|
if (scriptingInvalidMatch != null) {
|
|
isScriptingInvalid =
|
|
scriptingInvalidMatch.getJspProperty().isScriptingInvalid();
|
|
}
|
|
if (pageEncodingMatch != null) {
|
|
pageEncoding = pageEncodingMatch.getJspProperty().getPageEncoding();
|
|
}
|
|
if (deferedSyntaxAllowedAsLiteralMatch != null) {
|
|
isDeferedSyntaxAllowedAsLiteral =
|
|
deferedSyntaxAllowedAsLiteralMatch.getJspProperty().isDeferedSyntaxAllowedAsLiteral();
|
|
}
|
|
if (trimDirectiveWhitespacesMatch != null) {
|
|
isTrimDirectiveWhitespaces =
|
|
trimDirectiveWhitespacesMatch.getJspProperty().isTrimDirectiveWhitespaces();
|
|
}
|
|
if (defaultContentTypeMatch != null) {
|
|
defaultContentType =
|
|
defaultContentTypeMatch.getJspProperty().getDefaultContentType();
|
|
}
|
|
if (bufferMatch != null) {
|
|
buffer = bufferMatch.getJspProperty().getBuffer();
|
|
}
|
|
if (errorOnUndeclaredNamespaceMatch != null) {
|
|
errorOnUndeclaredNamespace =
|
|
errorOnUndeclaredNamespaceMatch.getJspProperty().isErrorOnUndeclaredNamespace();
|
|
}
|
|
|
|
return new JspProperty(isXml, isELIgnored, isScriptingInvalid,
|
|
pageEncoding, includePreludes, includeCodas,
|
|
isDeferedSyntaxAllowedAsLiteral, isTrimDirectiveWhitespaces,
|
|
defaultContentType, buffer, errorOnUndeclaredNamespace);
|
|
}
|
|
|
|
/**
|
|
* To find out if a uri matches a url pattern in jsp config. If so,
|
|
* then the uri is a JSP page. This is used primarily for jspc.
|
|
* @param uri The path to check
|
|
* @return <code>true</code> if the path denotes a JSP page
|
|
*/
|
|
public boolean isJspPage(String uri) {
|
|
|
|
init();
|
|
if (jspProperties == null) {
|
|
return false;
|
|
}
|
|
|
|
String uriPath = null;
|
|
int index = uri.lastIndexOf('/');
|
|
if (index >=0 ) {
|
|
uriPath = uri.substring(0, index+1);
|
|
}
|
|
String uriExtension = null;
|
|
index = uri.lastIndexOf('.');
|
|
if (index >=0) {
|
|
uriExtension = uri.substring(index+1);
|
|
}
|
|
|
|
for (JspPropertyGroup jpg : jspProperties) {
|
|
|
|
String extension = jpg.getExtension();
|
|
String path = jpg.getPath();
|
|
|
|
if (extension == null) {
|
|
if (uri.equals(path)) {
|
|
// There is an exact match
|
|
return true;
|
|
}
|
|
} else {
|
|
if ((path == null || path.equals(uriPath)) &&
|
|
(extension.equals("*") || extension.equals(uriExtension))) {
|
|
// Matches *, *.ext, /p/*, or /p/*.ext
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static class JspPropertyGroup {
|
|
private final String path;
|
|
private final String extension;
|
|
private final JspProperty jspProperty;
|
|
|
|
JspPropertyGroup(String path, String extension,
|
|
JspProperty jspProperty) {
|
|
this.path = path;
|
|
this.extension = extension;
|
|
this.jspProperty = jspProperty;
|
|
}
|
|
|
|
public String getPath() {
|
|
return path;
|
|
}
|
|
|
|
public String getExtension() {
|
|
return extension;
|
|
}
|
|
|
|
public JspProperty getJspProperty() {
|
|
return jspProperty;
|
|
}
|
|
}
|
|
|
|
public static class JspProperty {
|
|
|
|
private final String isXml;
|
|
private final String elIgnored;
|
|
private final String scriptingInvalid;
|
|
private final String pageEncoding;
|
|
private final Collection<String> includePrelude;
|
|
private final Collection<String> includeCoda;
|
|
private final String deferedSyntaxAllowedAsLiteral;
|
|
private final String trimDirectiveWhitespaces;
|
|
private final String defaultContentType;
|
|
private final String buffer;
|
|
private final String errorOnUndeclaredNamespace;
|
|
|
|
public JspProperty(String isXml, String elIgnored,
|
|
String scriptingInvalid, String pageEncoding,
|
|
Collection<String> includePrelude, Collection<String> includeCoda,
|
|
String deferedSyntaxAllowedAsLiteral,
|
|
String trimDirectiveWhitespaces,
|
|
String defaultContentType,
|
|
String buffer,
|
|
String errorOnUndeclaredNamespace) {
|
|
|
|
this.isXml = isXml;
|
|
this.elIgnored = elIgnored;
|
|
this.scriptingInvalid = scriptingInvalid;
|
|
this.pageEncoding = pageEncoding;
|
|
this.includePrelude = includePrelude;
|
|
this.includeCoda = includeCoda;
|
|
this.deferedSyntaxAllowedAsLiteral = deferedSyntaxAllowedAsLiteral;
|
|
this.trimDirectiveWhitespaces = trimDirectiveWhitespaces;
|
|
this.defaultContentType = defaultContentType;
|
|
this.buffer = buffer;
|
|
this.errorOnUndeclaredNamespace = errorOnUndeclaredNamespace;
|
|
}
|
|
|
|
public String isXml() {
|
|
return isXml;
|
|
}
|
|
|
|
public String isELIgnored() {
|
|
return elIgnored;
|
|
}
|
|
|
|
public String isScriptingInvalid() {
|
|
return scriptingInvalid;
|
|
}
|
|
|
|
public String getPageEncoding() {
|
|
return pageEncoding;
|
|
}
|
|
|
|
public Collection<String> getIncludePrelude() {
|
|
return includePrelude;
|
|
}
|
|
|
|
public Collection<String> getIncludeCoda() {
|
|
return includeCoda;
|
|
}
|
|
|
|
public String isDeferedSyntaxAllowedAsLiteral() {
|
|
return deferedSyntaxAllowedAsLiteral;
|
|
}
|
|
|
|
public String isTrimDirectiveWhitespaces() {
|
|
return trimDirectiveWhitespaces;
|
|
}
|
|
|
|
public String getDefaultContentType() {
|
|
return defaultContentType;
|
|
}
|
|
|
|
public String getBuffer() {
|
|
return buffer;
|
|
}
|
|
|
|
public String isErrorOnUndeclaredNamespace() {
|
|
return errorOnUndeclaredNamespace;
|
|
}
|
|
}
|
|
}
|