Index: java/org/apache/catalina/startup/WebappServiceLoader.java
===================================================================
--- java/org/apache/catalina/startup/WebappServiceLoader.java	(revision 1524878)
+++ java/org/apache/catalina/startup/WebappServiceLoader.java	(working copy)
@@ -17,7 +17,6 @@
 package org.apache.catalina.startup;
 
 import java.io.BufferedReader;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -26,8 +25,10 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
 import javax.servlet.ServletContext;
 
@@ -39,9 +40,9 @@
  * by Servlet 8.2.4. This implementation does not attempt lazy loading as the
  * container is required to introspect all implementations discovered.
  * <p/>
- * If the ServletContext defines ORDERED_LIBS, then only JARs in WEB-INF/lib
- * that are named in that set will be included in the search for
- * provider configuration files; if ORDERED_LIBS is not defined then
+ * If the ServletContext defines ORDERED_LIBS, then any service configuration
+ * files found in JARs from  WEB-INF/lib that are not named in that set will
+ * be excluded; if ORDERED_LIBS is not defined then
  * all JARs will be searched for provider configuration files. Providers
  * defined by resources in the parent ClassLoader will always be returned.
  * <p/>
@@ -73,71 +74,52 @@
      * @throws IOException if there was a problem loading any service
      */
     public List<T> load(Class<T> serviceType) throws IOException {
-        String configFile = SERVICES + serviceType.getName();
+        String configFilename = SERVICES + serviceType.getName();
 
-        LinkedHashSet<String> applicationServicesFound = new LinkedHashSet<>();
-        LinkedHashSet<String> containerServicesFound = new LinkedHashSet<>();
-
-        ClassLoader loader = context.getClassLoader();
-
-        // if the ServletContext has ORDERED_LIBS, then use that to specify the
-        // set of JARs from WEB-INF/lib that should be used for loading services
+        Set<URL> excludedFiles;
         @SuppressWarnings("unchecked")
-        List<String> orderedLibs =
-                (List<String>) context.getAttribute(ServletContext.ORDERED_LIBS);
+        List<String> orderedLibs = (List<String>) context.getAttribute(ServletContext.ORDERED_LIBS);
         if (orderedLibs != null) {
-            // handle ordered libs directly, ...
-            for (String lib : orderedLibs) {
-                URL jarUrl = context.getResource(LIB + lib);
-                if (jarUrl == null) {
-                    // should not happen, just ignore
-                    continue;
+            // we have absolute ordering so exclude webapp jars not in orderedLibs
+            excludedFiles = new HashSet<>();
+            for (String path : context.getResourcePaths(LIB)) {
+                if (!orderedLibs.contains(path.substring(LIB.length()))) {
+                    // this assumes the URLs returned by ClassLoader.getResources() are the same
+                    // as the URLs returned by ServletContext.getResource() and that resources in
+                    // JAR files in WEB-INF/lib are returned as jar: URLs
+                    String exclude = context.getResource(path).toExternalForm();
+                    URL url;
+                    if (exclude.endsWith("/")) {
+                        url = new URL(exclude + configFilename);
+                    } else {
+                        url = new URL("jar:" + exclude + "!/" + configFilename);
+                    }
+                    excludedFiles.add(url);
                 }
-
-                String base = jarUrl.toExternalForm();
-                URL url;
-                if (base.endsWith("/")) {
-                    url = new URL(base + configFile);
-                } else {
-                    url = new URL("jar:" + base + "!/" + configFile);
-                }
-                try {
-                    parseConfigFile(applicationServicesFound, url);
-                } catch (FileNotFoundException e) {
-                    // no provider file found, this is OK
-                }
             }
-
-            // and the parent ClassLoader for all others
-            loader = loader.getParent();
+        } else {
+            excludedFiles = Collections.emptySet();
         }
 
-        Enumeration<URL> resources;
-        if (loader == null) {
-            resources = ClassLoader.getSystemResources(configFile);
-        } else {
-            resources = loader.getResources(configFile);
-        }
+        Set<String> servicesFound = new LinkedHashSet<>();
+        Enumeration<URL> resources = context.getClassLoader().getResources(configFilename);
         while (resources.hasMoreElements()) {
-            parseConfigFile(containerServicesFound, resources.nextElement());
+            URL configFile = resources.nextElement();
+            if (!excludedFiles.contains(configFile)) {
+                parseConfigFile(servicesFound, configFile);
+            }
         }
 
-        // Add the application services after the container services to ensure
-        // that the container services are loaded first
-        containerServicesFound.addAll(applicationServicesFound);
-
         // load the discovered services
-        if (containerServicesFound.isEmpty()) {
+        if (servicesFound.isEmpty()) {
             return Collections.emptyList();
         }
-        return loadServices(serviceType, containerServicesFound);
+        return loadServices(serviceType, servicesFound);
     }
 
-    void parseConfigFile(LinkedHashSet<String> servicesFound, URL url)
-            throws IOException {
+    void parseConfigFile(Set<String> servicesFound, URL url) throws IOException {
         try (InputStream is = url.openStream()) {
-            InputStreamReader in =
-                    new InputStreamReader(is, StandardCharsets.UTF_8);
+            InputStreamReader in = new InputStreamReader(is, StandardCharsets.UTF_8);
             BufferedReader reader = new BufferedReader(in);
             String line;
             while ((line = reader.readLine()) != null) {
@@ -154,8 +136,7 @@
         }
     }
 
-    List<T> loadServices(Class<T> serviceType, LinkedHashSet<String> servicesFound)
-            throws IOException {
+    List<T> loadServices(Class<T> serviceType, Set<String> servicesFound) throws IOException {
         ClassLoader loader = context.getClassLoader();
         List<T> services = new ArrayList<>(servicesFound.size());
         for (String serviceClass : servicesFound) {
Index: test/org/apache/catalina/startup/TestWebappServiceLoader.java
===================================================================
--- test/org/apache/catalina/startup/TestWebappServiceLoader.java	(revision 1524878)
+++ test/org/apache/catalina/startup/TestWebappServiceLoader.java	(working copy)
@@ -21,8 +21,9 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.LinkedHashSet;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import javax.servlet.ServletContainerInitializer;
 import javax.servlet.ServletContext;
@@ -39,14 +40,13 @@
             "META-INF/services/javax.servlet.ServletContainerInitializer";
     private IMocksControl control;
     private ClassLoader cl;
-    private ClassLoader parent;
     private ServletContext context;
     private WebappServiceLoader<ServletContainerInitializer> loader;
 
     @Before
     public void init() {
         control = EasyMock.createStrictControl();
-        parent = control.createMock(ClassLoader.class);
+        ClassLoader parent = control.createMock(ClassLoader.class);
         cl = EasyMock.createMockBuilder(ClassLoader.class)
                 .withConstructor(parent)
                 .addMockedMethod("loadClass", String.class)
@@ -72,13 +72,13 @@
     public void testInitializerFromClasspath() throws IOException {
         URL url = new URL("file://test");
         loader = EasyMock.createMockBuilder(WebappServiceLoader.class)
-                .addMockedMethod("parseConfigFile", LinkedHashSet.class, URL.class)
+                .addMockedMethod("parseConfigFile", Set.class, URL.class)
                 .withConstructor(context).createMock(control);
         EasyMock.expect(context.getAttribute(ServletContext.ORDERED_LIBS))
                 .andReturn(null);
         EasyMock.expect(cl.getResources(CONFIG_FILE))
                 .andReturn(Collections.enumeration(Collections.singleton(url)));
-        loader.parseConfigFile(EasyMock.isA(LinkedHashSet.class), EasyMock.same(url));
+        loader.parseConfigFile(EasyMock.isA(Set.class), EasyMock.same(url));
         control.replay();
         Assert.assertTrue(loader.load(ServletContainerInitializer.class).isEmpty());
         control.verify();
@@ -87,24 +87,29 @@
     @Test
     @SuppressWarnings("unchecked")
     public void testWithOrdering() throws IOException {
-        URL url1 = new URL("file://jar1.jar");
         URL sci1 = new URL("jar:file://jar1.jar!/" + CONFIG_FILE);
-        URL url2 = new URL("file://dir/");
         URL sci2 = new URL("file://dir/" + CONFIG_FILE);
+        URL urlX = new URL("file://excluded.jar");
+        URL sciX = new URL("jar:file://excluded.jar!/" + CONFIG_FILE);
         loader = EasyMock.createMockBuilder(WebappServiceLoader.class)
-                .addMockedMethod("parseConfigFile", LinkedHashSet.class, URL.class)
+                .addMockedMethod("parseConfigFile", Set.class, URL.class)
                 .withConstructor(context).createMock(control);
-        List<String> jars = Arrays.asList("jar1.jar", "dir/");
+        List<String> orderedLibs = Arrays.asList("jar1.jar", "dir/");
+        Set<String> allLibs = new HashSet<>();
+        allLibs.add("/WEB-INF/lib/jar1.jar");
+        allLibs.add("/WEB-INF/lib/excluded.jar");
+        allLibs.add("/WEB-INF/lib/dir/");
+        List<URL> scis = Arrays.asList(sci1, sciX, sci2);
         EasyMock.expect(context.getAttribute(ServletContext.ORDERED_LIBS))
-                .andReturn(jars);
-        EasyMock.expect(context.getResource("/WEB-INF/lib/jar1.jar"))
-                .andReturn(url1);
-        loader.parseConfigFile(EasyMock.isA(LinkedHashSet.class), EasyMock.eq(sci1));
-        EasyMock.expect(context.getResource("/WEB-INF/lib/dir/"))
-                .andReturn(url2);
-        loader.parseConfigFile(EasyMock.isA(LinkedHashSet.class), EasyMock.eq(sci2));
-        EasyMock.expect(parent.getResources(CONFIG_FILE))
-                .andReturn(Collections.<URL>emptyEnumeration());
+                .andReturn(orderedLibs);
+        EasyMock.expect(context.getResourcePaths("/WEB-INF/lib/"))
+                .andReturn(allLibs);
+        EasyMock.expect(context.getResource("/WEB-INF/lib/excluded.jar"))
+                .andReturn(urlX);
+        EasyMock.expect(cl.getResources(CONFIG_FILE))
+                .andReturn(Collections.enumeration(scis));
+        loader.parseConfigFile(EasyMock.isA(Set.class), EasyMock.eq(sci1));
+        loader.parseConfigFile(EasyMock.isA(Set.class), EasyMock.eq(sci2));
 
         control.replay();
         Assert.assertTrue(loader.load(ServletContainerInitializer.class).isEmpty());
@@ -113,7 +118,7 @@
 
     @Test
     public void testParseConfigFile() throws IOException {
-        LinkedHashSet<String> found = new LinkedHashSet<>();
+        Set<String> found = new HashSet<>();
         loader = new WebappServiceLoader<>(context);
         loader.parseConfigFile(found, getClass().getResource("service-config.txt"));
         Assert.assertEquals(Collections.singleton("provider1"), found);
@@ -126,8 +131,7 @@
         cl.loadClass(sci.getName());
         EasyMock.expectLastCall()
                 .andReturn(sci);
-        LinkedHashSet<String> names = new LinkedHashSet<>();
-        names.add(sci.getName());
+        Set<String> names = Collections.singleton(sci.getName());
         control.replay();
         Collection<ServletContainerInitializer> initializers =
                 loader.loadServices(ServletContainerInitializer.class, names);
@@ -143,8 +147,7 @@
         cl.loadClass(sci.getName());
         EasyMock.expectLastCall()
                 .andReturn(sci);
-        LinkedHashSet<String> names = new LinkedHashSet<>();
-        names.add(sci.getName());
+        Set<String> names = Collections.singleton(sci.getName());
         control.replay();
         try {
             loader.loadServices(ServletContainerInitializer.class, names);
@@ -162,8 +165,7 @@
         cl.loadClass(sci.getName());
         EasyMock.expectLastCall()
                 .andReturn(sci);
-        LinkedHashSet<String> names = new LinkedHashSet<>();
-        names.add(sci.getName());
+        Set<String> names = Collections.singleton(sci.getName());
         control.replay();
         try {
             loader.loadServices(ServletContainerInitializer.class, names);
