public static final void broadcast( final AtmosphereResource r, final AtmosphereResourceEvent e, final Broadcaster broadcaster) { AtmosphereRequest request = r.getRequest(); ContainerResponse cr = null; // Make sure only one thread can play with the ContainerResponse. Threading issue can arise if // there is a scheduler // or if ContainerResponse is associated with more than Broadcaster. cr = (ContainerResponse) request.getAttribute(FrameworkConfig.CONTAINER_RESPONSE); if (cr == null || !r.isSuspended() && !r.getAtmosphereResourceEvent().isResumedOnTimeout()) { if (cr == null) { logger.warn( "Unexpected state. ContainerResponse has been resumed. Caching message {} for {}", e.getMessage(), r.uuid()); } else { logger.warn("The AtmosphereResource {} hasn't been suspended yet.", r.uuid(), e); } if (DefaultBroadcaster.class.isAssignableFrom(broadcaster.getClass())) { DefaultBroadcaster.class.cast(broadcaster).cacheLostMessage(r, true); } AtmosphereResourceImpl.class.cast(r)._destroy(); return; } synchronized (cr) { try { // This is required when you change the response's type String m = null; if (request.getAttribute(FrameworkConfig.EXPECTED_CONTENT_TYPE) != null) { m = (String) request.getAttribute(FrameworkConfig.EXPECTED_CONTENT_TYPE); } if (m == null || m.equalsIgnoreCase("text/event-stream")) { if (cr.getHttpHeaders().getFirst(HttpHeaders.CONTENT_TYPE) != null) { m = cr.getHttpHeaders().getFirst(HttpHeaders.CONTENT_TYPE).toString(); } if (m == null || m.equalsIgnoreCase("application/octet-stream")) { m = r.getAtmosphereConfig().getInitParameter(ApplicationConfig.SSE_CONTENT_TYPE); if (m == null) { m = "text/plain"; } } } if (e.getMessage() instanceof Response) { cr.setResponse((Response) e.getMessage()); cr.getHttpHeaders().add(HttpHeaders.CONTENT_TYPE, m); cr.write(); try { cr.getOutputStream().flush(); } catch (IOException ex) { logger.trace("", ex); } } else if (e.getMessage() instanceof List) { for (Object msg : (List<Object>) e.getMessage()) { cr.setResponse(Response.ok(msg).build()); cr.getHttpHeaders().add(HttpHeaders.CONTENT_TYPE, m); cr.write(); } // https://github.com/Atmosphere/atmosphere/issues/169 try { cr.getOutputStream().flush(); } catch (IOException ex) { logger.trace("", ex); } } else { if (e.getMessage() == null) { logger.warn("Broadcasted message is null"); return; } cr.setResponse(Response.ok(e.getMessage()).build()); cr.getHttpHeaders().add(HttpHeaders.CONTENT_TYPE, m); cr.write(); try { cr.getOutputStream().flush(); } catch (IOException ex) { logger.trace("", ex); } } } catch (Throwable t) { boolean notifyAndCache = true; logger.trace( "Unexpected exception for AtmosphereResource {} and Broadcaster {}", r.uuid(), broadcaster.getID()); if (isJetty(r)) { for (StackTraceElement element : t.getStackTrace()) { if (element.getClassName().equals("java.io.BufferedWriter") && element.getMethodName().equals("flush")) { logger.trace("Workaround issue https://github.com/Atmosphere/atmosphere/issues/710"); notifyAndCache = false; } } } if (DefaultBroadcaster.class.isAssignableFrom(broadcaster.getClass())) { DefaultBroadcaster.class.cast(broadcaster).onException(t, r, notifyAndCache); } else { onException(t, r); } } finally { if (cr != null) { cr.setEntity(null); } Boolean resumeOnBroadcast = (Boolean) request.getAttribute(ApplicationConfig.RESUME_ON_BROADCAST); if (resumeOnBroadcast != null && resumeOnBroadcast) { String uuid = (String) request.getAttribute(AtmosphereFilter.RESUME_UUID); if (uuid != null) { if (request.getAttribute(AtmosphereFilter.RESUME_CANDIDATES) != null) { ((ConcurrentHashMap<String, AtmosphereResource>) request.getAttribute(AtmosphereFilter.RESUME_CANDIDATES)) .remove(uuid); } } r.getRequest().setAttribute(FrameworkConfig.CONTAINER_RESPONSE, null); r.resume(); } } } }