/*
* 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.tomcat.util.descriptor.web;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.util.descriptor.DigesterFactory;
import org.apache.tomcat.util.descriptor.XmlErrorHandler;
import org.apache.tomcat.util.descriptor.XmlIdentifiers;
import org.apache.tomcat.util.digester.Digester;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Test case for {@link WebXml}.
*/
public class TestWebXml {
@Test
public void testParseVersion() {
WebXml webxml = new WebXml();
// Defaults
Assert.assertEquals(4, webxml.getMajorVersion());
Assert.assertEquals(0, webxml.getMinorVersion());
// Both get changed
webxml.setVersion("2.5");
Assert.assertEquals(2, webxml.getMajorVersion());
Assert.assertEquals(5, webxml.getMinorVersion());
// unknown input should be ignored
webxml.setVersion("0.0");
Assert.assertEquals(2, webxml.getMajorVersion());
Assert.assertEquals(5, webxml.getMinorVersion());
// null input should be ignored
webxml.setVersion(null);
Assert.assertEquals(2, webxml.getMajorVersion());
Assert.assertEquals(5, webxml.getMinorVersion());
}
@Test
public void testParsePublicIdVersion22() {
WebXml webxml = new WebXml();
webxml.setPublicId(XmlIdentifiers.WEB_22_PUBLIC);
Assert.assertEquals(2, webxml.getMajorVersion());
Assert.assertEquals(2, webxml.getMinorVersion());
Assert.assertEquals("2.2", webxml.getVersion());
}
@Test
public void testParsePublicIdVersion23() {
WebXml webxml = new WebXml();
webxml.setPublicId(XmlIdentifiers.WEB_23_PUBLIC);
Assert.assertEquals(2, webxml.getMajorVersion());
Assert.assertEquals(3, webxml.getMinorVersion());
Assert.assertEquals("2.3", webxml.getVersion());
}
@Test
public void testParseVersion24() {
WebXml webxml = new WebXml();
webxml.setVersion("2.4");
Assert.assertEquals(2, webxml.getMajorVersion());
Assert.assertEquals(4, webxml.getMinorVersion());
Assert.assertEquals("2.4", webxml.getVersion());
}
@Test
public void testParseVersion25() {
WebXml webxml = new WebXml();
webxml.setVersion("2.5");
Assert.assertEquals(2, webxml.getMajorVersion());
Assert.assertEquals(5, webxml.getMinorVersion());
Assert.assertEquals("2.5", webxml.getVersion());
}
@Test
public void testParseVersion30() {
WebXml webxml = new WebXml();
webxml.setVersion("3.0");
Assert.assertEquals(3, webxml.getMajorVersion());
Assert.assertEquals(0, webxml.getMinorVersion());
Assert.assertEquals("3.0", webxml.getVersion());
}
@Test
public void testParseVersion31() {
WebXml webxml = new WebXml();
webxml.setVersion("3.1");
Assert.assertEquals(3, webxml.getMajorVersion());
Assert.assertEquals(1, webxml.getMinorVersion());
Assert.assertEquals("3.1", webxml.getVersion());
}
@Test
public void testParseVersion40() {
WebXml webxml = new WebXml();
webxml.setVersion("4.0");
Assert.assertEquals(4, webxml.getMajorVersion());
Assert.assertEquals(0, webxml.getMinorVersion());
Assert.assertEquals("4.0", webxml.getVersion());
}
@Test
public void testValidateVersion22() throws IOException, SAXException {
doTestValidateVersion("2.2");
}
@Test
public void testValidateVersion23() throws IOException, SAXException {
doTestValidateVersion("2.3");
}
@Test
public void testValidateVersion24() throws IOException, SAXException {
doTestValidateVersion("2.4");
}
@Test
public void testValidateVersion25() throws IOException, SAXException {
doTestValidateVersion("2.5");
}
@Test
public void testValidateVersion30() throws IOException, SAXException {
doTestValidateVersion("3.0");
}
@Test
public void testValidateVersion31() throws IOException, SAXException {
doTestValidateVersion("3.1");
}
@Test
public void testValidateVersion40() throws IOException, SAXException {
doTestValidateVersion("4.0");
}
private void doTestValidateVersion(String version) throws IOException, SAXException {
WebXml webxml = new WebXml();
// Special cases
if ("2.2".equals(version)) {
webxml.setPublicId(XmlIdentifiers.WEB_22_PUBLIC);
} else if ("2.3".equals(version)) {
webxml.setPublicId(XmlIdentifiers.WEB_23_PUBLIC);
} else {
webxml.setVersion(version);
}
// Merged web.xml that is published as MERGED_WEB_XML context attribute
// in the simplest case consists of webapp's web.xml file
// plus the default conf/web.xml one.
Set<WebXml> defaults = new HashSet<>();
defaults.add(getDefaultWebXmlFragment());
webxml.merge(defaults);
Digester digester = DigesterFactory.newDigester(true, true, new WebRuleSet(), true);
XmlErrorHandler handler = new XmlErrorHandler();
digester.setErrorHandler(handler);
InputSource is = new InputSource(new StringReader(webxml.toXml()));
WebXml webxmlResult = new WebXml();
digester.push(webxmlResult);
digester.parse(is);
Assert.assertEquals(0, handler.getErrors().size());
Assert.assertEquals(0, handler.getWarnings().size());
Assert.assertEquals(version, webxml.getVersion());
Assert.assertEquals(version, webxmlResult.getVersion());
}
// A simplified copy of ContextConfig.getDefaultWebXmlFragment().
// Assuming that global web.xml exists, host-specific web.xml does not exist.
private WebXml getDefaultWebXmlFragment() throws IOException, SAXException {
InputSource globalWebXml = new InputSource(new File("conf/web.xml")
.getAbsoluteFile().toURI().toString());
WebXml webXmlDefaultFragment = new WebXml();
webXmlDefaultFragment.setOverridable(true);
webXmlDefaultFragment.setDistributable(true);
webXmlDefaultFragment.setAlwaysAddWelcomeFiles(false);
Digester digester = DigesterFactory.newDigester(true, true, new WebRuleSet(), true);
XmlErrorHandler handler = new XmlErrorHandler();
digester.setErrorHandler(handler);
digester.push(webXmlDefaultFragment);
digester.parse(globalWebXml);
Assert.assertEquals(0, handler.getErrors().size());
Assert.assertEquals(0, handler.getWarnings().size());
webXmlDefaultFragment.setReplaceWelcomeFiles(true);
// Assert that web.xml was parsed and is not empty. Default servlet is known to be there.
Assert.assertNotNull(webXmlDefaultFragment.getServlets().get("default"));
// Manually add some version specific features to ensure that these do
// not cause problems for the merged web.xml
// Filters were added in 2.3 so should be excluded in 2.2
FilterDef filterDef = new FilterDef();
filterDef.setFilterClass("org.apache.tomcat.DummyFilter");
filterDef.setFilterName("Dummy");
webXmlDefaultFragment.addFilter(filterDef);
FilterMap filterMap = new FilterMap();
filterMap.setFilterName("Dummy");
filterMap.addURLPatternDecoded("/*");
webXmlDefaultFragment.addFilterMapping(filterMap);
// Listeners were added in 2.3 so should be excluded in 2.2
webXmlDefaultFragment.addListener("org.apache.tomcat.DummyListener");
// resource-env-ref was added in 2.3 so should be excluded in 2.2
ContextResourceEnvRef resourceEnvRef = new ContextResourceEnvRef();
resourceEnvRef.setName("dummy");
resourceEnvRef.setType("dummy");
webXmlDefaultFragment.addResourceEnvRef(resourceEnvRef);
// ejb-local-ref was added in 2.3 so should be excluded in 2.2
ContextLocalEjb ejbLocalRef = new ContextLocalEjb();
ejbLocalRef.setName("dummy");
ejbLocalRef.setType("Session");
ejbLocalRef.setLocal("dummy");
ejbLocalRef.setHome("dummy");
webXmlDefaultFragment.addEjbLocalRef(ejbLocalRef);
// Servlet/run-as was added in 2.3 so should be excluded in 2.2
ServletDef servletDef = new ServletDef();
servletDef.setServletName("Dummy");
servletDef.setServletClass("org.apache.tomcat.DummyServlet");
servletDef.setRunAs("dummy");
webXmlDefaultFragment.addServlet(servletDef);
webXmlDefaultFragment.addServletMapping("/dummy", "Dummy");
// resource-ref/res-sharing-scope was added in 2.3 so should be excluded
// in 2.2
ContextResource contextResource = new ContextResource();
contextResource.setName("dummy");
contextResource.setType("dummy");
contextResource.setAuth("Container");
contextResource.setScope("Shareable");
webXmlDefaultFragment.addResourceRef(contextResource);
// security-constraint/display-name was added in 2.3 so should be
// excluded in 2.2
SecurityConstraint sc = new SecurityConstraint();
sc.setDisplayName("dummy");
SecurityCollection collection = new SecurityCollection();
collection.setName("dummy");
collection.addPatternDecoded("/*");
collection.addMethod("DELETE");
sc.addCollection(collection);
webXmlDefaultFragment.addSecurityConstraint(sc);
// service-ref was added in 2.4 so should be excluded in 2.3 and earlier
ContextService serviceRef = new ContextService();
serviceRef.setName("dummy");
serviceRef.setInterface("dummy");
webXmlDefaultFragment.addServiceRef(serviceRef);
// message-destination-ref was added in 2.4 so should be excluded in 2.3
// and earlier
MessageDestinationRef mdRef = new MessageDestinationRef();
mdRef.setName("dummy");
mdRef.setType("dummy");
mdRef.setUsage("Consumes");
webXmlDefaultFragment.addMessageDestinationRef(mdRef);
// message-destination was added in 2.4 so should be excluded in 2.3
// and earlier
MessageDestination md = new MessageDestination();
md.setName("dummy");
webXmlDefaultFragment.addMessageDestination(md);
// local-encoding-mapping-list was added in 2.4 so should be excluded in
// 2.3 and earlier
webXmlDefaultFragment.addLocaleEncodingMapping("en", "UTF-8");
// jsp-config was added in Servlet 2.4
webXmlDefaultFragment.addTaglib("dummy", "dummy");
// filter-mapping/dispatcher added in Servlet 2.4
filterMap.setDispatcher("REQUEST");
// listener-[description|display-name|icon] added in Servlet 2.4
// None of these are supported in WebXml
// filter-mapping/dispatcher/ASYNC added in Servlet 3.0
filterMap.setDispatcher("ASYNC");
// error-page with just location allowed in Servlet 3.0+
ErrorPage errorPage = new ErrorPage();
errorPage.setLocation("/dummy");
webXmlDefaultFragment.addErrorPage(errorPage);
// async-supported added to Servlet and Filter in 3.0
filterDef.setAsyncSupported("false");
servletDef.setAsyncSupported("false");
// session-cookie-config added in 3.0
SessionConfig sessionConfig = new SessionConfig();
sessionConfig.setCookieDomain("dummy");
webXmlDefaultFragment.setSessionConfig(sessionConfig);
// http-method-omission added in Servlet 3.0
// Let this trigger a validation error as dropping it silently could
// be a security concern
// multi-part-config added in Servlet 3.0
MultipartDef multiPart = new MultipartDef();
servletDef.setMultipartDef(multiPart);
// deny-uncovered-http-methods added in Servlet 3.1
webXmlDefaultFragment.setDenyUncoveredHttpMethods(true);
return webXmlDefaultFragment;
}
@Test
public void testLifecycleMethodsWebXml() {
WebXml webxml = new WebXml();
webxml.addPostConstructMethods("a", "a");
webxml.addPreDestroyMethods("b", "b");
WebXml fragment = new WebXml();
fragment.addPostConstructMethods("c", "c");
fragment.addPreDestroyMethods("d", "d");
Set<WebXml> fragments = new HashSet<>();
fragments.add(fragment);
webxml.merge(fragments);
Map<String, String> postConstructMethods = webxml.getPostConstructMethods();
Map<String, String> preDestroyMethods = webxml.getPreDestroyMethods();
Assert.assertEquals(1, postConstructMethods.size());
Assert.assertEquals(1, preDestroyMethods.size());
Assert.assertEquals("a", postConstructMethods.get("a"));
Assert.assertEquals("b", preDestroyMethods.get("b"));
}
@Test
public void testLifecycleMethodsWebFragments() {
WebXml webxml = new WebXml();
WebXml fragment1 = new WebXml();
fragment1.addPostConstructMethods("a", "a");
fragment1.addPreDestroyMethods("b", "b");
WebXml fragment2 = new WebXml();
fragment2.addPostConstructMethods("c", "c");
fragment2.addPreDestroyMethods("d", "d");
Set<WebXml> fragments = new HashSet<>();
fragments.add(fragment1);
fragments.add(fragment2);
webxml.merge(fragments);
Map<String, String> postConstructMethods = webxml.getPostConstructMethods();
Map<String, String> preDestroyMethods = webxml.getPreDestroyMethods();
Assert.assertEquals(2, postConstructMethods.size());
Assert.assertEquals(2, preDestroyMethods.size());
Assert.assertEquals("a", postConstructMethods.get("a"));
Assert.assertEquals("c", postConstructMethods.get("c"));
Assert.assertEquals("b", preDestroyMethods.get("b"));
Assert.assertEquals("d", preDestroyMethods.get("d"));
}
@Test
public void testLifecycleMethodsWebFragmentsWithConflicts() {
WebXml webxml = new WebXml();
WebXml fragment1 = new WebXml();
fragment1.addPostConstructMethods("a", "a");
fragment1.addPreDestroyMethods("b", "a");
WebXml fragment2 = new WebXml();
fragment2.addPostConstructMethods("a", "b");
Set<WebXml> fragments = new HashSet<>();
fragments.add(fragment1);
fragments.add(fragment2);
Assert.assertFalse(webxml.merge(fragments));
Assert.assertEquals(0, webxml.getPostConstructMethods().size());
WebXml fragment3 = new WebXml();
fragment3.addPreDestroyMethods("b", "b");
fragments.remove(fragment2);
fragments.add(fragment3);
Assert.assertFalse(webxml.merge(fragments));
Assert.assertEquals(0, webxml.getPreDestroyMethods().size());
}
@Test(expected=IllegalArgumentException.class)
public void testBug54387a() {
// Multiple servlets may not be mapped to the same url-pattern
WebXml webxml = new WebXml();
webxml.addServletMapping("/foo", "a");
webxml.addServletMapping("/foo", "b");
}
@Test(expected=IllegalArgumentException.class)
public void testBug54387b() {
// Multiple servlets may not be mapped to the same url-pattern
WebXml webxml = new WebXml();
WebXml f1 = new WebXml();
WebXml f2 = new WebXml();
HashSet<WebXml> fragments = new HashSet<>();
fragments.add(f1);
fragments.add(f2);
f1.addServletMapping("/foo", "a");
f2.addServletMapping("/foo", "b");
webxml.merge(fragments);
}
@Test
public void testBug54387c() {
// Multiple servlets may not be mapped to the same url-pattern but main
// web.xml takes priority
WebXml webxml = new WebXml();
WebXml f1 = new WebXml();
WebXml f2 = new WebXml();
HashSet<WebXml> fragments = new HashSet<>();
fragments.add(f1);
fragments.add(f2);
f1.addServletMapping("/foo", "a");
f2.addServletMapping("/foo", "b");
webxml.addServletMapping("/foo", "main");
webxml.merge(fragments);
}
}