public void visitContainerInitializer(
      WebAppContext context, ContainerInitializer containerInitializer) {
    if (containerInitializer == null) return;

    // add the ContainerInitializer to the list of container initializers
    List<ContainerInitializer> containerInitializers =
        (List<ContainerInitializer>)
            context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS);
    if (containerInitializers == null) {
      containerInitializers = new ArrayList<ContainerInitializer>();
      context.setAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS, containerInitializers);
    }

    containerInitializers.add(containerInitializer);

    // Ensure a bean is set up on the context that will invoke the ContainerInitializers as the
    // context starts
    ServletContainerInitializersStarter starter =
        (ServletContainerInitializersStarter)
            context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZER_STARTER);
    if (starter == null) {
      starter = new ServletContainerInitializersStarter(context);
      context.setAttribute(AnnotationConfiguration.CONTAINER_INITIALIZER_STARTER, starter);
      context.addBean(starter, true);
    }
  }
  /**
   * @see
   *     org.eclipse.jetty.webapp.AbstractConfiguration#postConfigure(org.eclipse.jetty.webapp.WebAppContext)
   */
  @Override
  public void postConfigure(WebAppContext context) throws Exception {
    ConcurrentHashMap<String, ConcurrentHashSet<String>> classMap =
        (ClassInheritanceMap) context.getAttribute(CLASS_INHERITANCE_MAP);
    List<ContainerInitializer> initializers =
        (List<ContainerInitializer>) context.getAttribute(CONTAINER_INITIALIZERS);

    context.removeAttribute(CLASS_INHERITANCE_MAP);
    if (classMap != null) classMap.clear();

    context.removeAttribute(CONTAINER_INITIALIZERS);
    if (initializers != null) initializers.clear();

    if (_discoverableAnnotationHandlers != null) _discoverableAnnotationHandlers.clear();

    _classInheritanceHandler = null;
    if (_containerInitializerAnnotationHandlers != null)
      _containerInitializerAnnotationHandlers.clear();

    if (_parserTasks != null) {
      _parserTasks.clear();
      _parserTasks = null;
    }

    super.postConfigure(context);
  }
  /**
   * @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);
  }
 @Override
 public void preConfigure(final WebAppContext context) throws Exception {
   _webAppClassNameResolver = new WebAppClassNameResolver(context);
   _containerClassNameResolver = new ContainerClassNameResolver(context);
   String tmp = (String) context.getAttribute(SERVLET_CONTAINER_INITIALIZER_EXCLUSION_PATTERN);
   _sciExcludePattern = (tmp == null ? null : Pattern.compile(tmp));
 }
  /**
   * Jetty-specific extension that allows an ordering to be applied across ALL
   * ServletContainerInitializers.
   *
   * @return
   */
  public ServletContainerInitializerOrdering getInitializerOrdering(WebAppContext context) {
    if (context == null) return null;

    String tmp = (String) context.getAttribute(SERVLET_CONTAINER_INITIALIZER_ORDER);
    if (tmp == null || "".equals(tmp.trim())) return null;

    return new ServletContainerInitializerOrdering(tmp);
  }
  /**
   * Here is the order in which jars and osgi artifacts are scanned for discoverable annotations.
   *
   * <ol>
   *   <li>The container jars are scanned.
   *   <li>The WEB-INF/classes are scanned
   *   <li>The osgi fragment to the web bundle are parsed.
   *   <li>The WEB-INF/lib are scanned
   *   <li>The required bundles are parsed
   * </ol>
   */
  @Override
  public void parseWebInfLib(
      WebAppContext context, org.eclipse.jetty.annotations.AnnotationParser parser)
      throws Exception {
    AnnotationParser oparser = (AnnotationParser) parser;

    Bundle webbundle = (Bundle) context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
    Set<Bundle> fragAndRequiredBundles =
        (Set<Bundle>) context.getAttribute(OSGiWebInfConfiguration.FRAGMENT_AND_REQUIRED_BUNDLES);
    if (fragAndRequiredBundles != null) {
      // index and scan fragments
      for (Bundle bundle : fragAndRequiredBundles) {
        // skip bundles that have been uninstalled since we discovered them
        if (bundle.getState() == Bundle.UNINSTALLED) continue;

        Resource bundleRes = oparser.indexBundle(bundle);
        if (!context.getMetaData().getWebInfJars().contains(bundleRes)) {
          context.getMetaData().addWebInfJar(bundleRes);
        }

        if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null) {
          // a fragment indeed:
          parseFragmentBundle(context, oparser, webbundle, bundle);
        }
      }
    }
    // scan ourselves
    oparser.indexBundle(webbundle);
    parseWebBundle(context, oparser, webbundle);

    // scan the WEB-INF/lib
    super.parseWebInfLib(context, parser);
    if (fragAndRequiredBundles != null) {
      // scan the required bundles
      for (Bundle requiredBundle : fragAndRequiredBundles) {
        // skip bundles that have been uninstalled since we discovered them
        if (requiredBundle.getState() == Bundle.UNINSTALLED) continue;

        if (requiredBundle.getHeaders().get(Constants.FRAGMENT_HOST) == null) {
          // a bundle indeed:
          parseRequiredBundle(context, oparser, webbundle, requiredBundle);
        }
      }
    }
  }
 @Override
 public void deconfigure(WebAppContext context) throws Exception {
   context.removeAttribute(CLASS_INHERITANCE_MAP);
   context.removeAttribute(CONTAINER_INITIALIZERS);
   ServletContainerInitializersStarter starter =
       (ServletContainerInitializersStarter) context.getAttribute(CONTAINER_INITIALIZER_STARTER);
   if (starter != null) {
     context.removeBean(starter);
     context.removeAttribute(CONTAINER_INITIALIZER_STARTER);
   }
 }
 /**
  * Overriden method which, contrary to the original implementation in the parent class, add
  * directly the web-fragment.xml resource to the MetaData, instead of re-creating it with a forced
  * jar prefix.
  */
 @Override
 @SuppressWarnings("unchecked")
 public void findWebFragments(final WebAppContext context, final MetaData metaData)
     throws Exception {
   List<Resource> frags = (List<Resource>) context.getAttribute(FRAGMENT_RESOURCES);
   if (frags != null) {
     for (Resource frag : frags) {
       Resource parentResource = Util.chop(frag.getURL(), "/META-INF/web-fragment.xml");
       metaData.addFragment(parentResource, frag);
     }
   }
 }
  /**
   * @see
   *     org.eclipse.jetty.webapp.AbstractConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
   */
  @Override
  public void configure(WebAppContext context) throws Exception {
    context.addDecorator(new AnnotationDecorator(context));

    // Even if metadata is complete, we still need to scan for ServletContainerInitializers - if
    // there are any

    if (!context.getMetaData().isMetaDataComplete()) {
      // If metadata isn't complete, if this is a servlet 3 webapp or isConfigDiscovered is true, we
      // need to search for annotations
      if (context.getServletContext().getEffectiveMajorVersion() >= 3
          || context.isConfigurationDiscovered()) {
        _discoverableAnnotationHandlers.add(new WebServletAnnotationHandler(context));
        _discoverableAnnotationHandlers.add(new WebFilterAnnotationHandler(context));
        _discoverableAnnotationHandlers.add(new WebListenerAnnotationHandler(context));
      }
    }

    // Regardless of metadata, if there are any ServletContainerInitializers with @HandlesTypes,
    // then we need to scan all the
    // classes so we can call their onStartup() methods correctly
    createServletContainerInitializerAnnotationHandlers(
        context, getNonExcludedInitializers(context));

    if (!_discoverableAnnotationHandlers.isEmpty()
        || _classInheritanceHandler != null
        || !_containerInitializerAnnotationHandlers.isEmpty()) scanForAnnotations(context);

    // Resolve container initializers
    List<ContainerInitializer> initializers =
        (List<ContainerInitializer>)
            context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS);
    if (initializers != null && initializers.size() > 0) {
      Map<String, Set<String>> map =
          (Map<String, Set<String>>)
              context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP);
      if (map == null) LOG.warn("ServletContainerInitializers: detected. Class hierarchy: empty");
      for (ContainerInitializer i : initializers) i.resolveClasses(context, map);
    }
  }
 /**
  * Work out how long we should wait for the async scanning to occur.
  *
  * @param context
  * @return
  */
 protected int getMaxScanWait(WebAppContext context) {
   // try context attribute to get max time in sec to wait for scan completion
   Object o = context.getAttribute(MAX_SCAN_WAIT);
   if (o != null && o instanceof Number) {
     return ((Number) o).intValue();
   }
   // try server attribute to get max time in sec to wait for scan completion
   o = context.getServer().getAttribute(MAX_SCAN_WAIT);
   if (o != null && o instanceof Number) {
     return ((Number) o).intValue();
   }
   // try system property to get max time in sec to wait for scan completion
   return Integer.getInteger(MAX_SCAN_WAIT, DEFAULT_MAX_SCAN_WAIT).intValue();
 }
 public void visitMetaInfResource(WebAppContext context, Resource dir) {
   Collection<Resource> metaInfResources =
       (Collection<Resource>) context.getAttribute(MetaInfConfiguration.METAINF_RESOURCES);
   if (metaInfResources == null) {
     metaInfResources = new HashSet<Resource>();
     context.setAttribute(MetaInfConfiguration.METAINF_RESOURCES, metaInfResources);
   }
   metaInfResources.add(dir);
   // also add to base resource of webapp
   Resource[] collection = new Resource[metaInfResources.size() + 1];
   int i = 0;
   collection[i++] = context.getBaseResource();
   for (Resource resource : metaInfResources) collection[i++] = resource;
   context.setBaseResource(new ResourceCollection(collection));
 }
 /**
  * Check if we should use multiple threads to scan for annotations or not
  *
  * @param context
  * @return
  */
 protected boolean isUseMultiThreading(WebAppContext context) {
   // try context attribute to see if we should use multithreading
   Object o = context.getAttribute(MULTI_THREADED);
   if (o instanceof Boolean) {
     return ((Boolean) o).booleanValue();
   }
   // try server attribute to see if we should use multithreading
   o = context.getServer().getAttribute(MULTI_THREADED);
   if (o instanceof Boolean) {
     return ((Boolean) o).booleanValue();
   }
   // try system property to see if we should use multithreading
   return Boolean.valueOf(
       System.getProperty(MULTI_THREADED, Boolean.toString(DEFAULT_MULTI_THREADED)));
 }
  public void doHandle(Class clazz) {
    // Check that the PreDestroy is on a class that we're interested in
    if (Util.supportsPostConstructPreDestroy(clazz)) {
      Method[] methods = clazz.getDeclaredMethods();
      for (int i = 0; i < methods.length; i++) {
        Method m = (Method) methods[i];
        if (m.isAnnotationPresent(PreDestroy.class)) {
          if (m.getParameterTypes().length != 0)
            throw new IllegalStateException(m + " has parameters");
          if (m.getReturnType() != Void.TYPE) throw new IllegalStateException(m + " is not void");
          if (m.getExceptionTypes().length != 0)
            throw new IllegalStateException(m + " throws checked exceptions");
          if (Modifier.isStatic(m.getModifiers()))
            throw new IllegalStateException(m + " is static");

          // ServletSpec 3.0 p80 If web.xml declares even one predestroy then all predestroys
          // in fragments must be ignored. Otherwise, they are additive.
          MetaData metaData = _context.getMetaData();
          Origin origin = metaData.getOrigin("pre-destroy");
          if (origin != null
              && (origin == Origin.WebXml
                  || origin == Origin.WebDefaults
                  || origin == Origin.WebOverride)) return;

          PreDestroyCallback callback = new PreDestroyCallback();
          callback.setTarget(clazz.getName(), m.getName());

          LifeCycleCallbackCollection lifecycles =
              (LifeCycleCallbackCollection)
                  _context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
          if (lifecycles == null) {
            lifecycles = new LifeCycleCallbackCollection();
            _context.setAttribute(
                LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, lifecycles);
          }

          lifecycles.add(callback);
        }
      }
    }
  }
  /**
   * @param context
   * @param descriptor
   * @param node
   */
  public void visitContextParam(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
      throws Exception {
    String name = node.getString("param-name", false, true);
    String value = node.getString("param-value", false, true);
    List<String> values = new ArrayList<>();

    // extract values
    switch (name) {
      case ServletContext.ORDERED_LIBS:
      case AnnotationConfiguration.CONTAINER_INITIALIZERS:
      case MetaInfConfiguration.METAINF_TLDS:
      case MetaInfConfiguration.METAINF_RESOURCES:
        context.removeAttribute(name);

        QuotedStringTokenizer tok = new QuotedStringTokenizer(value, ",");
        while (tok.hasMoreElements()) values.add(tok.nextToken().trim());

        break;

      default:
        values.add(value);
    }

    // handle values
    switch (name) {
      case ServletContext.ORDERED_LIBS:
        {
          List<Object> libs = new ArrayList<>();
          Object o = context.getAttribute(ServletContext.ORDERED_LIBS);
          if (o instanceof Collection<?>) libs.addAll((Collection<?>) o);
          libs.addAll(values);
          if (libs.size() > 0) context.setAttribute(ServletContext.ORDERED_LIBS, libs);

          break;
        }

      case AnnotationConfiguration.CONTAINER_INITIALIZERS:
        {
          for (String i : values)
            visitContainerInitializer(
                context,
                new ContainerInitializer(Thread.currentThread().getContextClassLoader(), i));
          break;
        }

      case MetaInfConfiguration.METAINF_TLDS:
        {
          List<Object> tlds = new ArrayList<>();
          String war = context.getBaseResource().getURI().toString();
          Object o = context.getAttribute(MetaInfConfiguration.METAINF_TLDS);
          if (o instanceof Collection<?>) tlds.addAll((Collection<?>) o);
          for (String i : values) {
            Resource r = Resource.newResource(i.replace("${WAR}/", war));
            if (r.exists()) tlds.add(r.getURL());
            else throw new IllegalArgumentException("TLD not found: " + r);
          }

          if (tlds.size() > 0) context.setAttribute(MetaInfConfiguration.METAINF_TLDS, tlds);
          break;
        }

      case MetaInfConfiguration.METAINF_RESOURCES:
        {
          String war = context.getBaseResource().getURI().toString();
          for (String i : values) {
            Resource r = Resource.newResource(i.replace("${WAR}/", war));
            if (r.exists()) visitMetaInfResource(context, r);
            else throw new IllegalArgumentException("Resource not found: " + r);
          }
          break;
        }

      default:
    }
  }