private void deploy(WebApp webApp) {

    Bundle bundle = webApp.getBundle();
    String contextName = webApp.getContextName();

    eventDispatcher.webEvent(
        new WebEvent(WebEvent.DEPLOYING, "/" + contextName, bundle, bundleContext.getBundle()));
    if (!contexts.containsKey(contextName)) {
      LinkedList<WebApp> queue = new LinkedList<WebApp>();
      contexts.put(contextName, queue);
      queue.add(webApp);

      // let the publisher set the deployment state and send web event
      m_publisher.publish(webApp, eventDispatcher, bundleContext);
    } else {
      LinkedList<WebApp> queue = contexts.get(contextName);
      queue.add(webApp);

      Collection<Long> duplicateIds = new LinkedList<Long>();
      for (WebApp duplicateWebApp : queue) {
        duplicateIds.add(duplicateWebApp.getBundle().getBundleId());
      }

      webApp.setDeploymentState(WebApp.WAITING_STATE);
      eventDispatcher.webEvent(
          new WebEvent(
              WebEvent.WAITING,
              "/" + contextName,
              bundle,
              bundleContext.getBundle(),
              duplicateIds));
    }
  }
  private void undeploy(WebApp webApp) {

    String contextName = webApp.getContextName();
    LinkedList<WebApp> queue = contexts.get(contextName);
    if (queue != null) {

      // Are we the published web app??
      if (queue.get(0) == webApp) {

        webApp.setDeploymentState(WebApp.UNDEPLOYED_STATE);
        eventDispatcher.webEvent(
            new WebEvent(
                WebEvent.UNDEPLOYING,
                "/" + contextName,
                webApp.getBundle(),
                bundleContext.getBundle()));
        m_publisher.unpublish(webApp);
        eventDispatcher.webEvent(
            new WebEvent(
                WebEvent.UNDEPLOYED,
                "/" + contextName,
                webApp.getBundle(),
                bundleContext.getBundle()));
        queue.removeFirst();

        // Below checks if another webapp is waiting for the context, if so the webapp is published.
        LOG.debug("Check for a waiting webapp.");
        if (!queue.isEmpty()) {
          LOG.debug("Found another bundle waiting for the context");
          WebApp next = queue.getFirst();

          eventDispatcher.webEvent(
              new WebEvent(
                  WebEvent.DEPLOYING,
                  "/" + contextName,
                  next.getBundle(),
                  bundleContext.getBundle()));

          // let the publisher set the deployment state and send web event
          m_publisher.publish(next, eventDispatcher, bundleContext);
        } else {
          contexts.remove(contextName);
        }

      } else if (queue.remove(webApp)) {
        webApp.setDeploymentState(WebApp.UNDEPLOYED_STATE);
        eventDispatcher.webEvent(
            new WebEvent(
                WebEvent.UNDEPLOYED,
                "/" + contextName,
                webApp.getBundle(),
                bundleContext.getBundle()));
      } else {
        LOG.debug("Web application was not in the deployment queue");
      }

    } else {
      LOG.debug(String.format("No web application published under context: %s", contextName));
    }
  }
  /**
   * Parse the web.xml and publish the corresponding web app. The received list is expected to
   * contain one URL of an web.xml (only first is used. The web.xml will be parsed and resulting web
   * application structure will be registered with the http service.
   *
   * @throws NullArgumentException if bundle or list of web xmls is null
   * @throws PreConditionException if the list of web xmls is empty or more then one xml
   * @see BundleObserver#addingEntries(Bundle,List)
   */
  public void addingEntries(final Bundle bundle, final List<URL> entries) {
    NullArgumentException.validateNotNull(bundle, "Bundle");
    NullArgumentException.validateNotNull(entries, "List of *.xml's");

    // Context name is also needed for some of the pre-condition checks, therefore it is retrieved
    // here.
    String contextName = extractContextName(bundle);
    LOG.info(String.format("Using [%s] as web application context name", contextName));

    List<String> virtualHostList = extractVirtualHostList(bundle);
    LOG.info(String.format("[%d] virtual hosts defined in bundle header", virtualHostList.size()));

    List<String> connectorList = extractConnectorList(bundle);
    LOG.info(String.format("[%d] connectors defined in bundle header", connectorList.size()));

    // try-catch only to inform framework and listeners of an event.
    try {
      //	        PreConditionException.validateLesserThan( entries.size(), 3, "Number of xml's" );
      PreConditionException.validateEqualTo(
          "WEB-INF".compareToIgnoreCase(Path.getDirectParent(entries.get(0))),
          0,
          "Direct parent of web.xml");
    } catch (PreConditionException pce) {
      LOG.error(pce.getMessage(), pce);
      eventDispatcher.webEvent(
          new WebEvent(WebEvent.FAILED, "/" + contextName, bundle, bundleContext.getBundle(), pce));
      throw pce;
    }

    if (webApps.containsKey(bundle.getBundleId())) {
      LOG.debug(
          String.format("Already found a web application in bundle %d", bundle.getBundleId()));
      return;
    }

    URL webXmlURL = null; // = entries.get( 0 );
    URL jettyWebXmlURL = null;

    for (URL url : entries) {
      if (isJettyWebXml(url)) {
        // it's the jetty-web.xml
        jettyWebXmlURL = url;
      } else if (isWebXml(url)) {
        // it's the web.xml
        webXmlURL = url;
      } else {
        // just another one
      }
    }
    if (webXmlURL == null) {
      PreConditionException pce =
          new PreConditionException("no web.xml configured in web-application");
      LOG.error(pce.getMessage(), pce);
      eventDispatcher.webEvent(
          new WebEvent(WebEvent.FAILED, "/" + contextName, bundle, bundleContext.getBundle(), pce));
      throw pce;
    }

    LOG.debug("Parsing a web application from [" + webXmlURL + "]");

    String rootPath = extractRootPath(bundle);
    LOG.info(String.format("Using [%s] as web application root path", rootPath));

    InputStream is = null;
    try {
      is = webXmlURL.openStream();
      final WebApp webApp = m_parser.parse(bundle, is);
      if (webApp != null) {
        LOG.debug("Parsed web app [" + webApp + "]");

        webApp.setWebXmlURL(webXmlURL);
        webApp.setJettyWebXmlURL(jettyWebXmlURL);
        webApp.setVirtualHostList(virtualHostList);
        webApp.setConnectorList(connectorList);
        webApp.setBundle(bundle);
        webApp.setContextName(contextName);
        webApp.setRootPath(rootPath);
        webApp.setDeploymentState(WebApp.UNDEPLOYED_STATE);

        webApps.put(bundle.getBundleId(), webApp);

        // The Webapp-Deploy header controls if the app is deployed on
        // startup.
        if ("true".equals(opt(getHeader(bundle, "Webapp-Deploy"), "true"))) {
          deploy(webApp);
        } else {
          eventDispatcher.webEvent(
              new WebEvent(
                  WebEvent.UNDEPLOYED,
                  "/" + webApp.getContextName(),
                  webApp.getBundle(),
                  bundleContext.getBundle()));
        }
      }
    } catch (Exception ignore) {
      LOG.error("Could not parse web.xml", ignore);
      eventDispatcher.webEvent(
          new WebEvent(
              WebEvent.FAILED, "/" + contextName, bundle, bundleContext.getBundle(), ignore));
    } finally {
      if (is != null) {
        try {
          is.close();
        } catch (IOException ignore) {
          // just ignore
        }
      }
    }
  }