@Test
  public void testHeartbeat() throws IOException, ServletException {
    // Open connection
    AtmosphereRequest request =
        new AtmosphereRequest.Builder().pathInfo("/heartbeat").method("GET").build();

    request.header(X_ATMOSPHERE_TRANSPORT, WEBSOCKET_TRANSPORT);
    framework.doCometSupport(request, AtmosphereResponse.newInstance());

    // Check suspend
    final AtmosphereResource res = r.get();
    assertNotNull(res);

    // Send heartbeat
    request =
        new AtmosphereRequest.Builder()
            .pathInfo("/heartbeat")
            .method("GET")
            .body(Heartbeat.paddingData)
            .build();
    request.header(X_ATMOSPHERE_TRANSPORT, WEBSOCKET_TRANSPORT);
    request.setAttribute(HeartbeatInterceptor.INTERCEPTOR_ADDED, "");
    res.initialize(
        res.getAtmosphereConfig(),
        res.getBroadcaster(),
        request,
        AtmosphereResponse.newInstance(),
        framework.getAsyncSupport(),
        res.getAtmosphereHandler());
    request.setAttribute(FrameworkConfig.INJECTED_ATMOSPHERE_RESOURCE, res);
    framework.doCometSupport(request, AtmosphereResponse.newInstance());
    assertNotNull(message.get());
    assertEquals(message.get(), Heartbeat.paddingData);
  }
  @Override
  public void onStateChange(AtmosphereResourceEvent event) throws IOException {
    AtmosphereResource r = event.getResource();
    AtmosphereResponse res = r.getResponse();

    if (event.isSuspended()) {
      String body = event.getMessage().toString();

      ChatData data = new Gson().fromJson(body, ChatData.class);

      res.getWriter().write(new ChatData(data.getAuthor(), data.getText()).toString());
      switch (r.transport()) {
        case JSONP:
        case AJAX:
        case LONG_POLLING:
          event.getResource().resume();
          break;
        default:
          res.getWriter().flush();
          break;
      }
    } else if (!event.isResuming()) {
      event.broadcaster().broadcast(new ChatData("Someone", "say bye bye!").toString());
    }
  }
