/** {@inheritDoc} */
  public synchronized void destroy() {

    String s = config.getInitParameter(ApplicationConfig.SHARED);
    if (s != null && s.equalsIgnoreCase("true")) {
      logger.warn(
          "Factory shared, will not be destroyed. That can possibly cause memory leaks if"
              + "Broadcaster where created. Make sure you destroy them manually.");
      return;
    }

    Enumeration<Broadcaster> e = store.elements();
    Broadcaster b;
    // We just need one when shared.
    BroadcasterConfig bc = null;
    while (e.hasMoreElements()) {
      try {
        b = e.nextElement();
        b.resumeAll();
        b.destroy();
        bc = b.getBroadcasterConfig();
      } catch (Throwable t) {
        // Shield us from any bad behaviour
        logger.trace("Destroy", t);
      }
    }

    try {
      if (bc != null) bc.forceDestroy();
    } catch (Throwable t) {
      logger.trace("Destroy", t);
    }

    store.clear();
    factory = null;
  }
  @Override
  public AtmosphereResource resume() {

    if (!isSuspended()) {
      logger.warn("AtmosphereResource {} not suspend, cannot resume it.", uuid());
      return this;
    }

    try {
      if (!isResumed.getAndSet(true) && isInScope.get()) {
        logger.trace("AtmosphereResource {} is resuming", uuid());

        action.type(Action.TYPE.RESUME);

        // We need it as Jetty doesn't support timeout
        Broadcaster b = getBroadcaster(false);
        if (!b.isDestroyed() && b instanceof DefaultBroadcaster) {
          ((DefaultBroadcaster) b).broadcastOnResume(this);
        }

        notifyListeners();

        try {
          if (!b.isDestroyed()) {
            broadcaster.removeAtmosphereResource(this);
          }
        } catch (IllegalStateException ex) {
          logger.warn("Unable to resume", this);
          logger.debug(ex.getMessage(), ex);
        }

        if (b.getScope() == Broadcaster.SCOPE.REQUEST) {
          logger.debug("Broadcaster's scope is set to request, destroying it {}", b.getID());
          b.destroy();
        }

        // Resuming here means we need to pull away from all other Broadcaster, if they exists.
        if (config.getBroadcasterFactory() != null) {
          config.getBroadcasterFactory().removeAllAtmosphereResource(this);
        }

        try {
          req.setAttribute(ApplicationConfig.RESUMED_ON_TIMEOUT, Boolean.FALSE);
        } catch (Exception ex) {
          logger.debug("Resume exception: Cannot resume an already resumed/cancelled request", ex);
        } finally {
          try {
            Meteor m = (Meteor) req.getAttribute(METEOR);
            if (m != null) {
              m.destroy();
            }
          } catch (Exception ex) {
            logger.debug(
                "Meteor resume exception: Cannot resume an already resumed/cancelled request", ex);
          }
        }

        if (req.getAttribute(PRE_SUSPEND) == null) {
          asyncSupport.action(this);
        }
      } else {
        logger.trace("Already resumed {}", this);
        return this;
      }
    } catch (Throwable t) {
      logger.trace("Wasn't able to resume a connection {}", this, t);
    }
    listeners.clear();
    return this;
  }
  /** {@inheritDoc} */
  @Override
  public synchronized AtmosphereResource resume() {
    // We need to synchronize the method because the resume may occurs at the same time a message is
    // published
    // and we will miss that message. The DefaultBroadcaster synchronize on that method before
    // writing a message.
    try {
      if (!isResumed && isInScope) {
        action.type(Action.TYPE.RESUME);
        isResumed = true;

        // We need it as Jetty doesn't support timeout
        Broadcaster b = getBroadcaster(false);
        if (!b.isDestroyed() && b instanceof DefaultBroadcaster) {
          ((DefaultBroadcaster) b).broadcastOnResume(this);
        }

        notifyListeners();

        try {
          if (!b.isDestroyed()) {
            broadcaster.removeAtmosphereResource(this);
          }
        } catch (IllegalStateException ex) {
          logger.warn("Unable to resume", this);
          logger.debug(ex.getMessage(), ex);
        }

        if (b.getScope() == Broadcaster.SCOPE.REQUEST) {
          logger.debug("Broadcaster's scope is set to request, destroying it {}", b.getID());
          b.destroy();
        }

        // Resuming here means we need to pull away from all other Broadcaster, if they exists.
        if (config.getBroadcasterFactory() != null) {
          config.getBroadcasterFactory().removeAllAtmosphereResource(this);
        }

        try {
          req.setAttribute(ApplicationConfig.RESUMED_ON_TIMEOUT, Boolean.FALSE);
        } catch (Exception ex) {
          logger.debug("Resume exception: Cannot resume an already resumed/cancelled request", ex);
        } finally {
          try {
            Meteor m = (Meteor) req.getAttribute(METEOR);
            if (m != null) {
              m.destroy();
            }
          } catch (Exception ex) {
            logger.debug(
                "Meteor resume exception: Cannot resume an already resumed/cancelled request", ex);
          }
        }

        if (req.getAttribute(PRE_SUSPEND) == null) {
          asyncSupport.action(this);
        }
      } else {
        logger.debug("Cannot resume an already resumed/cancelled request {}", this);
      }
    } catch (Throwable t) {
      logger.trace("Wasn't able to resume a connection {}", this, t);
    }
    notifyListeners(new AtmosphereResourceEventImpl(this, true, false));
    listeners.clear();
    return this;
  }