void invokeAtmosphereHandler(AtmosphereResourceImpl r) throws IOException {
    if (!r.isInScope()) return;

    AtmosphereRequest req = r.getRequest(false);
    String disableOnEvent =
        r.getAtmosphereConfig().getInitParameter(ApplicationConfig.DISABLE_ONSTATE_EVENT);

    try {
      if (disableOnEvent == null || !disableOnEvent.equals(String.valueOf(true))) {
        AtmosphereHandler atmosphereHandler =
            (AtmosphereHandler) req.getAttribute(FrameworkConfig.ATMOSPHERE_HANDLER);

        synchronized (r) {
          atmosphereHandler.onStateChange(r.getAtmosphereResourceEvent());

          Meteor m = (Meteor) req.getAttribute(AtmosphereResourceImpl.METEOR);
          if (m != null) {
            m.destroy();
          }
        }
        req.removeAttribute(FrameworkConfig.ATMOSPHERE_RESOURCE);
      }
    } catch (IOException ex) {
      try {
        r.onThrowable(ex);
      } catch (Throwable t) {
        logger.warn("failed calling onThrowable()", ex);
      }
    }
  }
  /**
   * Note: Filter does not delegate to chain, since it would lead into cycle by calling {@link
   * PushServlet#service(ServletRequest, ServletResponse)}.
   */
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
      HttpServletRequest httpReq = (HttpServletRequest) request;
      HttpServletResponse httpResp = (HttpServletResponse) response;

      if ("GET".equals(httpReq.getMethod())) {
        String pushSessionId = httpReq.getParameter(PUSH_SESSION_ID_PARAM);

        Session session = null;

        if (pushSessionId != null) {
          ensureServletContextAvailable(request);
          PushContext pushContext =
              (PushContext) servletContext.getAttribute(PushContext.INSTANCE_KEY_NAME);
          session = pushContext.getSessionManager().getPushSession(pushSessionId);
        }

        if (session == null) {
          if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(MessageFormat.format("Session {0} was not found", pushSessionId));
          }
          httpResp.sendError(HttpServletResponse.SC_BAD_REQUEST);
          return;
        }

        httpResp.setContentType("text/plain");

        Meteor meteor =
            Meteor.build(httpReq, SCOPE.REQUEST, Collections.<BroadcastFilter>emptyList(), null);

        try {
          Request pushRequest = new RequestImpl(meteor, session);

          httpReq.setAttribute(SESSION_ATTRIBUTE_NAME, session);
          httpReq.setAttribute(REQUEST_ATTRIBUTE_NAME, pushRequest);

          pushRequest.suspend();
        } catch (Exception e) {
          LOGGER.error(e.getMessage(), e);
        }

        return;
      }
    }
  }
  @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;
  }
 public static Meteor getMeteor(HttpServletRequest request) {
   return Meteor.build(request);
 }