protected Broadcaster getBroadcaster(boolean autoCreate) {
    if (broadcaster == null) {
      throw new IllegalStateException("No Broadcaster associated with this AtmosphereResource.");
    }

    String s = config.getInitParameter(ApplicationConfig.RECOVER_DEAD_BROADCASTER);
    if (s != null) {
      autoCreate = Boolean.parseBoolean(s);
    }

    if (autoCreate && broadcaster.isDestroyed() && config.getBroadcasterFactory() != null) {
      logger.debug(
          "Broadcaster {} has been destroyed and cannot be re-used. Recreating a new one with the same name. You can turn off this"
              + " mechanism by adding, in web.xml, {} set to false",
          broadcaster.getID(),
          ApplicationConfig.RECOVER_DEAD_BROADCASTER);

      Broadcaster.SCOPE scope = broadcaster.getScope();
      synchronized (this) {
        String id =
            scope != Broadcaster.SCOPE.REQUEST
                ? broadcaster.getID()
                : broadcaster.getID() + ".recovered" + UUID.randomUUID();

        // Another Thread may have added the Broadcaster.
        broadcaster = config.getBroadcasterFactory().lookup(id, true);
        broadcaster.setScope(scope);
        broadcaster.addAtmosphereResource(this);
      }
    }
    return broadcaster;
  }
  public void cancel() throws IOException {

    if (!isCancelled.getAndSet(true)) {
      logger.trace("Cancelling {}", uuid);

      if (config.getBroadcasterFactory().getDefault() != null) {
        config.getBroadcasterFactory().getDefault().removeAllAtmosphereResource(this);
        if (transport.equals(TRANSPORT.WEBSOCKET)) {
          String parentUUID = (String) req.getAttribute(SUSPENDED_ATMOSPHERE_RESOURCE_UUID);
          AtmosphereResource p = AtmosphereResourceFactory.getDefault().find(parentUUID);
          if (p != null) {
            config.getBroadcasterFactory().getDefault().removeAllAtmosphereResource(p);
          }
        }
      }

      asyncSupport.complete(this);

      SessionTimeoutSupport.restoreTimeout(req);
      action.type(Action.TYPE.CANCELLED);
      if (asyncSupport != null) asyncSupport.action(this);
      // We must close the underlying WebSocket as well.
      if (AtmosphereResponse.class.isAssignableFrom(response.getClass())) {
        AtmosphereResponse.class.cast(response).close();
        AtmosphereResponse.class.cast(response).destroy();
      }

      if (AtmosphereRequest.class.isAssignableFrom(req.getClass())) {
        AtmosphereRequest.class.cast(req).destroy();
      }
      req.removeAttribute(FrameworkConfig.ATMOSPHERE_RESOURCE);
      event.destroy();
    }
  }
  /**
   * Create a {@link ScheduledExecutorService} used ot schedule I/O and non I/O events.
   *
   * @param config the {@link AtmosphereConfig}
   * @return {@link ScheduledExecutorService}
   */
  public static ScheduledExecutorService getScheduler(final AtmosphereConfig config) {
    final boolean shared = config.framework().isShareExecutorServices();

    if (!shared || config.properties().get("scheduler") == null) {
      ScheduledExecutorService scheduler =
          Executors.newScheduledThreadPool(
              Runtime.getRuntime().availableProcessors(),
              new ThreadFactory() {

                private final AtomicInteger count = new AtomicInteger();

                @Override
                public Thread newThread(final Runnable runnable) {
                  Thread t =
                      new Thread(runnable, "Atmosphere-Scheduler-" + count.getAndIncrement());
                  t.setDaemon(true);
                  return t;
                }
              });

      if (shared) {
        config.properties().put("scheduler", scheduler);
      }
      return scheduler;
    } else {
      return (ScheduledExecutorService) config.properties().get("scheduler");
    }
  }
  @Override
  public void configure(AtmosphereConfig config) {
    try {

      String s = config.getInitParameter(ATMOSPHERE_SPRING_EXCLUDE_CLASSES);
      if (s != null) {
        String[] list = s.split(",");
        for (String clazz : list) {
          excludedFromInjection.add(IOUtils.loadClass(getClass(), clazz));
        }

        if (list.length > 0) {
          preventSpringInjection = true;
        }
      }

      context = new AnnotationConfigApplicationContext();
      context.setParent(
          WebApplicationContextUtils.getWebApplicationContext(
              config.framework().getServletContext()));

      context.refresh();

      // Hack to make it injectable
      context.register(AtmosphereConfig.class);
      ((AtmosphereConfig)
              context.getBean(AtmosphereConfig.class.getCanonicalName(), config.framework()))
          .populate(config);
    } catch (Exception ex) {
      logger.warn("Unable to configure injection", ex);
    }
  }
 @BeforeMethod
 public void setUp() throws Exception {
   AtmosphereFramework f =
       new AtmosphereFramework()
           .addInitParameter(ApplicationConfig.WEBSOCKET_SUPPRESS_JSR356, "true");
   config = f.init().getAtmosphereConfig();
   factory = config.getBroadcasterFactory();
   factory.remove(Broadcaster.ROOT_MASTER);
   metaBroadcaster = config.metaBroadcaster();
 }
 /** {@inheritDoc} */
 @Override
 protected BroadcasterConfig createBroadcasterConfig(AtmosphereConfig config) {
   BroadcasterConfig bc =
       (BroadcasterConfig) config.properties().get(BroadcasterConfig.class.getName());
   if (bc == null) {
     bc = new BroadcasterConfig(config.framework().broadcasterFilters(), config, false, getID());
     config.properties().put(BroadcasterConfig.class.getName(), bc);
   }
   return bc;
 }
    /**
     * Checks in the {@link AtmosphereConfig} if the {@link #propertyName} is defined as an
     * init-param and instantiate the appropriate class.
     *
     * <p>If instantiation fails, the exception is logged and {@code null} is returned.
     *
     * @param desiredType the type to be returned
     * @param config the configuration that provides parameters
     * @param <T> the generic for modular call
     * @return the instance of the expected class, {@code null} if an error occurs
     */
    public <T> T retrieve(final Class<T> desiredType, final AtmosphereConfig config) {
      final String initParameter = config.getInitParameter(this.propertyName);
      final String className = (initParameter != null) ? initParameter : defaultClass;

      try {
        final AtmosphereFramework fwk = config.framework();
        final Object retval =
            fwk.newClassInstance(
                desiredType, desiredType.getClass().cast(Class.forName(className)));
        return desiredType.cast(retval);
      } catch (Exception e) {
        logger.error("Unable to initialize {}", getClass().getName(), e);
        return null;
      }
    }
  /**
   * Initialize an {@link AtmosphereResource}.
   *
   * @param config The {@link org.atmosphere.cpr.AtmosphereConfig}
   * @param broadcaster The {@link org.atmosphere.cpr.Broadcaster}.
   * @param req The {@link AtmosphereRequest}
   * @param response The {@link AtmosphereResource}
   * @param asyncSupport The {@link AsyncSupport}
   * @param atmosphereHandler The {@link AtmosphereHandler}
   * @return this
   */
  @Override
  public AtmosphereResource initialize(
      AtmosphereConfig config,
      Broadcaster broadcaster,
      AtmosphereRequest req,
      AtmosphereResponse response,
      AsyncSupport asyncSupport,
      AtmosphereHandler atmosphereHandler) {
    this.req = req;
    this.response = response;
    this.broadcaster = broadcaster;
    this.config = config;
    this.asyncSupport = asyncSupport;
    this.atmosphereHandler = atmosphereHandler;
    this.event = new AtmosphereResourceEventImpl(this);

    String s = (String) req.getAttribute(SUSPENDED_ATMOSPHERE_RESOURCE_UUID);
    if (s == null) {
      s = response.getHeader(HeaderConfig.X_ATMOSPHERE_TRACKING_ID);
    }
    uuid = s == null ? UUID.randomUUID().toString() : s;

    if (config.isSupportSession()) {
      // Keep a reference to an HttpSession in case the associated request get recycled by the
      // underlying container.
      try {
        session = req.getSession(true);
      } catch (NullPointerException ex) {
        // http://java.net/jira/browse/GLASSFISH-18856
        logger.trace("http://java.net/jira/browse/GLASSFISH-18856", ex);
      }
    }
    transport = configureTransport();
    return this;
  }
 @Override
 public void configure(AtmosphereConfig config) {
   String s = config.getInitParameter(ApplicationConfig.MESSAGE_DELIMITER);
   if (s != null) {
     wsDelimiter = s;
   }
 }
  /**
   * Return the {@link AtmosphereHandler} mapped to the passed servlet-path.
   *
   * @param req the {@link AtmosphereResponse}
   * @return the {@link AtmosphereHandler} mapped to the passed servlet-path.
   * @throws javax.servlet.ServletException
   */
  protected AtmosphereHandlerWrapper map(AtmosphereRequest req) throws ServletException {
    String path;
    if (req.getPathInfo() != null) {
      path = req.getServletPath() + req.getPathInfo();
    } else {
      path = req.getServletPath();
    }

    if (path.isEmpty()) {
      path = "/";
    }

    // (1) First, try exact match
    AtmosphereHandlerWrapper atmosphereHandlerWrapper = map(path);
    if (atmosphereHandlerWrapper == null) {
      // (2) Try with a trailing /
      if (!path.endsWith("/")) {
        atmosphereHandlerWrapper = map(path + "/");
      }

      // (3) Try wildcard
      if (atmosphereHandlerWrapper == null) {
        atmosphereHandlerWrapper = map("/all");
      }
    }

    if (atmosphereHandlerWrapper == null) {
      throw new AtmosphereMappingException("No AtmosphereHandler maps request for " + path);
    }
    config
        .getBroadcasterFactory()
        .add(atmosphereHandlerWrapper.broadcaster, atmosphereHandlerWrapper.broadcaster.getID());
    return atmosphereHandlerWrapper;
  }
 protected AtmosphereHandlerWrapper map(String path) {
   AtmosphereHandlerWrapper atmosphereHandlerWrapper = config.handlers().get(path);
   if (atmosphereHandlerWrapper == null) {
     final Map<String, String> m = new HashMap<String, String>();
     for (Map.Entry<String, AtmosphereHandlerWrapper> e : config.handlers().entrySet()) {
       UriTemplate t = new UriTemplate(e.getKey());
       logger.trace("Trying to map {} to {}", t, path);
       if (t.match(path, m)) {
         atmosphereHandlerWrapper = e.getValue();
         logger.trace("Mapped {} to {}", t, e.getValue());
         break;
       }
     }
   }
   return atmosphereHandlerWrapper;
 }
 public void _destroy() {
   try {
     removeEventListeners();
     if (!isCancelled.get()) {
       try {
         getBroadcaster(false).removeAtmosphereResource(this);
       } catch (IllegalStateException ex) {
         logger.trace(ex.getMessage(), ex);
       }
       if (config.getBroadcasterFactory().getDefault() != null) {
         config.getBroadcasterFactory().getDefault().removeAllAtmosphereResource(this);
       }
     }
   } catch (Throwable t) {
     logger.trace("destroyResource", t);
   }
 }
 @Override
 public HttpSession session(boolean create) {
   if (config.isSupportSession() && session == null) {
     // http://java.net/jira/browse/GLASSFISH-18856
     session = req.getSession(create);
   }
   return session;
 }
 private static void keepAliveThreads(ThreadPoolExecutor e, AtmosphereConfig config) {
   int keepAlive = DEFAULT_KEEP_ALIVE;
   String s = config.getInitParameter(ApplicationConfig.EXECUTORFACTORY_KEEP_ALIVE);
   if (s != null) {
     keepAlive = Integer.parseInt(s);
   }
   e.setKeepAliveTime(keepAlive, TimeUnit.SECONDS);
 }
 public JBossWebCometSupport(AtmosphereConfig config) {
   super(config);
   Object b = config.getInitParameter(ApplicationConfig.TOMCAT_CLOSE_STREAM);
   closeConnectionOnInputStream = b == null ? true : Boolean.parseBoolean(b.toString());
   try {
     Class.forName(HttpEvent.class.getName());
   } catch (Throwable e) {
     throw new IllegalStateException(unableToDetectComet());
   }
 }
  @Override
  public void configure(AtmosphereConfig config) {
    Object o = config.properties().get("shared");
    if (o != null) {
      isShared = Boolean.parseBoolean(o.toString());
    }

    if (isShared) {
      reaper = ExecutorsFactory.getScheduler(config);
    } else {
      reaper = Executors.newSingleThreadScheduledExecutor();
    }
    this.config = config;
  }
  @Override
  public void init(ServletConfig sc) throws ServletException {

    String maxInactive =
        sc.getInitParameter(MAX_INACTIVE) != null
            ? sc.getInitParameter(MAX_INACTIVE)
            : config.getInitParameter(MAX_INACTIVE);

    if (maxInactive != null) {
      trackActiveRequest = true;
      final long maxInactiveTime = Long.parseLong(maxInactive);
      if (maxInactiveTime <= 0) return;

      closedDetector.scheduleAtFixedRate(
          new Runnable() {
            public void run() {
              for (AtmosphereRequest req : aliveRequests.keySet()) {
                long l = (Long) req.getAttribute(MAX_INACTIVE);
                if (l > 0 && System.currentTimeMillis() - l > maxInactiveTime) {
                  try {
                    logger.debug(
                        "Close detector disconnecting {}. Current size {}",
                        req,
                        aliveRequests.size());
                    AtmosphereResourceImpl r = (AtmosphereResourceImpl) aliveRequests.remove(req);
                    cancelled(req, r.getResponse(false));
                  } catch (Throwable e) {
                    logger.warn("closedDetector", e);
                  } finally {
                    try {
                      req.setAttribute(MAX_INACTIVE, (long) -1);
                    } catch (Throwable t) {
                      logger.trace("closedDetector", t);
                    }
                  }
                }
              }
            }
          },
          0,
          1,
          TimeUnit.SECONDS);
    }
  }