Example #3
0
  @Test
  public void setCookieTest()
      throws IOException, ServletException, ExecutionException, InterruptedException {
    final AtomicReference<Cookie> cValue = new AtomicReference<Cookie>();
    final AtomicReference<AtmosphereResource> r = new AtomicReference<AtmosphereResource>();

    framework.addAtmosphereHandler(
        "/*",
        new AtmosphereHandler() {

          @Override
          public void onRequest(AtmosphereResource resource) throws IOException {
            resource.getResponse().addCookie(resource.getRequest().getCookies()[0]);
            r.set(resource);
            resource.getBroadcaster().addAtmosphereResource(resource);
          }

          @Override
          public void onStateChange(AtmosphereResourceEvent event) throws IOException {
            Cookie[] c = event.getResource().getRequest().getCookies();
            cValue.set(c[0]);
          }

          @Override
          public void destroy() {}
        });
    Set<Cookie> c = new HashSet<Cookie>();
    Cookie a = new Cookie("yo", "man");
    a.setComment("kdaskjdaskda");
    a.setDomain("dasdasdasd");
    a.setHttpOnly(true);
    a.setPath("/ya");
    c.add(a);

    AtmosphereRequest request =
        new AtmosphereRequestImpl.Builder().cookies(c).pathInfo("/a").build();
    AtmosphereResponse response =
        AtmosphereResponseImpl.newInstance().delegateToNativeResponse(false);
    response.destroyable(false);
    framework.doCometSupport(request, response);

    r.get().getBroadcaster().broadcast("yo").get();
    assertNotNull(cValue.get());

    Cookie i = c.iterator().next();
    assertEquals(i.getName(), cValue.get().getName());
    assertEquals(i.getValue(), cValue.get().getValue());
    assertEquals(
        "yo=man; Domain=dasdasdasd; Path=/ya; HttpOnly", response.headers().get("Set-Cookie"));
  }
  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();
    }
  }
  public synchronized void cancel() throws IOException {
    action.type(Action.TYPE.RESUME);
    if (isCancelled.getAndSet(true)) return;

    if (asyncSupport instanceof AsynchronousProcessor) {
      try {
        AsynchronousProcessor.class.cast(asyncSupport).resumed(req, response);
      } catch (ServletException e) {
        logger.trace("", e);
      }
    }
    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();
    }

    // TODO: Grab some measurement.
    //        req = null;
    //        response = null;

    // Just in case
    if (broadcaster != null) {
      broadcaster.removeAtmosphereResource(this);
    }
    event.destroy();
  }
  /**
   * 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 AtmosphereResource write(byte[] o) {
   response.write(o);
   if (resumeOnBroadcast()) {
     resume();
   }
   return this;
 }
 @Override
 public AtmosphereResource write(String s) {
   response.write(s);
   if (resumeOnBroadcast()) {
     resume();
   }
   return this;
 }
Example #9
0
 @Override
 public void onMessage(AtmosphereResponse response, TatamiNotification notification)
     throws IOException {
   if (log.isDebugEnabled()) {
     log.debug("Received Atmosphere message: " + notification);
   }
   String json = jsonObjectMapper.writeValueAsString(notification);
   response.getWriter().write(json);
 }
  @Test
  public void testPut() throws IOException, ServletException {

    AtmosphereRequest request =
        new AtmosphereRequest.Builder().pathInfo("/d").method("PUT").build();
    framework.doCometSupport(request, AtmosphereResponse.newInstance());
    assertNotNull(r.get());
    r.get().resume();
  }
  @Test
  public void testSuspend() throws IOException, ServletException {

    AtmosphereRequest request =
        new AtmosphereRequest.Builder().pathInfo("/j").method("GET").build();
    request.header(X_ATMOSPHERE_TRANSPORT, LONG_POLLING_TRANSPORT);
    framework.doCometSupport(request, AtmosphereResponse.newInstance());
    assertNotNull(r.get());
  }
 @Test
 public void testInputStreamMessage() throws IOException, ServletException {
   AtmosphereRequest request =
       new AtmosphereRequest.Builder().pathInfo("/inputStreamInjection").method("GET").build();
   framework.doCometSupport(request, AtmosphereResponse.newInstance());
   assertNotNull(r.get());
   r.get().resume();
   assertNotNull(message.get());
   assertEquals(message.get(), "message");
 }
  /**
   * Write the broadcasted object using the {@link OutputStream}. If a {@link Serializer} is
   * defined, the operation will be delagated to it. If not, the <F> (Response) OutputStream will be
   * used by calling Object.toString.getBytes()
   *
   * @param os an {@link OutputStream}
   * @param o an Object
   * @throws IOException
   */
  @Override
  public AtmosphereResource write(OutputStream os, Object o) throws IOException {
    if (o == null) throw new IllegalStateException("Object cannot be null");

    if (serializer != null) {
      serializer.write(os, o);
    } else {
      response.getOutputStream().write(o.toString().getBytes());
    }
    return this;
  }
 @Test
 public void createTest() {
   AtmosphereResource r =
       AtmosphereResourceFactory.getDefault()
           .create(
               mock(AtmosphereConfig.class),
               mock(Broadcaster.class),
               AtmosphereResponse.newInstance().request(AtmosphereRequest.create()),
               mock(AsyncSupport.class),
               mock(AtmosphereHandler.class));
   assertNotNull(r);
 }
  @Test
  public void testOverrideBroadcaster() throws IOException, ServletException {
    framework.setDefaultBroadcasterClassName(SimpleBroadcaster.class.getName());

    AtmosphereRequest request =
        new AtmosphereRequest.Builder().pathInfo("/override").method("GET").build();
    request.header(X_ATMOSPHERE_TRANSPORT, LONG_POLLING_TRANSPORT);
    framework.doCometSupport(request, AtmosphereResponse.newInstance());

    assertNotNull(r.get());
    assertEquals(r.get().getBroadcaster().getClass().getName(), SimpleBroadcaster.class.getName());
  }
  @Test
  public void testOnClose() throws IOException, ServletException {
    framework.addAtmosphereHandler(
        "/a",
        new AbstractReflectorAtmosphereHandler() {
          @Override
          public void onRequest(AtmosphereResource resource) throws IOException {}

          @Override
          public void destroy() {}
        });

    AtmosphereRequest request = new AtmosphereRequest.Builder().pathInfo("/a").build();

    final AtomicReference<Boolean> closed = new AtomicReference<Boolean>();

    final AtmosphereResourceEventListener listener =
        new AtmosphereResourceEventListenerAdapter() {
          @Override
          public void onClose(AtmosphereResourceEvent event) {
            closed.set(true);
          }
        };

    framework.interceptor(
        new AtmosphereInterceptor() {
          @Override
          public void configure(AtmosphereConfig config) {}

          @Override
          public Action inspect(AtmosphereResource r) {
            r.addEventListener(listener).suspend();
            try {
              r.getBroadcaster().broadcast("void").get();
            } catch (InterruptedException e) {
            } catch (ExecutionException e) {
            }
            return Action.CONTINUE;
          }

          @Override
          public void postInspect(AtmosphereResource r) {
            try {
              r.close();
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
        });
    framework.doCometSupport(request, AtmosphereResponse.newInstance());
    assertTrue(closed.get());
  }
  @Test
  public void testPriority() throws IOException, ServletException {

    AtmosphereRequest request =
        new AtmosphereRequest.Builder().pathInfo("/priority").method("GET").build();
    request.header(X_ATMOSPHERE_TRANSPORT, LONG_POLLING_TRANSPORT);
    framework.doCometSupport(request, AtmosphereResponse.newInstance());
    assertEquals(
        framework.getAtmosphereHandlers().get("/priority").interceptors.getFirst().toString(),
        "XXX");

    assertNotNull(r.get());
  }
 @Test
 public void notFoundTest() {
   for (int i = 0; i < 10; i++) {
     BroadcasterFactory.getDefault().get(String.valueOf(i));
   }
   AtmosphereResource r =
       AtmosphereResourceFactory.getDefault()
           .create(
               mock(AtmosphereConfig.class),
               BroadcasterFactory.getDefault().lookup("1"),
               AtmosphereResponse.newInstance().request(AtmosphereRequest.create()),
               mock(AsyncSupport.class),
               mock(AtmosphereHandler.class));
   assertNotNull(r);
   assertNull(AtmosphereResourceFactory.getDefault().find(r.uuid()));
 }
  @BeforeMethod
  public void setUp() throws Exception {
    AtmosphereConfig config = new AtmosphereFramework().getAtmosphereConfig();
    DefaultBroadcasterFactory factory =
        new DefaultBroadcasterFactory(ExcludeSessionBroadcaster.class, "NEVER", config);
    broadcaster = factory.get(ExcludeSessionBroadcaster.class, "test");
    atmosphereHandler = new AR();
    ar =
        new AtmosphereResourceImpl(
            config,
            broadcaster,
            mock(AtmosphereRequest.class),
            AtmosphereResponse.create(),
            mock(BlockingIOCometSupport.class),
            atmosphereHandler);

    broadcaster.addAtmosphereResource(ar);
  }
  @Test
  public void findTest() {
    Broadcaster b1 = BroadcasterFactory.getDefault().get("b1");
    Broadcaster b2 = BroadcasterFactory.getDefault().get("b2");
    AtmosphereResource r =
        AtmosphereResourceFactory.getDefault()
            .create(
                mock(AtmosphereConfig.class),
                b1,
                AtmosphereResponse.newInstance().request(AtmosphereRequest.create()),
                mock(AsyncSupport.class),
                mock(AtmosphereHandler.class));
    assertNotNull(r);

    b2.addAtmosphereResource(r);

    assertNotNull(AtmosphereResourceFactory.getDefault().find(r.uuid()));
  }
  public void cancel() throws IOException {

    if (!isCancelled.getAndSet(true)) {
      logger.trace("Cancelling {}", uuid);
      action.type(Action.TYPE.RESUME);
      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();
      }

      if (broadcaster != null) {
        broadcaster.removeAtmosphereResource(this);
      }
      event.destroy();
    }
  }
  @Override
  public Action inspect(final AtmosphereResource ar) {
    final AtmosphereResourceImpl r = AtmosphereResourceImpl.class.cast(ar);
    final AtmosphereRequest request = r.getRequest(false);
    final AtmosphereResponse response = r.getResponse(false);

    String uuid = request.getHeader(HeaderConfig.X_ATMOSPHERE_TRACKING_ID);
    String handshakeUUID = request.getHeader(HeaderConfig.X_ATMO_PROTOCOL);
    if (uuid != null && uuid.equals("0") && handshakeUUID != null) {
      request.header(HeaderConfig.X_ATMO_PROTOCOL, null);
      // Since 1.0.10

      final StringBuffer message =
          new StringBuffer(r.uuid()).append(wsDelimiter).append(System.currentTimeMillis());

      // https://github.com/Atmosphere/atmosphere/issues/993
      boolean track = false;
      if (r.getBroadcaster().getBroadcasterConfig().hasFilters()) {
        for (BroadcastFilter bf : r.getBroadcaster().getBroadcasterConfig().filters()) {
          if (TrackMessageSizeFilter.class.isAssignableFrom(bf.getClass())) {
            track = true;
            break;
          }
        }
      }

      final AtomicReference<String> protocolMessage =
          new AtomicReference<String>(message.toString());
      if (track) {
        protocolMessage.set(
            (String) f.filter(r, protocolMessage.get(), protocolMessage.get()).message());
      }

      if (!Utils.resumableTransport(r.transport())) {
        OnSuspend a =
            new OnSuspend() {
              @Override
              public void onSuspend(AtmosphereResourceEvent event) {
                response.write(protocolMessage.get());
                try {
                  response.flushBuffer();
                } catch (IOException e) {
                  logger.trace("", e);
                }
              }
            };
        // Pass the information to Servlet Based Framework
        request.setAttribute(CALLBACK_JAVASCRIPT_PROTOCOL, a);
        r.addEventListener(a);
      } else {
        response.write(protocolMessage.get());
      }

      // We don't need to reconnect here
      if (r.transport() == AtmosphereResource.TRANSPORT.WEBSOCKET
          || r.transport() == AtmosphereResource.TRANSPORT.STREAMING
          || r.transport() == AtmosphereResource.TRANSPORT.SSE) {
        return Action.CONTINUE;
      } else {
        return Action.SKIP_ATMOSPHEREHANDLER;
      }
    }
    return Action.CONTINUE;
  }
Example #23
0
 public ObserverSender(AtmosphereResponse response, List<Observer> observers) {
   if (observers == null) throw new UtilException("Cannot create ObserverSender without observer");
   this.response = response;
   this.observers = observers;
   this.writer = response.getAsyncIOWriter();
 }
  /**
   * Write the {@link AtmosphereResourceEvent#getMessage()} back to the client using the {@link
   * org.atmosphere.cpr.AtmosphereResponse#getOutputStream()} or {@link
   * org.atmosphere.cpr.AtmosphereResponse#getWriter()}. If a {@link org.atmosphere.cpr.Serializer}
   * is defined, it will be invoked and the writ operation will be delegated to to it.
   *
   * <p>By default, this method will try to use {@link
   * org.atmosphere.cpr.AtmosphereResponse#getWriter()}.
   *
   * @param event the {@link AtmosphereResourceEvent#getMessage()}
   * @throws java.io.IOException
   */
  public void onStateChange(AtmosphereResourceEvent event) throws IOException {

    Object message = event.getMessage();
    AtmosphereResponse r = event.getResource().getResponse();
    if (message == null || event.isCancelled() || event.getResource().getRequest().destroyed())
      return;

    if (event.getResource().getSerializer() != null) {
      try {
        event
            .getResource()
            .getSerializer()
            .write(event.getResource().getResponse().getOutputStream(), message);
      } catch (Throwable ex) {
        logger.warn("Serializer exception: message: " + message, ex);
        throw new IOException(ex);
      }
    } else {
      boolean isUsingStream =
          (Boolean) event.getResource().getRequest().getAttribute(PROPERTY_USE_STREAM);

      if (!isUsingStream) {
        try {
          r.getWriter();
        } catch (IllegalStateException e) {
          isUsingStream = true;
        }
      }

      if (message instanceof List) {
        for (String s : (List<String>) message) {
          if (isUsingStream) {
            r.getOutputStream().write(s.getBytes(r.getCharacterEncoding()));
            r.getOutputStream().flush();
          } else {
            r.getWriter().write(s);
            r.getWriter().flush();
          }
        }
      } else {
        if (isUsingStream) {
          r.getOutputStream().write(message.toString().getBytes(r.getCharacterEncoding()));
          r.getOutputStream().flush();
        } else {
          r.getWriter().write(message.toString());
          r.getWriter().flush();
        }
      }

      Boolean resumeOnBroadcast = event.getResource().resumeOnBroadcast();
      if (!resumeOnBroadcast) {
        // For legacy reason, check the attribute as well
        Object o =
            event.getResource().getRequest().getAttribute(ApplicationConfig.RESUME_ON_BROADCAST);
        if (o != null && Boolean.class.isAssignableFrom(o.getClass())) {
          resumeOnBroadcast = Boolean.class.cast(o);
        }
      }

      if (resumeOnBroadcast != null && resumeOnBroadcast) {
        event.getResource().resume();
      }
    }
  }
 /**
  * Delegate the request processing to an instance of {@link org.atmosphere.cpr.AsyncSupport}
  *
  * @param req the {@link javax.servlet.http.HttpServletRequest}
  * @param res the {@link javax.servlet.http.HttpServletResponse}
  * @throws java.io.IOException
  * @throws javax.servlet.ServletException
  */
 @Override
 public void doPost(HttpServletRequest req, HttpServletResponse res)
     throws IOException, ServletException {
   framework.doCometSupport(AtmosphereRequest.wrap(req), AtmosphereResponse.wrap(res));
 }
Example #26
0
    @Override
    public void onStateChange(AtmosphereResourceEvent event) throws IOException {

      AtmosphereResponse response = event.getResource().getResponse();
      AtmosphereResource resource = event.getResource();

      if (event.isSuspended()) {

        // Set content type before do response.getWriter()
        // http://docs.oracle.com/javaee/5/api/javax/servlet/ServletResponse.html#setContentType(java.lang.String)
        response.setContentType("text/plain; charset=UTF-8");
        response.setCharacterEncoding("UTF-8");

        if (event.getMessage().getClass().isArray()) {

          LOG.fine("SEND MESSAGE ARRAY " + event.getMessage().toString());

          List<Object> list = Arrays.asList(event.getMessage());

          response.getOutputStream().write(MSG_SEPARATOR.getBytes(MSG_CHARSET));
          for (Object object : list) {
            String message = (String) object;
            message += MSG_SEPARATOR;
            response.getOutputStream().write(message.getBytes(MSG_CHARSET));
          }

        } else if (event.getMessage() instanceof List) {

          LOG.fine("SEND MESSAGE LIST " + event.getMessage().toString());

          @SuppressWarnings("unchecked")
          List<Object> list = List.class.cast(event.getMessage());

          response.getOutputStream().write(MSG_SEPARATOR.getBytes(MSG_CHARSET));
          for (Object object : list) {
            String message = (String) object;
            message += MSG_SEPARATOR;
            response.getOutputStream().write(message.getBytes(MSG_CHARSET));
          }

        } else if (event.getMessage() instanceof String) {

          LOG.fine("SEND MESSAGE " + event.getMessage().toString());

          String message = (String) event.getMessage();
          response.getOutputStream().write(message.getBytes(MSG_CHARSET));
        }

        try {

          response.flushBuffer();

          switch (resource.transport()) {
            case JSONP:
            case LONG_POLLING:
              event.getResource().resume();
              break;
            case WEBSOCKET:
            case STREAMING:
            case SSE:
              response.getOutputStream().flush();
              break;
            default:
              LOG.info("Unknown transport");
              break;
          }
        } catch (IOException e) {
          LOG.info("Error resuming resource response", e);
        }

      } else if (event.isResuming()) {

        LOG.fine("RESUMING");

      } else if (event.isResumedOnTimeout()) {

        LOG.fine("RESUMED ON TIMEOUT");

      } else if (event.isClosedByApplication() || event.isClosedByClient()) {

        LOG.fine("CONNECTION CLOSED");

        AtmosphereResourceSession resourceSession =
            AtmosphereResourceSessionFactory.getDefault().getSession(resource);

        AtmosphereChannel resourceChannel =
            resourceSession.getAttribute(WAVE_CHANNEL_ATTRIBUTE, AtmosphereChannel.class);

        if (resourceChannel != null) {
          resourceChannel.onDisconnect();
        }
      }
    }
  /**
   * All proprietary Comet based {@link Servlet} must invoke the timedout method when the underlying
   * WebServer time out the {@link AtmosphereResponse}. The returned value, of type {@link Action},
   * tells the proprietary Comet {@link Servlet} to resume (again), suspended or do nothing with the
   * current {@link AtmosphereResponse}.
   *
   * @param request the {@link AtmosphereRequest}
   * @param response the {@link AtmosphereResponse}
   * @return action the Action operation.
   * @throws java.io.IOException
   * @throws javax.servlet.ServletException
   */
  public Action timedout(AtmosphereRequest request, AtmosphereResponse response)
      throws IOException, ServletException {

    AtmosphereResourceImpl r = null;
    try {
      if (trackActiveRequest) {
        long l = (Long) request.getAttribute(MAX_INACTIVE);
        if (l == -1) {
          // The closedDetector closed the connection.
          return timedoutAction;
        }
        request.setAttribute(MAX_INACTIVE, (long) -1);
      }

      logger.debug("Timing out the connection for request {}", request);

      // Something went wrong.
      if (request == null || response == null) {
        logger.warn("Invalid Request/Response: {}/{}", request, response);
        return timedoutAction;
      }

      r = (AtmosphereResourceImpl) request.getAttribute(FrameworkConfig.ATMOSPHERE_RESOURCE);

      if (r != null && r.getAtmosphereResourceEvent().isSuspended()) {
        r.getAtmosphereResourceEvent().setIsResumedOnTimeout(true);

        Broadcaster b = r.getBroadcaster();
        if (b instanceof DefaultBroadcaster) {
          ((DefaultBroadcaster) b).broadcastOnResume(r);
        }

        if (request.getAttribute(ApplicationConfig.RESUMED_ON_TIMEOUT) != null) {
          r.getAtmosphereResourceEvent()
              .setIsResumedOnTimeout(
                  (Boolean) request.getAttribute(ApplicationConfig.RESUMED_ON_TIMEOUT));
        }

        invokeAtmosphereHandler(r);
      }
    } catch (Throwable t) {
      logger.error("failed to timeout resource {}", r, t);
    } finally {
      try {
        if (r != null) {
          r.notifyListeners();
          r.setIsInScope(false);
          r.cancel();
        }
      } catch (Throwable t) {
        logger.trace("timedout", t);
      } finally {

        try {
          response.getOutputStream().close();
        } catch (Throwable t) {
          try {
            response.getWriter().close();
          } catch (Throwable t2) {
          }
        }

        if (r != null) {
          destroyResource(r);
        }
      }
    }

    return timedoutAction;
  }
  @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;
  }
  /**
   * 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();
  }