/** * All proprietary Comet based {@link Servlet} must invoke the cancelled method when the * underlying WebServer detect that the client closed the connection. * * @param req the {@link AtmosphereRequest} * @param res the {@link AtmosphereResponse} * @return action the Action operation. * @throws java.io.IOException * @throws javax.servlet.ServletException */ public Action cancelled(AtmosphereRequest req, AtmosphereResponse res) throws IOException, ServletException { synchronized (req) { AtmosphereResourceImpl r = null; try { if (trackActiveRequest) { long l = (Long) req.getAttribute(MAX_INACTIVE); if (l == -1) { // The closedDetector closed the connection. return timedoutAction; } req.setAttribute(MAX_INACTIVE, (long) -1); } logger.debug("Cancelling the connection for request {}", req); r = (AtmosphereResourceImpl) req.getAttribute(FrameworkConfig.ATMOSPHERE_RESOURCE); if (r != null) { r.getAtmosphereResourceEvent().setCancelled(true); invokeAtmosphereHandler(r); try { r.getResponse().sendError(503); r.getResponse().getOutputStream().close(); } catch (Throwable t) { try { r.getResponse().getWriter().close(); } catch (Throwable t2) { } } } } catch (Throwable ex) { // Something wrong happenned, ignore the exception logger.debug("failed to cancel resource: " + r, ex); } finally { try { if (r != null) { r.notifyListeners(); r.setIsInScope(false); r.cancel(); } } catch (Throwable t) { logger.trace("cancel", t); } finally { if (r != null) { destroyResource(r); } } } } return cancelledAction; }
@Override public void close() throws IOException { cancel(); event.setCloseByApplication(true); notifyListeners(); }
@Override public AtmosphereResource notifyListeners() { notifyListeners(event); return this; }
@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; }
/** * 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; }