/**
   * @param context
   * @param scis
   * @throws Exception
   */
  public void createServletContainerInitializerAnnotationHandlers(
      WebAppContext context, List<ServletContainerInitializer> scis) throws Exception {
    if (scis == null || scis.isEmpty()) return; // nothing to do

    List<ContainerInitializer> initializers = new ArrayList<ContainerInitializer>();
    context.setAttribute(CONTAINER_INITIALIZERS, initializers);

    for (ServletContainerInitializer service : scis) {
      HandlesTypes annotation = service.getClass().getAnnotation(HandlesTypes.class);
      ContainerInitializer initializer = null;
      if (annotation != null) {
        // There is a HandlesTypes annotation on the on the ServletContainerInitializer
        Class<?>[] classes = annotation.value();
        if (classes != null) {
          initializer = new ContainerInitializer(service, classes);

          // If we haven't already done so, we need to register a handler that will
          // process the whole class hierarchy to satisfy the ServletContainerInitializer
          if (context.getAttribute(CLASS_INHERITANCE_MAP) == null) {
            // MultiMap<String> map = new MultiMap<>();
            ConcurrentHashMap<String, ConcurrentHashSet<String>> map = new ClassInheritanceMap();
            context.setAttribute(CLASS_INHERITANCE_MAP, map);
            _classInheritanceHandler = new ClassInheritanceHandler(map);
          }

          for (Class<?> c : classes) {
            // The value of one of the HandlesTypes classes is actually an Annotation itself so
            // register a handler for it
            if (c.isAnnotation()) {
              if (LOG.isDebugEnabled())
                LOG.debug("Registering annotation handler for " + c.getName());
              _containerInitializerAnnotationHandlers.add(
                  new ContainerInitializerAnnotationHandler(initializer, c));
            }
          }
        } else {
          initializer = new ContainerInitializer(service, null);
          if (LOG.isDebugEnabled())
            LOG.debug("No classes in HandlesTypes on initializer " + service.getClass());
        }
      } else {
        initializer = new ContainerInitializer(service, null);
        if (LOG.isDebugEnabled()) LOG.debug("No annotation on initializer " + service.getClass());
      }

      initializers.add(initializer);
    }

    // add a bean to the context which will call the servletcontainerinitializers when appropriate
    ServletContainerInitializersStarter starter =
        (ServletContainerInitializersStarter) context.getAttribute(CONTAINER_INITIALIZER_STARTER);
    if (starter != null)
      throw new IllegalStateException("ServletContainerInitializersStarter already exists");
    starter = new ServletContainerInitializersStarter(context);
    context.setAttribute(CONTAINER_INITIALIZER_STARTER, starter);
    context.addBean(starter, true);
  }
  private ServletContainerInitializerInfo buildInitializerInfo(
      InstanceFactoryFactory factory, Class<? extends ServletContainerInitializer> clazz) {

    Set<Class<?>> types = new HashSet<>();

    HandlesTypes handlesTypes = clazz.getAnnotation(HandlesTypes.class);
    if (handlesTypes != null) {
      for (Class<?> type : handlesTypes.value()) {
        if (type.isAnnotation()) {
          types.addAll(annotationDatabase.getTypes((Class<? extends Annotation>) type));
        }
      }
    }

    InstanceFactory<? extends ServletContainerInitializer> instanceFactory =
        factory.getInstanceFactory(clazz);

    return new ServletContainerInitializerInfo(clazz, instanceFactory, types);
  }
  /** Process SCIs. */
  public void deploy(final DeploymentPhaseContext phaseContext)
      throws DeploymentUnitProcessingException {
    final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
    final ModuleSpecification moduleSpecification =
        deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION);
    final ServiceModuleLoader loader =
        deploymentUnit.getAttachment(Attachments.SERVICE_MODULE_LOADER);
    if (!DeploymentTypeMarker.isType(DeploymentType.WAR, deploymentUnit)) {
      return; // Skip non web deployments
    }
    WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
    assert warMetaData != null;
    final Module module = deploymentUnit.getAttachment(Attachments.MODULE);
    if (module == null) {
      throw MESSAGES.failedToResolveModule(deploymentUnit);
    }
    final ClassLoader classLoader = module.getClassLoader();
    ScisMetaData scisMetaData = deploymentUnit.getAttachment(ScisMetaData.ATTACHMENT_KEY);
    if (scisMetaData == null) {
      scisMetaData = new ScisMetaData();
      deploymentUnit.putAttachment(ScisMetaData.ATTACHMENT_KEY, scisMetaData);
    }
    Set<ServletContainerInitializer> scis = scisMetaData.getScis();
    if (scis == null) {
      scis = new HashSet<ServletContainerInitializer>();
      scisMetaData.setScis(scis);
    }
    Map<ServletContainerInitializer, Set<Class<?>>> handlesTypes = scisMetaData.getHandlesTypes();
    if (handlesTypes == null) {
      handlesTypes = new HashMap<ServletContainerInitializer, Set<Class<?>>>();
      scisMetaData.setHandlesTypes(handlesTypes);
    }
    // Find the SCIs from shared modules
    for (ModuleDependency dependency : moduleSpecification.getSystemDependencies()) {
      try {
        Module depModule = loader.loadModule(dependency.getIdentifier());
        ServiceLoader<ServletContainerInitializer> serviceLoader =
            depModule.loadService(ServletContainerInitializer.class);
        for (ServletContainerInitializer service : serviceLoader) {
          scis.add(service);
        }
      } catch (ModuleLoadException e) {
        if (dependency.isOptional() == false) {
          throw MESSAGES.errorLoadingSCIFromModule(dependency.getIdentifier(), e);
        }
      }
    }
    // Find local ServletContainerInitializer services
    List<String> order = warMetaData.getOrder();
    Map<String, VirtualFile> localScis = warMetaData.getScis();
    if (order != null && localScis != null) {
      for (String jar : order) {
        VirtualFile sci = localScis.get(jar);
        if (sci != null) {
          ServletContainerInitializer service = loadSci(classLoader, sci, jar, true);
          if (service != null) {
            scis.add(service);
          }
        }
      }
    }
    // Process HandlesTypes for ServletContainerInitializer
    Map<Class<?>, Set<ServletContainerInitializer>> typesMap =
        new HashMap<Class<?>, Set<ServletContainerInitializer>>();
    for (ServletContainerInitializer service : scis) {
      if (service.getClass().isAnnotationPresent(HandlesTypes.class)) {
        HandlesTypes handlesTypesAnnotation = service.getClass().getAnnotation(HandlesTypes.class);
        Class<?>[] typesArray = handlesTypesAnnotation.value();
        if (typesArray != null) {
          for (Class<?> type : typesArray) {
            Set<ServletContainerInitializer> servicesSet = typesMap.get(type);
            if (servicesSet == null) {
              servicesSet = new HashSet<ServletContainerInitializer>();
              typesMap.put(type, servicesSet);
            }
            servicesSet.add(service);
            handlesTypes.put(service, new HashSet<Class<?>>());
          }
        }
      }
    }
    Class<?>[] typesArray = typesMap.keySet().toArray(new Class<?>[0]);

    final CompositeIndex index =
        deploymentUnit.getAttachment(Attachments.COMPOSITE_ANNOTATION_INDEX);
    if (index == null) {
      throw MESSAGES.unableToResolveAnnotationIndex(deploymentUnit);
    }

    // Find classes which extend, implement, or are annotated by HandlesTypes
    for (Class<?> type : typesArray) {
      DotName className = DotName.createSimple(type.getName());
      Set<ClassInfo> classInfos = processHandlesType(className, type, index);
      Set<Class<?>> classes = loadClassInfoSet(classInfos, classLoader);
      Set<ServletContainerInitializer> sciSet = typesMap.get(type);
      for (ServletContainerInitializer sci : sciSet) {
        handlesTypes.get(sci).addAll(classes);
      }
    }
  }
  /** @see WebXmlParser#parse(InputStream) */
  public WebApp parse(final Bundle bundle, final InputStream inputStream) {
    final WebApp webApp = new WebApp(); // changed to final because of inner
    // class.
    try {
      final Element rootElement = getRootElement(inputStream);
      if (rootElement != null) {
        // webApp = new WebApp();
        // web-app attributes
        String version = getAttribute(rootElement, "version");
        Integer majorVersion = null;
        if (version != null && !version.isEmpty() && version.length() > 2) {
          LOG.debug("version found in web.xml - " + version);
          try {
            majorVersion = Integer.parseInt(version.split("\\.")[0]);
          } catch (NumberFormatException nfe) {
            // munch do nothing here stay with null therefore
            // annotation scanning is disabled.
          }
        } else if (version != null && !version.isEmpty() && version.length() > 0) {
          try {
            majorVersion = Integer.parseInt(version);
          } catch (NumberFormatException e) {
            // munch do nothing here stay with null....
          }
        }
        Boolean metaDataComplete =
            Boolean.parseBoolean(getAttribute(rootElement, "metadata-complete", "false"));
        webApp.setMetaDataComplete(metaDataComplete);
        LOG.debug("metadata-complete is: " + metaDataComplete);
        // web-app elements
        webApp.setDisplayName(getTextContent(getChild(rootElement, "display-name")));
        parseContextParams(rootElement, webApp);
        parseSessionConfig(rootElement, webApp);
        parseServlets(rootElement, webApp);
        parseFilters(rootElement, webApp);
        parseListeners(rootElement, webApp);
        parseErrorPages(rootElement, webApp);
        parseWelcomeFiles(rootElement, webApp);
        parseMimeMappings(rootElement, webApp);
        parseSecurity(rootElement, webApp);

        LOG.debug("scanninf for ServletContainerInitializers");

        ServiceLoader<ServletContainerInitializer> serviceLoader =
            ServiceLoader.load(
                ServletContainerInitializer.class, bundle.getClass().getClassLoader());
        if (serviceLoader != null) {
          LOG.debug("ServletContainerInitializers found");
          while (serviceLoader.iterator().hasNext()) {
            // for (ServletContainerInitializer service :
            // serviceLoader) {
            ServletContainerInitializer service = null;
            try {
              bundle.loadClass(ServletContainerInitializer.class.getName());
              Object obj = serviceLoader.iterator().next();
              if (obj instanceof ServletContainerInitializer)
                service = (ServletContainerInitializer) obj;
              else continue;
            } catch (ServiceConfigurationError e) {
              LOG.error("ServiceConfigurationError loading ServletContainerInitializer", e);
              continue;
            } catch (ClassNotFoundException e) {
              LOG.error("ServiceConfigurationError loading ServletContainerInitializer", e);
              continue;
            }
            WebAppServletContainerInitializer webAppServletContainerInitializer =
                new WebAppServletContainerInitializer();
            webAppServletContainerInitializer.setServletContainerInitializer(service);
            if (!webApp.getMetaDataComplete() && majorVersion != null && majorVersion >= 3) {
              HandlesTypes annotation = service.getClass().getAnnotation(HandlesTypes.class);
              Class[] classes;
              if (annotation != null) {
                // add annotated classes to service
                classes = annotation.value();
                webAppServletContainerInitializer.setClasses(classes);
              }
            }
            webApp.addServletContainerInitializer(webAppServletContainerInitializer);
          }
        }

        if (!webApp.getMetaDataComplete() && majorVersion != null && majorVersion >= 3) {
          LOG.debug("metadata-complete is either false or not set");

          LOG.debug("scanning for annotated classes");
          Enumeration<?> clazzes = bundle.findEntries("/", "*.class", true);

          for (; clazzes.hasMoreElements(); ) {
            URL clazzUrl = (URL) clazzes.nextElement();
            Class<?> clazz;
            String clazzFile = clazzUrl.getFile();
            LOG.debug("Class file found at :" + clazzFile);
            if (clazzFile.startsWith("/WEB-INF/classes"))
              clazzFile = clazzFile.replaceFirst("/WEB-INF/classes", "");
            else if (clazzFile.startsWith("/WEB-INF/lib"))
              clazzFile = clazzFile.replaceFirst("/WEB-INF/lib", "");
            String clazzName =
                clazzFile.replaceAll("/", ".").replaceAll(".class", "").replaceFirst(".", "");
            try {
              clazz = bundle.loadClass(clazzName);
            } catch (ClassNotFoundException e) {
              LOG.debug("Class {} not found", clazzName);
              continue;
            }
            if (clazz.isAnnotationPresent(WebServlet.class)) {
              LOG.debug("found WebServlet annotation on class: " + clazz);
              WebServletAnnotationScanner annonScanner =
                  new WebServletAnnotationScanner(bundle, clazz.getCanonicalName());
              annonScanner.scan(webApp);
            } else if (clazz.isAnnotationPresent(WebFilter.class)) {
              LOG.debug("found WebFilter annotation on class: " + clazz);
              WebFilterAnnotationScanner filterScanner =
                  new WebFilterAnnotationScanner(bundle, clazz.getCanonicalName());
              filterScanner.scan(webApp);
            } else if (clazz.isAnnotationPresent(WebListener.class)) {
              LOG.debug("found WebListener annotation on class: " + clazz);
              addWebListener(webApp, clazz.getSimpleName());
            }
          }
          LOG.debug("class scanning done");
        }

        // special handling for finding JSF Context listeners wrapped in
        // *.tld files
        Enumeration tldEntries = bundle.getResources("*.tld");
        // Enumeration tldEntries = bundle.findEntries("/", "*.tld",
        // true);
        while (tldEntries != null && tldEntries.hasMoreElements()) {
          URL url = (URL) tldEntries.nextElement();
          Element rootTld = getRootElement(url.openStream());
          if (rootTld != null) {
            parseListeners(rootTld, webApp);
          }
        }

      } else {
        LOG.warn("The parsed web.xml does not have a root element");
        return null;
      }
    } catch (ParserConfigurationException ignore) {
      LOG.error("Cannot parse web.xml", ignore);
    } catch (IOException ignore) {
      LOG.error("Cannot parse web.xml", ignore);
    } catch (SAXException ignore) {
      LOG.error("Cannot parse web.xml", ignore);
    }
    return webApp;
  }