--- test/org/apache/jasper/servlet/TestJspCServletContext.java (revision 0) +++ test/org/apache/jasper/servlet/TestJspCServletContext.java (revision 0) @@ -0,0 +1,97 @@ +/* + * 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.servlet; + +import java.io.File; +import java.util.Collection; +import java.util.Iterator; + +import javax.servlet.descriptor.JspConfigDescriptor; +import javax.servlet.descriptor.JspPropertyGroupDescriptor; + +import org.junit.Assert; +import org.junit.Test; + +public class TestJspCServletContext { + + @Test + public void testWebapp() throws Exception { + File appDir = new File("test/webapp"); + JspCServletContext context = new JspCServletContext(null, appDir.toURI().toURL()); + Assert.assertEquals(3, context.getEffectiveMajorVersion()); + Assert.assertEquals(1, context.getEffectiveMinorVersion()); + JspConfigDescriptor jspConfigDescriptor = context.getJspConfigDescriptor(); + Assert.assertTrue(jspConfigDescriptor.getTaglibs().isEmpty()); + Collection propertyGroups = jspConfigDescriptor.getJspPropertyGroups(); + Assert.assertEquals(1, propertyGroups.size()); + JspPropertyGroupDescriptor groupDescriptor = propertyGroups.iterator().next(); + Assert.assertEquals("text/plain", groupDescriptor.getDefaultContentType()); + Collection urlPatterns = groupDescriptor.getUrlPatterns(); + Assert.assertEquals(2, urlPatterns.size()); + Iterator iterator = urlPatterns.iterator(); + Assert.assertEquals("/bug49nnn/bug49726a.jsp", iterator.next()); + Assert.assertEquals("/bug49nnn/bug49726b.jsp", iterator.next()); + } + + @Test + public void testWebapp_2_3() throws Exception { + File appDir = new File("test/webapp-2.3"); + JspCServletContext context = new JspCServletContext(null, appDir.toURI().toURL()); + Assert.assertEquals(2, context.getEffectiveMajorVersion()); + Assert.assertEquals(3, context.getEffectiveMinorVersion()); + } + + @Test + public void testWebapp_2_4() throws Exception { + File appDir = new File("test/webapp-2.4"); + JspCServletContext context = new JspCServletContext(null, appDir.toURI().toURL()); + Assert.assertEquals(2, context.getEffectiveMajorVersion()); + Assert.assertEquals(4, context.getEffectiveMinorVersion()); + } + + @Test + public void testWebapp_2_5() throws Exception { + File appDir = new File("test/webapp-2.5"); + JspCServletContext context = new JspCServletContext(null, appDir.toURI().toURL()); + Assert.assertEquals(2, context.getEffectiveMajorVersion()); + Assert.assertEquals(5, context.getEffectiveMinorVersion()); + } + + @Test + public void testWebapp_3_0() throws Exception { + File appDir = new File("test/webapp-3.0"); + JspCServletContext context = new JspCServletContext(null, appDir.toURI().toURL()); + Assert.assertEquals(3, context.getEffectiveMajorVersion()); + Assert.assertEquals(0, context.getEffectiveMinorVersion()); + } + + @Test + public void testWebapp_3_1() throws Exception { + File appDir = new File("test/webapp-3.1"); + JspCServletContext context = new JspCServletContext(null, appDir.toURI().toURL()); + Assert.assertEquals(3, context.getEffectiveMajorVersion()); + Assert.assertEquals(1, context.getEffectiveMinorVersion()); + } + + @Test + public void testWebresources() throws Exception { + File appDir = new File("test/webresources/dir1"); + JspCServletContext context = new JspCServletContext(null, appDir.toURI().toURL()); + Assert.assertEquals(3, context.getEffectiveMajorVersion()); + Assert.assertEquals(1, context.getEffectiveMinorVersion()); + } +} --- test/webapp/WEB-INF/web.xml (revision 1498031) +++ test/webapp/WEB-INF/web.xml (working copy) @@ -15,10 +15,10 @@ See the License for the specific language governing permissions and limitations under the License. --> - --- java/org/apache/jasper/JspC.java (revision 1498031) +++ java/org/apache/jasper/JspC.java (working copy) @@ -30,7 +30,6 @@ import java.io.PrintWriter; import java.io.Reader; import java.io.Writer; -import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; @@ -1414,15 +1413,11 @@ } } - protected void initServletContext() { - try { - context =new JspCServletContext - (new PrintWriter(System.out), - new URL("file:" + uriRoot.replace('\\','/') + '/')); - tldLocationsCache = TldLocationsCache.getInstance(context); - } catch (MalformedURLException me) { - System.out.println("**" + me); - } + protected void initServletContext() throws IOException, JasperException { + // TODO: should we use the Ant Project's log? + PrintWriter log = new PrintWriter(System.out); + URL resourceBase = new File(uriRoot).getCanonicalFile().toURI().toURL(); + context = new JspCServletContext(log, resourceBase); rctxt = new JspRuntimeContext(context, this); jspConfig = new JspConfig(context); tagPluginManager = new TagPluginManager(context); --- java/org/apache/jasper/servlet/JspCServletContext.java (revision 1498031) +++ java/org/apache/jasper/servlet/JspCServletContext.java (working copy) @@ -19,15 +19,21 @@ import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.net.MalformedURLException; import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.EnumSet; import java.util.Enumeration; import java.util.EventListener; import java.util.HashSet; import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; @@ -43,8 +49,13 @@ import javax.servlet.SessionCookieConfig; import javax.servlet.SessionTrackingMode; import javax.servlet.descriptor.JspConfigDescriptor; +import javax.servlet.descriptor.JspPropertyGroupDescriptor; +import javax.servlet.descriptor.TaglibDescriptor; +import org.apache.jasper.JasperException; import org.apache.jasper.util.ExceptionUtils; +import org.apache.jasper.xmlparser.ParserUtils; +import org.apache.jasper.xmlparser.TreeNode; /** @@ -78,11 +89,15 @@ private final URL myResourceBaseURL; + /** * Web application class loader. */ private ClassLoader loader; + private final int effectiveMajorVersion; + private final int effectiveMinorVersion; + private final JspConfigDescriptor jspConfigDescriptor; // ----------------------------------------------------------- Constructors @@ -92,15 +107,56 @@ * @param aLogWriter PrintWriter which is used for log() calls * @param aResourceBaseURL Resource base URL */ - public JspCServletContext(PrintWriter aLogWriter, URL aResourceBaseURL) { + public JspCServletContext(PrintWriter aLogWriter, URL aResourceBaseURL) throws IOException, JasperException { myAttributes = new Hashtable<>(); myLogWriter = aLogWriter; myResourceBaseURL = aResourceBaseURL; + TreeNode mergedWebApp = loadAndMerge(); + + // set effective version for this application + Version version = Version.fromString(mergedWebApp.findAttribute("version")); + effectiveMajorVersion = version.getMajor(); + effectiveMinorVersion = version.getMinor(); + + jspConfigDescriptor = new JspCJspConfigDescriptor(mergedWebApp, version); } + private TreeNode loadAndMerge() throws IOException, JasperException { + ParserUtils pu = new ParserUtils(); + TreeNode root = load(pu); + List libs = getFragments(); + for (TreeNode lib : libs) { + merge(root, lib); + } + return root; + } + private TreeNode load(ParserUtils pu) throws JasperException, IOException { + URL webUrl = getResource("/WEB-INF/web.xml"); + if (webUrl == null) { + // No web.xml in the application, use a empty one as default to support Servlet 3.0 applications. + // This avoids inconsistency between compilation triggered by JspServlet which would use the + // container's specification level and JspC which would default to Servlet 2.3 behaviour (including + // unanticipated consequences such as disabling EL evaluation). + webUrl = JspCServletContext.class.getResource("web.xml"); + } + try (InputStream is = webUrl.openStream()) { + return pu.parseXMLDocument(webUrl.toExternalForm(), is); + } + } + + private List getFragments() { + // TODO: find, parse and order web fragments + // Perhaps the logic for this in ContextConfig could be moved to o.a.tomcat.util and shared. + return Collections.emptyList(); + } + + private void merge(TreeNode root, TreeNode fragment) { + // TODO: merge a web-fragment into the main web.xml + } + // --------------------------------------------------------- Public Methods @@ -628,13 +684,13 @@ @Override public int getEffectiveMajorVersion() { - return 3; + return effectiveMajorVersion; } @Override public int getEffectiveMinorVersion() { - return 0; + return effectiveMinorVersion; } @@ -646,7 +702,7 @@ @Override public JspConfigDescriptor getJspConfigDescriptor() { - return null; + return jspConfigDescriptor; } @@ -660,4 +716,295 @@ public String getVirtualServerName() { return null; } + + private static enum Version { + VERSION_2_3(2, 3), + VERSION_2_4(2, 4), + VERSION_2_5(2, 5), + VERSION_3_0(3, 0), + VERSION_3_1(3, 1); + private final int major; + private final int minor; + private Version(int major, int minor) { + this.major = major; + this.minor = minor; + } + + private int getMajor() { + return major; + } + + private int getMinor() { + return minor; + } + + private static Version fromString(String version) { + if (version == null) { + return VERSION_2_3; + } + switch (version) { + case "2.4": + return VERSION_2_4; + case "2.5": + return VERSION_2_5; + case "3.0": + return VERSION_3_0; + case "3.1": + return VERSION_3_1; + default: + return VERSION_2_3; + } + } + } + + private static class JspCJspConfigDescriptor implements JspConfigDescriptor { + private final Collection taglibs; + private final Collection jspPropertyGroups; + + private JspCJspConfigDescriptor(TreeNode webapp, Version version) { + // In Servlet 2.3, elements were under the root and there were no property groups + if (version == Version.VERSION_2_3) { + taglibs = taglibDescriptors(webapp); + jspPropertyGroups = Collections.emptyList(); + return; + } + + // In later versions, JSP configuration is under the element + TreeNode jspConfig = webapp.findChild("jsp-config"); + if (jspConfig == null) { + taglibs = Collections.emptyList(); + jspPropertyGroups = Collections.emptyList(); + return; + } + taglibs = taglibDescriptors(jspConfig); + jspPropertyGroups = propertyGroups(jspConfig); + } + + private Collection taglibDescriptors(TreeNode parent) { + Collection descriptors = new ArrayList<>(); + Iterator taglibs = parent.findChildren("taglib"); + while (taglibs.hasNext()) { + TreeNode taglib = taglibs.next(); + final String tagUri = optionalChild(taglib, "taglib-uri"); + final String tagLoc = optionalChild(taglib, "taglib-location"); + descriptors.add(new JspCTaglibDescriptor(tagUri, tagLoc)); + } + return Collections.unmodifiableCollection(descriptors); + } + + private Collection propertyGroups(TreeNode parent) { + List descriptors = new ArrayList<>(); + Iterator groups = parent.findChildren("jsp-property-group"); + while (groups.hasNext()) { + TreeNode group = groups.next(); + String buffer = null; + String defaultContentType = null; + String deferedSyntaxAllowedAsLiteral = null; + String elIgnored = null; + String errorOnUndeclaredNamespace = null; + List includeCodas = new ArrayList<>(); + List includePreludes = new ArrayList<>(); + String isXml = null; + String pageEncoding = null; + String scriptingInvalid = null; + String trimDirectiveWhitespaces = null; + List urlPatterns = new ArrayList<>(); + Iterator child = group.findChildren(); + while (child.hasNext()) { + TreeNode node = child.next(); + String body = node.getBody(); + switch (node.getName()) { + case "buffer": + buffer = body; + break; + case "default-content-type": + defaultContentType = body; + break; + case "deferred-syntax-allowed-as-literal": + deferedSyntaxAllowedAsLiteral = body; + break; + case "el-ignored": + elIgnored = body; + break; + case "error-on-undeclared-namespace": + errorOnUndeclaredNamespace = body; + break; + case "include-coda": + includeCodas.add(body); + break; + case "include-prelude": + includePreludes.add(body); + break; + case "is-xml": + isXml = body; + break; + case "page-encoding": + pageEncoding = body; + break; + case "scripting-invalid": + scriptingInvalid = body; + break; + case "trim-directive-whitespaces": + buffer = body; + break; + case "url-pattern": + urlPatterns.add(body); + break; + } + } + descriptors.add(new JspCPropertyGroupDescriptor( + buffer, + defaultContentType, + deferedSyntaxAllowedAsLiteral, + elIgnored, + errorOnUndeclaredNamespace, + Collections.unmodifiableCollection(includeCodas), + Collections.unmodifiableCollection(includePreludes), + isXml, + pageEncoding, + scriptingInvalid, + trimDirectiveWhitespaces, + Collections.unmodifiableCollection(urlPatterns))); + } + return Collections.unmodifiableList(descriptors); + } + + private static String optionalChild(TreeNode parent, String name) { + TreeNode child = parent.findChild(name); + return child == null ? null : child.getBody(); + } + + @Override + public Collection getTaglibs() { + return taglibs; + } + + @Override + public Collection getJspPropertyGroups() { + return jspPropertyGroups; + } + + // TODO: share with org.apache.catalina.core.ApplicationTaglibDescriptor + private static class JspCTaglibDescriptor implements TaglibDescriptor { + private final String tagUri; + private final String tagLoc; + + public JspCTaglibDescriptor(String tagUri, String tagLoc) { + this.tagUri = tagUri; + this.tagLoc = tagLoc; + } + + @Override + public String getTaglibURI() { + return tagUri; + } + + @Override + public String getTaglibLocation() { + return tagLoc; + } + } + + // TODO: share with org.apache.catalina.core.ApplicationJspPropertyGroupDescriptor + private static class JspCPropertyGroupDescriptor implements JspPropertyGroupDescriptor { + private final String buffer; + private final String defaultContentType; + private final String deferedSyntaxAllowedAsLiteral; + private final String elIgnored; + private final String errorOnUndeclaredNamespace; + private final Collection includeCodas; + private final Collection includePreludes; + private final String isXml; + private final String pageEncoding; + private final String scriptingInvalid; + private final String trimDirectiveWhitespaces; + private final Collection urlPatterns; + + private JspCPropertyGroupDescriptor(String buffer, + String defaultContentType, + String deferedSyntaxAllowedAsLiteral, + String elIgnored, + String errorOnUndeclaredNamespace, + Collection includeCodas, + Collection includePreludes, + String isXml, + String pageEncoding, + String scriptingInvalid, + String trimDirectiveWhitespaces, + Collection urlPatterns) { + this.buffer = buffer; + this.defaultContentType = defaultContentType; + this.deferedSyntaxAllowedAsLiteral = deferedSyntaxAllowedAsLiteral; + this.elIgnored = elIgnored; + this.errorOnUndeclaredNamespace = errorOnUndeclaredNamespace; + this.includeCodas = includeCodas; + this.includePreludes = includePreludes; + this.isXml = isXml; + this.pageEncoding = pageEncoding; + this.scriptingInvalid = scriptingInvalid; + this.trimDirectiveWhitespaces = trimDirectiveWhitespaces; + this.urlPatterns = urlPatterns; + } + + @Override + public Collection getUrlPatterns() { + return urlPatterns; + } + + @Override + public String getElIgnored() { + return elIgnored; + } + + @Override + public String getPageEncoding() { + return pageEncoding; + } + + @Override + public String getScriptingInvalid() { + return scriptingInvalid; + } + + @Override + public String getIsXml() { + return isXml; + } + + @Override + public Collection getIncludePreludes() { + return includePreludes; + } + + @Override + public Collection getIncludeCodas() { + return includeCodas; + } + + @Override + public String getDeferredSyntaxAllowedAsLiteral() { + return deferedSyntaxAllowedAsLiteral; + } + + @Override + public String getTrimDirectiveWhitespaces() { + return trimDirectiveWhitespaces; + } + + @Override + public String getDefaultContentType() { + return defaultContentType; + } + + @Override + public String getBuffer() { + return buffer; + } + + @Override + public String getErrorOnUndeclaredNamespace() { + return errorOnUndeclaredNamespace; + } + } + } } --- java/org/apache/jasper/servlet/web.xml (revision 0) +++ java/org/apache/jasper/servlet/web.xml (revision 0) @@ -0,0 +1,25 @@ + + + + +