Example #18
0
  public ChannelBufferPool(
      final int minIdle,
      final int writeBufferPoolSize,
      final long validationInterval,
      AtmosphereConfig config) {
    this.writeBufferPoolSize = writeBufferPoolSize;

    initialize(minIdle);

    if (writeBufferPoolSize != -1) {
      ExecutorsFactory.getScheduler(config)
          .scheduleWithFixedDelay(
              new Runnable() {
                @Override
                public void run() {
                  int size = pool.size();
                  if (size < minIdle) {
                    int sizeToBeAdded = minIdle - size;
                    for (int i = 0; i < sizeToBeAdded; i++) {
                      pool.add(createObject());
                    }
                  } else if (size > writeBufferPoolSize) {
                    int sizeToBeRemoved = size - writeBufferPoolSize;
                    for (int i = 0; i < sizeToBeRemoved; i++) {
                      pool.poll();
                    }
                  }
                }
              },
              validationInterval,
              validationInterval,
              TimeUnit.SECONDS);

      config.shutdownHook(
          new AtmosphereConfig.ShutdownHook() {
            @Override
            public void shutdown() {
              pool.clear();
            }
          });
    }
  }
  public void configure(AtmosphereConfig config) {
    this.config = config;

    String maxInactive = config.getInitParameter(MAX_INACTIVE);
    if (maxInactive != null) {
      maxInactiveTime = Long.parseLong(maxInactive);
    }

    if (maxInactiveTime > 0) {
      ExecutorsFactory.getScheduler(config)
          .scheduleAtFixedRate(
              new Runnable() {
                public void run() {
                  idleResources();
                }
              },
              0,
              2,
              TimeUnit.SECONDS);
    }
  }
  /** {@inheritDoc} */
  @Override
  public void configure(final AtmosphereConfig config) {
    framework = config.framework();
    arsf = AtmosphereResourceSessionFactory.getDefault();
    setStompFormat(PropertyClass.STOMP_FORMAT_CLASS.retrieve(StompFormat.class, config));

    try {
      // TODO: user must map AtmosphereServlet to /stomp in web.xml, can we offer a chance to set a
      // custom location ?
      framework.addAtmosphereHandler(
          "/stomp",
          framework.newClassInstance(
              AtmosphereHandler.class, AbstractReflectorAtmosphereHandler.Default.class));

      interceptors =
          new ConcurrentHashMap<org.atmosphere.stomp.protocol.Action, StompInterceptor>();
      configureInterceptor(
          config,
          ConnectInterceptor.class,
          org.atmosphere.stomp.protocol.Action.CONNECT,
          org.atmosphere.stomp.protocol.Action.STOMP,
          org.atmosphere.stomp.protocol.Action.NULL);
      configureInterceptor(
          config, SubscribeInterceptor.class, org.atmosphere.stomp.protocol.Action.SUBSCRIBE);
      configureInterceptor(
          config, UnsubscribeInterceptor.class, org.atmosphere.stomp.protocol.Action.UNSUBSCRIBE);
      configureInterceptor(
          config, SendInterceptor.class, org.atmosphere.stomp.protocol.Action.SEND);

      final BroadcastFilterLifecycle filter =
          framework.newClassInstance(BroadcastFilterLifecycle.class, StompBroadcastFilter.class);
      framework.broadcasterFilters(filter);
      filter.init(config);
    } catch (InstantiationException e) {
      logger.error("", e);
    } catch (IllegalAccessException e) {
      logger.error("", e);
    }
  }
  protected void idleResources() {
    for (Broadcaster b : config.getBroadcasterFactory().lookupAll()) {
      for (AtmosphereResource r : b.getAtmosphereResources()) {
        AtmosphereRequest req = AtmosphereResourceImpl.class.cast(r).getRequest(false);
        try {
          if (req.getAttribute(MAX_INACTIVE) == null) return;

          long l = (Long) req.getAttribute(MAX_INACTIVE);
          if (l > 0 && System.currentTimeMillis() - l > maxInactiveTime) {
            req.setAttribute(MAX_INACTIVE, (long) -1);

            logger.debug("IdleResourceInterceptor disconnecting {}", r);

            if (!AtmosphereResourceImpl.class.cast(r).isSuspended()
                || !AtmosphereResourceImpl.class.cast(r).isInScope()) {
              return;
            }

            Future<?> f = (Future<?>) req.getAttribute(HeartbeatInterceptor.HEARTBEAT_FUTURE);
            if (f != null) f.cancel(false);
            req.removeAttribute(HeartbeatInterceptor.HEARTBEAT_FUTURE);

            Object o = req.getAttribute(ASYNCHRONOUS_HOOK);
            req.setAttribute(ASYNCHRONOUS_HOOK, null);

            AsynchronousProcessor.AsynchronousProcessorHook h;
            if (o != null
                && AsynchronousProcessor.AsynchronousProcessorHook.class.isAssignableFrom(
                    o.getClass())) {
              h = (AsynchronousProcessor.AsynchronousProcessorHook) o;
              h.closed();
            }
          }
        } catch (Throwable e) {
          logger.warn("IdleResourceInterceptor", e);
        }
      }
    }
  }
  /**
   * Create an {@link ExecutorService} to be used for dispatching messages, not I/O events.
   *
   * @param config the {@link AtmosphereConfig}
   * @param name a name to use if shared is false.
   * @return {@link ExecutorService}
   */
  public static ExecutorService getMessageDispatcher(
      final AtmosphereConfig config, final String name) {
    final boolean shared = config.framework().isShareExecutorServices();

    boolean isExecutorShared = shared ? true : false;
    if (!shared || config.properties().get("executorService") == null) {
      int numberOfMessageProcessingThread = DEFAULT_MESSAGE_THREAD;
      String s =
          config.getInitParameter(
              ApplicationConfig.BROADCASTER_MESSAGE_PROCESSING_THREADPOOL_MAXSIZE);
      if (s != null) {
        numberOfMessageProcessingThread = Integer.parseInt(s);
      }

      if (isExecutorShared && numberOfMessageProcessingThread == 1) {
        logger.warn(
            "Not enough numberOfMessageProcessingThread for a shareable thread pool {}, "
                + "Setting it to a newCachedThreadPool",
            numberOfMessageProcessingThread);
        numberOfMessageProcessingThread = -1;
      }

      ThreadPoolExecutor messageService;
      if (numberOfMessageProcessingThread == -1) {
        messageService =
            (ThreadPoolExecutor)
                Executors.newCachedThreadPool(
                    new ThreadFactory() {

                      private final AtomicInteger count = new AtomicInteger();

                      @Override
                      public Thread newThread(final Runnable runnable) {
                        Thread t =
                            new Thread(
                                runnable,
                                (shared ? "Atmosphere-Shared" : name)
                                    + "-DispatchOp-"
                                    + count.getAndIncrement());
                        t.setDaemon(true);
                        return t;
                      }
                    });
      } else {
        messageService =
            (ThreadPoolExecutor)
                Executors.newFixedThreadPool(
                    numberOfMessageProcessingThread,
                    new ThreadFactory() {

                      private final AtomicInteger count = new AtomicInteger();

                      @Override
                      public Thread newThread(final Runnable runnable) {
                        Thread t =
                            new Thread(
                                runnable,
                                (shared ? "Atmosphere-Shared" : name)
                                    + "-DispatchOp-"
                                    + count.getAndIncrement());
                        t.setDaemon(true);
                        return t;
                      }
                    });
      }

      keepAliveThreads(messageService, config);

      if (shared) {
        config.properties().put("executorService", messageService);
      }
      return messageService;
    } else {
      return (ExecutorService) config.properties().get("executorService");
    }
  }
 @AfterMethod
 public void unSet() throws Exception {
   config.destroy();
   ExecutorsFactory.reset(config);
   factory.destroy();
 }
  @Override
  public AtmosphereResource suspend(long timeout) {

    if (event.isSuspended() || disableSuspend) return this;

    if (config.isSupportSession()
        && req.getSession(false) != null
        && req.getSession().getMaxInactiveInterval() != -1
        && req.getSession().getMaxInactiveInterval() * 1000 < timeout) {
      throw new IllegalStateException(
          "Cannot suspend a "
              + "response longer than the session timeout. Increase the value of session-timeout in web.xml");
    }

    if (transport().equals(TRANSPORT.JSONP) || transport().equals(TRANSPORT.LONG_POLLING)) {
      resumeOnBroadcast.set(true);
    }

    onPreSuspend(event);

    // Recheck based on preSuspend
    if (event.isSuspended() || disableSuspend) return this;

    if (!event.isResumedOnTimeout()) {

      Enumeration<String> connection = req.getHeaders("Connection");
      if (connection == null) {
        connection = req.getHeaders("connection");
      }

      if (connection != null && connection.hasMoreElements()) {
        String[] e = connection.nextElement().toString().split(",");
        for (String upgrade : e) {
          if (upgrade.trim().equalsIgnoreCase(WEBSOCKET_UPGRADE)) {
            if (!asyncSupport.supportWebSocket()) {
              response.addHeader(X_ATMOSPHERE_ERROR, "Websocket protocol not supported");
            } else {
              req.setAttribute(FrameworkConfig.TRANSPORT_IN_USE, HeaderConfig.WEBSOCKET_TRANSPORT);
            }
          }
        }
      }

      if (req.getHeader(X_ATMOSPHERE_TRANSPORT) == null) {
        req.setAttribute(FrameworkConfig.TRANSPORT_IN_USE, HeaderConfig.LONG_POLLING_TRANSPORT);
      }

      req.setAttribute(PRE_SUSPEND, "true");
      action.type(Action.TYPE.SUSPEND);
      action.timeout(timeout);

      // TODO: We can possibly optimize that call by avoiding creating a Broadcaster if we are sure
      // the Broadcaster
      // is unique.
      boolean isJersey = req.getAttribute(FrameworkConfig.CONTAINER_RESPONSE) != null;

      boolean skipCreation = false;
      if (req.getAttribute(SKIP_BROADCASTER_CREATION) != null) {
        skipCreation = true;
      }

      // Null means SCOPE=REQUEST set by a Meteor
      if (!skipCreation
          && (broadcaster == null || broadcaster.getScope() == Broadcaster.SCOPE.REQUEST)
          && !isJersey) {
        String id = broadcaster != null ? broadcaster.getID() : getClass().getName();
        Class<? extends Broadcaster> clazz =
            broadcaster != null ? broadcaster.getClass() : DefaultBroadcaster.class;

        broadcaster = config.getBroadcasterFactory().lookup(clazz, id, false);
        if (broadcaster == null || broadcaster.getAtmosphereResources().size() > 0) {
          broadcaster =
              config.getBroadcasterFactory().lookup(clazz, id + "/" + UUID.randomUUID(), true);
        }
      }

      broadcaster.addAtmosphereResource(this);
      if (req.getAttribute(DefaultBroadcaster.CACHED) != null
          && transport() != null
          && (transport().equals(TRANSPORT.LONG_POLLING) || transport().equals(TRANSPORT.JSONP))) {
        action.type(Action.TYPE.CONTINUE);
        // Do nothing because we have found cached message which was written already, and the
        // handler resumed.
        logger.debug("Cached message found, not suspending {}", uuid());
        return this;
      }
      req.removeAttribute(PRE_SUSPEND);
      notifyListeners();
    }
    return this;
  }
  @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;
  }
  /**
   * Parse the atmosphere-handlers element.
   *
   * @param document
   */
  private AtmosphereConfig parse(AtmosphereConfig config, Document document) {

    Element element = document.getDocumentElement();
    NodeList atmosphereHandlers = element.getElementsByTagName("atmosphere-handler");
    for (int i = 0; i < atmosphereHandlers.getLength(); i++) {
      AtmosphereHandlerConfig atmoHandler = new AtmosphereHandlerConfig();

      Node root = atmosphereHandlers.item(i);

      // parse Attributes
      for (int j = 0; j < root.getAttributes().getLength(); j++) {

        Node attribute = root.getAttributes().item(j);

        if (attribute.getNodeName().equals("support-session")) {
          atmoHandler.setSupportSession(attribute.getNodeValue());
        } else if (attribute.getNodeName().equals("context-root")) {
          atmoHandler.setContextRoot(attribute.getNodeValue());
        } else if (attribute.getNodeName().equals("class-name")) {
          atmoHandler.setClassName(attribute.getNodeValue());
        } else if (attribute.getNodeName().equals("broadcaster")) {
          atmoHandler.setBroadcaster(attribute.getNodeValue());
        } else if (attribute.getNodeName().equals("broadcasterCache")) {
          atmoHandler.setBroadcasterCache(attribute.getNodeValue());
        } else if (attribute.getNodeName().equals("broadcastFilterClasses")) {
          String[] values = attribute.getNodeValue().split(",");
          for (String value : values) {
            atmoHandler.getBroadcastFilterClasses().add(value);
          }
        } else if (attribute.getNodeName().equals("comet-support")) {
          atmoHandler.setCometSupport(attribute.getNodeValue());
        } else if (attribute.getNodeName().equals("interceptorClasses")) {
          String[] values = attribute.getNodeValue().split(",");
          for (String value : values) {
            atmoHandler.getAtmosphereInterceptorClasses().add(value);
          }
        }
      }

      NodeList list = root.getChildNodes();

      for (int j = 0; j < list.getLength(); j++) {
        Node n = list.item(j);
        if (n.getNodeName().equals("property")) {
          String param = n.getAttributes().getNamedItem("name").getNodeValue();
          String value = n.getAttributes().getNamedItem("value").getNodeValue();

          atmoHandler.getProperties().add(new AtmosphereHandlerProperty(param, value));
        } else if (n.getNodeName().equals("applicationConfig")) {

          String param = null;
          String value = null;
          for (int k = 0; k < n.getChildNodes().getLength(); k++) {

            Node n2 = n.getChildNodes().item(k);

            if (n2.getNodeName().equals("param-name")) {
              param = n2.getFirstChild().getNodeValue();
            } else if (n2.getNodeName().equals("param-value")) {
              if (n2 != null) {
                value = n2.getFirstChild().getNodeValue();
              }
            }
          }

          if (param != null) {
            atmoHandler.getApplicationConfig().add(new ApplicationConfiguration(param, value));
          }

        } else if (n.getNodeName().equals("frameworkConfig")) {
          String param = null;
          String value = null;
          for (int k = 0; k < n.getChildNodes().getLength(); k++) {

            Node n2 = n.getChildNodes().item(k);

            if (n2.getNodeName().equals("param-name")) {
              param = n2.getFirstChild().getNodeValue();
            } else if (n2.getNodeName().equals("param-value")) {
              if (n2 != null) {
                value = n2.getFirstChild().getNodeValue();
              }
            }
          }

          if (param != null) {
            atmoHandler.getFrameworkConfig().add(new FrameworkConfiguration(param, value));
          }
        }
      }

      config.getAtmosphereHandlerConfig().add(atmoHandler);
    }

    return config;
  }
  /**
   * Invoke the {@link AtmosphereHandler#onRequest} method.
   *
   * @param req the {@link AtmosphereRequest}
   * @param res the {@link AtmosphereResponse}
   * @return action the Action operation.
   * @throws java.io.IOException
   * @throws javax.servlet.ServletException
   */
  Action action(AtmosphereRequest req, AtmosphereResponse res)
      throws IOException, ServletException {

    boolean webSocketEnabled = false;
    if (req.getHeaders("Connection") != null && req.getHeaders("Connection").hasMoreElements()) {
      String[] e = req.getHeaders("Connection").nextElement().toString().split(",");
      for (String upgrade : e) {
        if (upgrade.equalsIgnoreCase("Upgrade")) {
          webSocketEnabled = true;
          break;
        }
      }
    }

    if (webSocketEnabled && !supportWebSocket()) {
      res.setStatus(501);
      res.addHeader(X_ATMOSPHERE_ERROR, "Websocket protocol not supported");
      res.flushBuffer();
      return new Action();
    }

    if (config.handlers().isEmpty()) {
      logger.error(
          "No AtmosphereHandler found. Make sure you define it inside META-INF/atmosphere.xml");
      throw new AtmosphereMappingException(
          "No AtmosphereHandler found. Make sure you define it insides META-INF/atmosphere.xml");
    }

    if (supportSession()) {
      // Create the session needed to support the Resume
      // operation from disparate requests.
      HttpSession session = req.getSession(true);
      // Do not allow times out.
      if (session.getMaxInactiveInterval() == DEFAULT_SESSION_TIMEOUT) {
        session.setMaxInactiveInterval(-1);
      }
    }

    req.setAttribute(FrameworkConfig.SUPPORT_SESSION, supportSession());

    AtmosphereHandlerWrapper handlerWrapper = map(req);
    // Check Broadcaster state. If destroyed, replace it.
    Broadcaster b = handlerWrapper.broadcaster;
    if (b.isDestroyed()) {
      synchronized (handlerWrapper) {
        config.getBroadcasterFactory().remove(b, b.getID());
        handlerWrapper.broadcaster = config.getBroadcasterFactory().get(b.getID());
      }
    }
    AtmosphereResourceImpl resource =
        new AtmosphereResourceImpl(
            config, handlerWrapper.broadcaster, req, res, this, handlerWrapper.atmosphereHandler);

    req.setAttribute(FrameworkConfig.ATMOSPHERE_RESOURCE, resource);
    req.setAttribute(FrameworkConfig.ATMOSPHERE_HANDLER, handlerWrapper.atmosphereHandler);

    try {
      handlerWrapper.atmosphereHandler.onRequest(resource);
    } catch (IOException t) {
      resource.onThrowable(t);
      throw t;
    }

    if (trackActiveRequest
        && resource.getAtmosphereResourceEvent().isSuspended()
        && req.getAttribute(FrameworkConfig.CANCEL_SUSPEND_OPERATION) == null) {
      req.setAttribute(MAX_INACTIVE, System.currentTimeMillis());
      aliveRequests.put(req, resource);
    }
    return resource.action();
  }
 public Tomcat7CometSupport(AtmosphereConfig config) {
   super(config);
   Object b = config.getInitParameter(ApplicationConfig.TOMCAT_CLOSE_STREAM);
   closeConnectionOnInputStream = b == null ? true : Boolean.parseBoolean(b.toString());
 }
 /** Return the container's name. */
 public String getContainerName() {
   return config.getServletConfig().getServletContext().getServerInfo();
 }