Author: markt Date: Thu Jun 2 15:54:26 2011 New Revision: 1130618 URL: http://svn.apache.org/viewvc?rev=1130618&view=rev Log: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=51278 Allow ServletContainerInitializers to override settings in the global default web.xml and the host web.xml.
Added: tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java (with props) tomcat/trunk/test/webapp-3.0/index.html (with props) Modified: tomcat/trunk/java/org/apache/catalina/Wrapper.java tomcat/trunk/java/org/apache/catalina/core/ApplicationContext.java tomcat/trunk/java/org/apache/catalina/core/ApplicationServletRegistration.java tomcat/trunk/java/org/apache/catalina/core/StandardWrapper.java tomcat/trunk/java/org/apache/catalina/deploy/ServletDef.java tomcat/trunk/java/org/apache/catalina/deploy/WebXml.java tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java tomcat/trunk/webapps/docs/changelog.xml Modified: tomcat/trunk/java/org/apache/catalina/Wrapper.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Wrapper.java?rev=1130618&r1=1130617&r2=1130618&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/Wrapper.java (original) +++ tomcat/trunk/java/org/apache/catalina/Wrapper.java Thu Jun 2 15:54:26 2011 @@ -386,4 +386,14 @@ public interface Wrapper extends Contain * Servlet associated with this wrapper. */ public void servletSecurityAnnotationScan() throws ServletException; + + /** + * Is the Servlet overridable by a ServletContainerInitializer? + */ + public boolean isOverridable(); + + /** + * Sets the overridable attribute for this Servlet. + */ + public void setOverridable(boolean overridable); } Modified: tomcat/trunk/java/org/apache/catalina/core/ApplicationContext.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/ApplicationContext.java?rev=1130618&r1=1130617&r2=1130618&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/core/ApplicationContext.java (original) +++ tomcat/trunk/java/org/apache/catalina/core/ApplicationContext.java Thu Jun 2 15:54:26 2011 @@ -1102,7 +1102,7 @@ public class ApplicationContext Wrapper wrapper = (Wrapper) context.findChild(servletName); - // Assume a 'complete' FilterRegistration is one that has a class and + // Assume a 'complete' ServletRegistration is one that has a class and // a name if (wrapper == null) { wrapper = context.createWrapper(); @@ -1111,7 +1111,11 @@ public class ApplicationContext } else { if (wrapper.getName() != null && wrapper.getServletClass() != null) { - return null; + if (wrapper.isOverridable()) { + wrapper.setOverridable(false); + } else { + return null; + } } } Modified: tomcat/trunk/java/org/apache/catalina/core/ApplicationServletRegistration.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/ApplicationServletRegistration.java?rev=1130618&r1=1130617&r2=1130618&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/core/ApplicationServletRegistration.java (original) +++ tomcat/trunk/java/org/apache/catalina/core/ApplicationServletRegistration.java Thu Jun 2 15:54:26 2011 @@ -171,8 +171,16 @@ public class ApplicationServletRegistrat Set<String> conflicts = new HashSet<String>(); for (String urlPattern : urlPatterns) { - if (context.findServletMapping(urlPattern) != null) { - conflicts.add(urlPattern); + String wrapperName = context.findServletMapping(urlPattern); + if (wrapperName != null) { + Wrapper wrapper = (Wrapper) context.findChild(wrapperName); + if (wrapper.isOverridable()) { + // Some Wrappers (from global and host web.xml) may be + // overridden rather than generating a conflict + context.removeServletMapping(urlPattern); + } else { + conflicts.add(urlPattern); + } } } Modified: tomcat/trunk/java/org/apache/catalina/core/StandardWrapper.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardWrapper.java?rev=1130618&r1=1130617&r2=1130618&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/core/StandardWrapper.java (original) +++ tomcat/trunk/java/org/apache/catalina/core/StandardWrapper.java Thu Jun 2 15:54:26 2011 @@ -276,6 +276,8 @@ public class StandardWrapper extends Con protected volatile boolean servletSecurityAnnotationScanRequired = false; + private boolean overridable = false; + /** * Static class array used when the SecurityManager is turned on and * <code>Servlet.init</code> is invoked. @@ -294,6 +296,15 @@ public class StandardWrapper extends Con // ------------------------------------------------------------- Properties + @Override + public boolean isOverridable() { + return overridable; + } + + @Override + public void setOverridable(boolean overridable) { + this.overridable = overridable; + } /** * Return the available date/time for this servlet, in milliseconds since Modified: tomcat/trunk/java/org/apache/catalina/deploy/ServletDef.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/deploy/ServletDef.java?rev=1130618&r1=1130617&r2=1130618&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/deploy/ServletDef.java (original) +++ tomcat/trunk/java/org/apache/catalina/deploy/ServletDef.java Thu Jun 2 15:54:26 2011 @@ -264,4 +264,19 @@ public class ServletDef implements Seria public void setEnabled(String enabled) { this.enabled = Boolean.valueOf(enabled); } + + + /** + * Can this ServletDef be overridden by an SCI? + */ + private boolean overridable = false; + + public boolean isOverridable() { + return overridable; + } + + public void setOverridable(boolean overridable) { + this.overridable = overridable; + } + } Modified: tomcat/trunk/java/org/apache/catalina/deploy/WebXml.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/deploy/WebXml.java?rev=1130618&r1=1130617&r2=1130618&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/deploy/WebXml.java (original) +++ tomcat/trunk/java/org/apache/catalina/deploy/WebXml.java Thu Jun 2 15:54:26 2011 @@ -61,7 +61,18 @@ public class WebXml { private static final org.apache.juli.logging.Log log= org.apache.juli.logging.LogFactory.getLog(WebXml.class); - + + // Global defaults are overridable but Servlets and Servlet mappings need to + // be unique. Duplicates normally trigger an error. This flag indicates if + // newly added Servlet elements are marked as overridable. + private boolean overridable = false; + public boolean isOverridable() { + return overridable; + } + public void setOverridable(boolean overridable) { + this.overridable = overridable; + } + // web.xml only elements // Absolute Ordering private Set<String> absoluteOrdering = null; @@ -305,6 +316,9 @@ public class WebXml { private Map<String,ServletDef> servlets = new HashMap<String,ServletDef>(); public void addServlet(ServletDef servletDef) { servlets.put(servletDef.getServletName(), servletDef); + if (overridable) { + servletDef.setOverridable(overridable); + } } public Map<String,ServletDef> getServlets() { return servlets; } @@ -1268,6 +1282,7 @@ public class WebXml { wrapper.setAsyncSupported( servlet.getAsyncSupported().booleanValue()); } + wrapper.setOverridable(servlet.isOverridable()); context.addChild(wrapper); } for (Entry<String, String> entry : servletMappings.entrySet()) { Modified: tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java?rev=1130618&r1=1130617&r2=1130618&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java (original) +++ tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java Thu Jun 2 15:54:26 2011 @@ -1186,6 +1186,15 @@ public class ContextConfig */ protected void webConfig() { WebXml webXml = createWebXml(); + /* Anything and everything can override the global and host defaults. + * This is implemented in two parts + * - Handle as a web fragment that gets added after everything else so + * everything else takes priority + * - Mark Servlets as overridable so SCI configuration can replace + * configuration from the defaults + */ + WebXml webXmlDefaultFragment = createWebXml(); + webXmlDefaultFragment.setOverridable(true); // Parse global web.xml if present InputSource globalWebXml = getGlobalWebXmlSource(); @@ -1193,17 +1202,19 @@ public class ContextConfig // This is unusual enough to log log.info(sm.getString("contextConfig.defaultMissing")); } else { - parseWebXml(globalWebXml, webXml, false); + parseWebXml(globalWebXml, webXmlDefaultFragment, false); } // Parse host level web.xml if present // Additive apart from welcome pages webXml.setReplaceWelcomeFiles(true); InputSource hostWebXml = getHostWebXmlSource(); - parseWebXml(hostWebXml, webXml, false); + parseWebXml(hostWebXml, webXmlDefaultFragment, false); + + Set<WebXml> defaults = new HashSet<WebXml>(); + defaults.add(webXmlDefaultFragment); // Parse context level web.xml - webXml.setReplaceWelcomeFiles(true); InputSource contextWebXml = getContextWebXmlSource(); parseWebXml(contextWebXml, webXml, false); @@ -1260,16 +1271,19 @@ public class ContextConfig ok = webXml.merge(orderedFragments); } - // Step 6.5 Convert explicitly mentioned jsps to servlets + // Step 7. Convert explicitly mentioned jsps to servlets if (!false) { convertJsps(webXml); } - - // Step 7. Apply merged web.xml to Context + + // Step 8. Apply global defaults + webXml.merge(defaults); + + // Step 9. Apply merged web.xml to Context if (ok) { webXml.configureContext(context); - // Step 7a. Make the merged web.xml available to other + // Step 9a. Make the merged web.xml available to other // components, specifically Jasper, to save those components // from having to re-generate it. // TODO Use a ServletContainerInitializer for Jasper @@ -1282,11 +1296,12 @@ public class ContextConfig } } } else { + webXml.merge(defaults); webXml.configureContext(context); } // Always need to look for static resources - // Step 8. Look for static resources packaged in JARs + // Step 10. Look for static resources packaged in JARs if (ok) { // Spec does not define an order. // Use ordered JARs followed by remaining JARs @@ -1309,7 +1324,7 @@ public class ContextConfig // Only look for ServletContainerInitializer if metadata is not // complete if (!webXml.isMetadataComplete()) { - // Step 9. Apply the ServletContainerInitializer config to the + // Step 11. Apply the ServletContainerInitializer config to the // context if (ok) { for (Map.Entry<ServletContainerInitializer, @@ -1328,6 +1343,7 @@ public class ContextConfig } else { // Apply unmerged web.xml to Context convertJsps(webXml); + webXml.merge(defaults); webXml.configureContext(context); } } Added: tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java?rev=1130618&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java (added) +++ tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java Thu Jun 2 15:54:26 2011 @@ -0,0 +1,102 @@ +/* + * 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.catalina.startup; + +import java.io.File; +import java.io.IOException; +import java.util.Set; + +import javax.servlet.Servlet; +import javax.servlet.ServletContainerInitializer; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRegistration; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.catalina.core.StandardContext; +import org.apache.tomcat.util.buf.ByteChunk; + +public class TestContextConfig extends TomcatBaseTest { + + public void testOverrideWithSCIDefaultName() throws Exception { + doTestOverrideDefaultServletWithSCI("default"); + } + + public void testOverrideWithSCIDefaultMapping() throws Exception { + doTestOverrideDefaultServletWithSCI("anything"); + } + + private void doTestOverrideDefaultServletWithSCI(String servletName) + throws Exception{ + + Tomcat tomcat = getTomcatInstance(); + + File appDir = new File("test/webapp-3.0"); + StandardContext ctxt = (StandardContext) tomcat.addContext(null, + "/test", appDir.getAbsolutePath()); + ctxt.setDefaultWebXml(new File("conf/web.xml").getAbsolutePath()); + ctxt.addLifecycleListener(new ContextConfig()); + + ctxt.addServletContainerInitializer( + new CustomDefaultServletSCI(servletName), null); + + tomcat.start(); + + ByteChunk res = new ByteChunk(); + + int rc =getUrl("http://localhost:" + getPort() + "/test", res, null); + + // Check return code + assertEquals(HttpServletResponse.SC_OK, rc); + + // Check context + assertEquals("OK - Custom default Servlet", res.toString()); + } + + private static class CustomDefaultServletSCI + implements ServletContainerInitializer { + + private String servletName; + + public CustomDefaultServletSCI(String servletName) { + this.servletName = servletName; + } + + @Override + public void onStartup(Set<Class<?>> c, ServletContext ctx) + throws ServletException { + Servlet s = new CustomDefaultServlet(); + ServletRegistration.Dynamic r = ctx.addServlet(servletName, s); + r.addMapping("/"); + } + + } + + private static class CustomDefaultServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + resp.setContentType("text/plain"); + resp.getWriter().print("OK - Custom default Servlet"); + } + } +} Propchange: tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java ------------------------------------------------------------------------------ svn:eol-style = native Added: tomcat/trunk/test/webapp-3.0/index.html URL: http://svn.apache.org/viewvc/tomcat/trunk/test/webapp-3.0/index.html?rev=1130618&view=auto ============================================================================== --- tomcat/trunk/test/webapp-3.0/index.html (added) +++ tomcat/trunk/test/webapp-3.0/index.html Thu Jun 2 15:54:26 2011 @@ -0,0 +1,8 @@ +<html> + <head> + <title>Index page</title> + </head> + <body> + <p>This is the index page served by the default Servlet.</p> + </body> +</html> \ No newline at end of file Propchange: tomcat/trunk/test/webapp-3.0/index.html ------------------------------------------------------------------------------ svn:eol-style = native Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1130618&r1=1130617&r2=1130618&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Thu Jun 2 15:54:26 2011 @@ -78,6 +78,10 @@ with an incomplete FORM authentication configuration. (markt) </fix> <fix> + <bug>51278</bug>: Allow ServletContainerInitializers to override + settings in the global default web.xml and the host web.xml. (markt) + </fix> + <fix> <bug>51310</bug>: When stopping the Server object on shutdown call destroy() after calling stop(). (markt) </fix> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org