diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java index ed5be4eba44..54312406042 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java @@ -107,6 +107,7 @@ public class ServletHandler extends ScopedHandler private boolean _startWithUnavailable=false; private boolean _ensureDefaultServlet=true; private IdentityService _identityService; + private boolean _allowDuplicateMappings=false; private ServletHolder[] _servlets=new ServletHolder[0]; private ServletMapping[] _servletMappings; @@ -686,6 +687,22 @@ public class ServletHandler extends ScopedHandler _startWithUnavailable=start; } + /** + * @return the allowDuplicateMappings + */ + public boolean isAllowDuplicateMappings() + { + return _allowDuplicateMappings; + } + + /** + * @param allowDuplicateMappings the allowDuplicateMappings to set + */ + public void setAllowDuplicateMappings(boolean allowDuplicateMappings) + { + _allowDuplicateMappings = allowDuplicateMappings; + } + /* ------------------------------------------------------------ */ /** * @return True if this handler will start with unavailable servlets @@ -1312,7 +1329,7 @@ public class ServletHandler extends ScopedHandler Map servletPathMappings = new HashMap<>(); //create a map of paths to set of ServletMappings that define that mapping - HashMap> sms = new HashMap<>(); + HashMap> sms = new HashMap<>(); for (ServletMapping servletMapping : _servletMappings) { String[] pathSpecs = servletMapping.getPathSpecs(); @@ -1320,10 +1337,10 @@ public class ServletHandler extends ScopedHandler { for (String pathSpec : pathSpecs) { - Set mappings = sms.get(pathSpec); + List mappings = sms.get(pathSpec); if (mappings == null) { - mappings = new HashSet<>(); + mappings = new ArrayList<>(); sms.put(pathSpec, mappings); } mappings.add(servletMapping); @@ -1336,7 +1353,7 @@ public class ServletHandler extends ScopedHandler { //for each path, look at the mappings where it is referenced //if a mapping is for a servlet that is not enabled, skip it - Set mappings = sms.get(pathSpec); + List mappings = sms.get(pathSpec); ServletMapping finalMapping = null; for (ServletMapping mapping : mappings) @@ -1354,9 +1371,15 @@ public class ServletHandler extends ScopedHandler finalMapping = mapping; else { - //already have a candidate - only accept another one if the candidate is a default + //already have a candidate - only accept another one + //if the candidate is a default, or we're allowing duplicate mappings if (finalMapping.isDefault()) finalMapping = mapping; + else if (isAllowDuplicateMappings()) + { + LOG.warn("Multiple servlets map to path: "+pathSpec+": "+finalMapping.getServletName()+","+mapping.getServletName()); + finalMapping = mapping; + } else { //existing candidate isn't a default, if the one we're looking at isn't a default either, then its an error diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHandlerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHandlerTest.java index 131cd96d371..989455bea5b 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHandlerTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHandlerTest.java @@ -26,6 +26,7 @@ import java.util.EnumSet; import javax.servlet.DispatcherType; +import org.eclipse.jetty.http.pathmap.MappedResource; import org.junit.Before; import org.junit.Test; @@ -47,6 +48,15 @@ public class ServletHandlerTest FilterMapping fm5 = new FilterMapping(); + ServletHolder sh1 = new ServletHolder(new Source (Source.Origin.DESCRIPTOR, "foo.xml")); + ServletMapping sm1 = new ServletMapping(); + + ServletHolder sh2 = new ServletHolder(new Source (Source.Origin.DESCRIPTOR, "foo.xml")); + ServletMapping sm2 = new ServletMapping(); + + ServletHolder sh3 = new ServletHolder(new Source (Source.Origin.DESCRIPTOR, "foo.xml")); + ServletMapping sm3 = new ServletMapping(); + @Before public void initMappings() @@ -70,6 +80,111 @@ public class ServletHandlerTest fh5.setName("fh5"); fm5.setPathSpec("/*"); fm5.setFilterHolder(fh5); + + sh1.setName("s1"); + sm1.setDefault(false); + sm1.setPathSpec("/foo/*"); + sm1.setServletName("s1"); + + sh2.setName("s2"); + sm2.setDefault(false); + sm2.setPathSpec("/foo/*"); + sm2.setServletName("s2"); + + sh3.setName("s3"); + sm3.setDefault(true); + sm3.setPathSpec("/foo/*"); + sm3.setServletName("s3"); + + + } + + @Test + public void testDuplicateMappingsForbidden() throws Exception + { + ServletHandler handler = new ServletHandler(); + handler.setAllowDuplicateMappings(false); + handler.addServlet(sh1); + handler.addServlet(sh2); + handler.updateNameMappings(); + + handler.addServletMapping(sm1); + handler.addServletMapping(sm2); + + try + { + handler.updateMappings(); + } + catch (IllegalStateException e) + { + //expected error + } + } + + + @Test + public void testDuplicateMappingsWithDefaults() throws Exception + { + ServletHandler handler = new ServletHandler(); + handler.setAllowDuplicateMappings(false); + handler.addServlet(sh1); + handler.addServlet(sh3); + handler.updateNameMappings(); + + handler.addServletMapping(sm3); + handler.addServletMapping(sm1); + + + handler.updateMappings(); + + MappedResource entry=handler.getHolderEntry("/foo/*"); + assertNotNull(entry); + assertEquals("s1", entry.getResource().getName()); + } + + + @Test + public void testDuplicateMappingsSameServlet() throws Exception + { + ServletHolder sh4 = new ServletHolder(); + + sh4.setName("s1"); + + ServletMapping sm4 = new ServletMapping(); + sm4.setPathSpec("/foo/*"); + sm4.setServletName("s1"); + + ServletHandler handler = new ServletHandler(); + handler.setAllowDuplicateMappings(true); + handler.addServlet(sh1); + handler.addServlet(sh4); + handler.updateNameMappings(); + + handler.addServletMapping(sm1); + handler.addServletMapping(sm4); + + + handler.updateMappings(); + } + + + + @Test + public void testDuplicateMappingsAllowed() throws Exception + { + ServletHandler handler = new ServletHandler(); + handler.setAllowDuplicateMappings(true); + handler.addServlet(sh1); + handler.addServlet(sh2); + handler.updateNameMappings(); + + handler.addServletMapping(sm1); + handler.addServletMapping(sm2); + handler.updateMappings(); + + MappedResource entry=handler.getHolderEntry("/foo/*"); + assertNotNull(entry); + assertEquals("s2", entry.getResource().getName()); } @Test