docs: private ServletPathMatchesData setupServletChains() {
          //create the default servlet
          ServletHandler defaultServlet = null;
          final ManagedServlets servlets = deployment.getServlets();
          final ManagedFilters filters = deployment.getFilters();
  
          final Map<String, ServletHandler> extensionServlets = new HashMap<>();
          final Map<String, ServletHandler> pathServlets = new HashMap<>();
  
          final Set<String> pathMatches = new HashSet<>();
          final Set<String> extensionMatches = new HashSet<>();
  
          DeploymentInfo deploymentInfo = deployment.getDeploymentInfo();
  
          //loop through all filter mappings, and add them to the set of known paths
          for (FilterMappingInfo mapping : deploymentInfo.getFilterMappings()) {
              if (mapping.getMappingType() == FilterMappingInfo.MappingType.URL) {
                  String path = mapping.getMapping();
                  if (path.equals("*")) {
                      //UNDERTOW-95, support this non-standard filter mapping
                      path = "/*";
                  }
                  if (!path.startsWith("*.")) {
                      pathMatches.add(path);
                  } else {
                      extensionMatches.add(path.substring(2));
                  }
              }
          }
  
          //now loop through all servlets.
          for (Map.Entry<String, ServletHandler> entry : servlets.getServletHandlers().entrySet()) {
              final ServletHandler handler = entry.getValue();
              //add the servlet to the approprite path maps
              for (String path : handler.getManagedServlet().getServletInfo().getMappings()) {
                  if (path.equals("/")) {
                      //the default servlet
                      pathMatches.add("/*");
                      if (defaultServlet != null) {
                          throw UndertowServletMessages.MESSAGES.twoServletsWithSameMapping(path);
                      }
                      defaultServlet = handler;
                  } else if (!path.startsWith("*.")) {
                      //either an exact or a /* based path match
                      if (path.isEmpty()) {
                          path = "/";
                      }
                      pathMatches.add(path);
                      if (pathServlets.containsKey(path)) {
                          throw UndertowServletMessages.MESSAGES.twoServletsWithSameMapping(path);
                      }
                      pathServlets.put(path, handler);
                  } else {
                      //an extension match based servlet
                      String ext = path.substring(2);
                      extensionMatches.add(ext);
                      if(extensionServlets.containsKey(ext)) {
                          throw UndertowServletMessages.MESSAGES.twoServletsWithSameMapping(path);
                      }
                      extensionServlets.put(ext, handler);
                  }
              }
          }
          ServletHandler managedDefaultServlet = servlets.getServletHandler(DEFAULT_SERVLET_NAME);
          if(managedDefaultServlet == null) {
              //we always create a default servlet, even if it is not going to have any path mappings registered
              managedDefaultServlet = servlets.addServlet(new ServletInfo(DEFAULT_SERVLET_NAME, DefaultServlet.class));
          }
  
          if (defaultServlet == null) {
              //no explicit default servlet was specified, so we register our mapping
              pathMatches.add("/*");
              defaultServlet = managedDefaultServlet;
          }
  
          final ServletPathMatchesData.Builder builder = ServletPathMatchesData.builder();
  
          //we now loop over every path in the application, and build up the patches based on this path
          //these paths contain both /* and exact matches.
          for (final String path : pathMatches) {
              //resolve the target servlet, will return null if this is the default servlet
              MatchData targetServletMatch = resolveServletForPath(path, pathServlets, extensionServlets, defaultServlet);
  
              final Map<DispatcherType, List<ManagedFilter>> noExtension = new EnumMap<>(DispatcherType.class);
              final Map<String, Map<DispatcherType, List<ManagedFilter>>> extension = new HashMap<>();
              //initalize the extension map. This contains all the filers in the noExtension map, plus
              //any filters that match the extension key
              for (String ext : extensionMatches) {
                  extension.put(ext, new EnumMap<DispatcherType, List<ManagedFilter>>(DispatcherType.class));
              }
  
              //loop over all the filters, and add them to the appropriate map in the correct order
              for (final FilterMappingInfo filterMapping : deploymentInfo.getFilterMappings()) {
                  ManagedFilter filter = filters.getManagedFilter(filterMapping.getFilterName());
                  if (filterMapping.getMappingType() == FilterMappingInfo.MappingType.SERVLET) {
                      if (targetServletMatch.handler != null) {
                          if (filterMapping.getMapping().equals(targetServletMatch.handler.getManagedServlet().getServletInfo().getName()) || filterMapping.getMapping().equals("*")) {
                              addToListMap(noExtension, filterMapping.getDispatcher(), filter);
                          }
                      }
                      for (Map.Entry<String, Map<DispatcherType, List<ManagedFilter>>> entry : extension.entrySet()) {
                          ServletHandler pathServlet = targetServletMatch.handler;
                          boolean defaultServletMatch = targetServletMatch.defaultServlet;
                          if (defaultServletMatch && extensionServlets.containsKey(entry.getKey())) {
                              pathServlet = extensionServlets.get(entry.getKey());
                          }
  
                          if (filterMapping.getMapping().equals(pathServlet.getManagedServlet().getServletInfo().getName()) || filterMapping.getMapping().equals("*")) {
                              addToListMap(extension.get(entry.getKey()), filterMapping.getDispatcher(), filter);
                          }
                      }
                  } else {
                      if (filterMapping.getMapping().isEmpty() || !filterMapping.getMapping().startsWith("*.")) {
                          if (isFilterApplicable(path, filterMapping.getMapping())) {
                              addToListMap(noExtension, filterMapping.getDispatcher(), filter);
                              for (Map<DispatcherType, List<ManagedFilter>> l : extension.values()) {
                                  addToListMap(l, filterMapping.getDispatcher(), filter);
                              }
                          }
                      } else {
                          addToListMap(extension.get(filterMapping.getMapping().substring(2)), filterMapping.getDispatcher(), filter);
                      }
                  }
              }
              //resolve any matches and add them to the builder
              if (path.endsWith("/*")) {
                  String prefix = path.substring(0, path.length() - 2);
                  //add the default non-extension match
                  builder.addPrefixMatch(prefix, createHandler(deploymentInfo, targetServletMatch.handler, noExtension, targetServletMatch.matchedPath, targetServletMatch.defaultServlet, targetServletMatch.mappingMatch, targetServletMatch.userPath), targetServletMatch.defaultServlet || targetServletMatch.handler.getManagedServlet().getServletInfo().isRequireWelcomeFileMapping());
  
                  //build up the chain for each non-extension match
                  for (Map.Entry<String, Map<DispatcherType, List<ManagedFilter>>> entry : extension.entrySet()) {
                      ServletHandler pathServlet = targetServletMatch.handler;
                      String pathMatch = targetServletMatch.matchedPath;
  
                      boolean defaultServletMatch = targetServletMatch.defaultServlet;
                      if (defaultServletMatch && extensionServlets.containsKey(entry.getKey())) {
                          defaultServletMatch = false;
                          pathServlet = extensionServlets.get(entry.getKey());
                      }
                      HttpHandler handler = pathServlet;
                      if (!entry.getValue().isEmpty()) {
                          handler = new FilterHandler(entry.getValue(), deploymentInfo.isAllowNonStandardWrappers(), handler);
                      }
                      builder.addExtensionMatch(prefix, entry.getKey(), servletChain(handler, pathServlet.getManagedServlet(), entry.getValue(), pathMatch, deploymentInfo, defaultServletMatch, defaultServletMatch ? MappingMatch.DEFAULT : MappingMatch.EXTENSION, defaultServletMatch ? "/" : "*." + entry.getKey()));
                  }
              } else if (path.isEmpty()) {
                  //the context root match
                  builder.addExactMatch("/", createHandler(deploymentInfo, targetServletMatch.handler, noExtension, targetServletMatch.matchedPath, targetServletMatch.defaultServlet, targetServletMatch.mappingMatch, targetServletMatch.userPath));
              } else {
                  //we need to check for an extension match, so paths like /exact.txt will have the correct filter applied
                  int lastSegmentIndex = path.lastIndexOf('/');
                  String lastSegment;
                  if(lastSegmentIndex > 0) {
                      lastSegment = path.substring(lastSegmentIndex);
                  } else {
                      lastSegment = path;
                  }
                  if (lastSegment.contains(".")) {
                      String ext = lastSegment.substring(lastSegment.lastIndexOf('.') + 1);
                      if (extension.containsKey(ext)) {
                          Map<DispatcherType, List<ManagedFilter>> extMap = extension.get(ext);
                          builder.addExactMatch(path, createHandler(deploymentInfo, targetServletMatch.handler, extMap, targetServletMatch.matchedPath, targetServletMatch.defaultServlet, targetServletMatch.mappingMatch, targetServletMatch.userPath));
                      } else {
                          builder.addExactMatch(path, createHandler(deploymentInfo, targetServletMatch.handler, noExtension, targetServletMatch.matchedPath, targetServletMatch.defaultServlet, targetServletMatch.mappingMatch, targetServletMatch.userPath));
                      }
                  } else {
                      builder.addExactMatch(path, createHandler(deploymentInfo, targetServletMatch.handler, noExtension, targetServletMatch.matchedPath, targetServletMatch.defaultServlet, targetServletMatch.mappingMatch, targetServletMatch.userPath));
                  }
  
              }
          }
  
          //now setup name based mappings
          //these are used for name based dispatch
          for (Map.Entry<String, ServletHandler> entry : servlets.getServletHandlers().entrySet()) {
              final Map<DispatcherType, List<ManagedFilter>> filtersByDispatcher = new EnumMap<>(DispatcherType.class);
              for (final FilterMappingInfo filterMapping : deploymentInfo.getFilterMappings()) {
                  ManagedFilter filter = filters.getManagedFilter(filterMapping.getFilterName());
                  if (filterMapping.getMappingType() == FilterMappingInfo.MappingType.SERVLET) {
                      if (filterMapping.getMapping().equals(entry.getKey())) {
                          addToListMap(filtersByDispatcher, filterMapping.getDispatcher(), filter);
                      }
                  }
              }
              if (filtersByDispatcher.isEmpty()) {
                  builder.addNameMatch(entry.getKey(), servletChain(entry.getValue(), entry.getValue().getManagedServlet(), filtersByDispatcher, null, deploymentInfo, false, MappingMatch.EXACT, ""));
              } else {
                  builder.addNameMatch(entry.getKey(), servletChain(new FilterHandler(filtersByDispatcher, deploymentInfo.isAllowNonStandardWrappers(), entry.getValue()), entry.getValue().getManagedServlet(), filtersByDispatcher, null, deploymentInfo, false, MappingMatch.EXACT, ""));
              }
          }
  
          return builder.build();
      }