org.atmosphere.cpr.AtmosphereResourceImpl Maven / Gradle / Ivy
/*
* Copyright 2015 Async-IO.org
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.atmosphere.cpr;
import org.atmosphere.HeartbeatAtmosphereResourceEvent;
import org.atmosphere.interceptor.AllowInterceptor;
import org.atmosphere.util.Utils;
import org.atmosphere.websocket.WebSocket;
import org.atmosphere.websocket.WebSocketEventListener;
import com.vaadin.external.org.slf4j.Logger;
import com.vaadin.external.org.slf4j.LoggerFactory;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Enumeration;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.atmosphere.cpr.ApplicationConfig.SUSPENDED_ATMOSPHERE_RESOURCE_UUID;
import static org.atmosphere.cpr.AtmosphereResource.TRANSPORT.UNDEFINED;
import static org.atmosphere.cpr.Broadcaster.ROOT_MASTER;
import static org.atmosphere.cpr.HeaderConfig.LONG_POLLING_TRANSPORT;
import static org.atmosphere.cpr.HeaderConfig.WEBSOCKET_UPGRADE;
import static org.atmosphere.cpr.HeaderConfig.X_ATMOSPHERE_ERROR;
import static org.atmosphere.cpr.HeaderConfig.X_ATMOSPHERE_TRANSPORT;
import static org.atmosphere.websocket.WebSocketEventListener.WebSocketEvent.TYPE.CLOSE;
/**
* {@link AtmosphereResource} implementation for supporting {@link AtmosphereRequest}
* and {@link AtmosphereResponse}.
*
* @author Jeanfrancois Arcand
*/
public class AtmosphereResourceImpl implements AtmosphereResource {
private static final Logger logger = LoggerFactory.getLogger(AtmosphereResourceImpl.class);
public static final String PRE_SUSPEND = AtmosphereResourceImpl.class.getName() + ".preSuspend";
public static final String SKIP_BROADCASTER_CREATION = AtmosphereResourceImpl.class.getName() + ".skipBroadcasterCreation";
public static final String METEOR = Meteor.class.getName();
private AtmosphereRequest req;
private AtmosphereResponse response;
private final Action action = new Action();
protected Broadcaster broadcaster;
private AtmosphereConfig config;
protected AsyncSupport asyncSupport;
private Serializer serializer;
private final AtomicBoolean isInScope = new AtomicBoolean(true);
private AtmosphereResourceEventImpl event;
private final AtomicBoolean isResumed = new AtomicBoolean();
private final AtomicBoolean isCancelled = new AtomicBoolean();
private final AtomicBoolean resumeOnBroadcast = new AtomicBoolean();
private Object writeOnTimeout = null;
private boolean disableSuspend = false;
private final AtomicBoolean disconnected = new AtomicBoolean();
private final ConcurrentLinkedQueue listeners =
new ConcurrentLinkedQueue();
private final AtomicBoolean isSuspendEvent = new AtomicBoolean();
private AtmosphereHandler atmosphereHandler;
private String uuid;
protected HttpSession session;
private boolean disableSuspendEvent;
private TRANSPORT transport;
private boolean forceBinaryWrite;
private final AtomicBoolean suspended = new AtomicBoolean();
private WebSocket webSocket;
private final AtomicBoolean inClosingPhase = new AtomicBoolean();
private boolean closeOnCancel = false;
public AtmosphereResourceImpl() {
}
@Deprecated
public AtmosphereResourceImpl(AtmosphereConfig config, Broadcaster broadcaster,
AtmosphereRequest req, AtmosphereResponse response,
AsyncSupport asyncSupport, AtmosphereHandler atmosphereHandler) {
initialize(config, broadcaster, req, response, asyncSupport, atmosphereHandler);
}
/**
* 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);
if (s == null && req != null) {
String tmp = req.getHeader(HeaderConfig.X_ATMOSPHERE_TRACKING_ID);
s = tmp != null && !tmp.equalsIgnoreCase("0") ? tmp : null;
}
}
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();
closeOnCancel = config.getInitParameter(ApplicationConfig.CLOSE_STREAM_ON_CANCEL, false);
return this;
}
protected void register() {
if (!Utils.pollableTransport(transport()) && !Utils.webSocketMessage(this)) {
config.resourcesFactory().registerUuidForFindCandidate(this);
}
}
private TRANSPORT configureTransport() {
if (req == null) return UNDEFINED;
String s = req.getHeader(HeaderConfig.X_ATMOSPHERE_TRANSPORT);
if (s == null) return UNDEFINED;
if (s.equals(UNDEFINED.name()) && Utils.rawWebSocket(req)) {
return TRANSPORT.WEBSOCKET;
}
s = s.replace("-", "_").toUpperCase();
if (TRANSPORT.POLLING.name().equals(s)) {
return TRANSPORT.POLLING;
} else if (TRANSPORT.LONG_POLLING.name().equals(s)) {
return TRANSPORT.LONG_POLLING;
} else if (TRANSPORT.STREAMING.name().equals(s)) {
return TRANSPORT.STREAMING;
} else if (TRANSPORT.JSONP.name().equals(s)) {
return TRANSPORT.JSONP;
} else if (TRANSPORT.WEBSOCKET.name().equals(s)) {
return TRANSPORT.WEBSOCKET;
} else if (TRANSPORT.SSE.name().equals(s)) {
return TRANSPORT.SSE;
} else if (TRANSPORT.AJAX.name().equals(s)) {
return TRANSPORT.AJAX;
} else if (TRANSPORT.CLOSE.name().equals(s)) {
return TRANSPORT.CLOSE;
} else {
return UNDEFINED;
}
}
@Override
public AtmosphereResourceEventImpl getAtmosphereResourceEvent() {
return event;
}
@Override
public AtmosphereHandler getAtmosphereHandler() {
return atmosphereHandler;
}
@Override
public AtmosphereResource writeOnTimeout(Object o) {
writeOnTimeout = o;
return this;
}
@Override
public Object writeOnTimeout() {
return writeOnTimeout;
}
@Override
public String uuid() {
return uuid;
}
@Override
public TRANSPORT transport() {
return transport;
}
/**
* Manually set the {@link TRANSPORT}
*
* @param transport set the {@link TRANSPORT}
* @return
*/
public AtmosphereResourceImpl transport(TRANSPORT transport) {
this.transport = transport;
return this;
}
@Override
public AtmosphereResource resumeOnBroadcast(boolean resumeOnBroadcast) {
this.resumeOnBroadcast.set(resumeOnBroadcast);
// For legacy reason
req.setAttribute(ApplicationConfig.RESUME_ON_BROADCAST, resumeOnBroadcast);
return this;
}
@Override
public boolean isSuspended() {
return suspended.get();
}
@Override
public boolean resumeOnBroadcast() {
boolean rob = resumeOnBroadcast.get();
if (!rob) {
Boolean b = (Boolean) req.getAttribute(ApplicationConfig.RESUME_ON_BROADCAST);
return b == null ? false : b;
}
return rob;
}
@Override
public AtmosphereResource resume() {
if (!isSuspended()) {
logger.trace("AtmosphereResource {} not suspended {}, cannot resume it.", uuid(), action);
return this;
}
try {
if (!isResumed.getAndSet(true) && isInScope.get()) {
suspended.set(false);
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);
}
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);
} finally {
unregister();
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);
}
}
listeners.clear();
return this;
}
@Override
public AtmosphereResource suspend() {
return suspend(-1);
}
@Override
public AtmosphereResource suspend(long timeout, TimeUnit timeunit) {
long timeoutms = -1;
if (timeunit != null) {
timeoutms = TimeUnit.MILLISECONDS.convert(timeout, timeunit);
}
return suspend(timeoutms);
}
@Override
public AtmosphereResource suspend(long timeout) {
if (event.isSuspended() || disableSuspend) return this;
if (config.isSupportSession()
&& req.getSession(false) != null
&& req.getSession().getMaxInactiveInterval() >= 0
&& 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 (Utils.resumableTransport(transport())) {
resumeOnBroadcast.set(true);
}
onPreSuspend(event);
// Recheck based on preSuspend
if (event.isSuspended() || disableSuspend) return this;
if (!event.isResumedOnTimeout()) {
suspended.set(true);
Enumeration 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 || transport().equals(UNDEFINED)) {
req.setAttribute(FrameworkConfig.TRANSPORT_IN_USE, 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() : ROOT_MASTER;
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 && Utils.resumableTransport(transport())) {
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);
register();
notifyListeners();
}
return this;
}
public AtmosphereRequest getRequest(boolean enforceScope) {
if (enforceScope && !isInScope.get()) {
throw new IllegalStateException("Request object no longer" + " valid. This object has been cancelled");
}
return req;
}
public AtmosphereResponse getResponse(boolean enforceScope) {
if (enforceScope && !isInScope.get()) {
throw new IllegalStateException("Response object no longer valid. This object has been cancelled");
}
return response;
}
@Override
public AtmosphereRequest getRequest() {
return getRequest(true);
}
@Override
public AtmosphereResponse getResponse() {
return getResponse(true);
}
@Override
public Broadcaster getBroadcaster() {
return getBroadcaster(true);
}
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;
}
@Override
public AtmosphereResourceImpl setBroadcaster(Broadcaster broadcaster) {
this.broadcaster = broadcaster;
return this;
}
@Override
public AtmosphereConfig getAtmosphereConfig() {
return config;
}
/**
* Return the {@link Action} which represent the state of the response, e.g. suspended, resumed or timed out.
*
* @return the {@link Action}
*/
public Action action() {
return action;
}
/**
* Completely reset the instance to its initial state.
*/
public void reset() {
isResumed.set(false);
isCancelled.set(false);
isInScope.set(true);
isSuspendEvent.set(false);
listeners.clear();
action.type(Action.TYPE.CREATED);
}
/**
* Protect the object from being used after it got cancelled.
*
* @param isInScope
*/
public void setIsInScope(boolean isInScope) {
this.isInScope.set(isInScope);
}
/**
* Check if the {@link AtmosphereRequest} still is valid.
*
* @return true if the {@link AtmosphereRequest} still is valid
*/
public boolean isInScope() {
return isInScope.get();
}
/**
* Set the {@link Serializer} used to write broadcasted objects.
*
* @param s
*/
@Override
public AtmosphereResource setSerializer(Serializer s) {
serializer = s;
return this;
}
@Override
public boolean isResumed() {
return isResumed.get();
}
@Override
public boolean isCancelled() {
return isCancelled.get();
}
@Override
public AtmosphereResource write(String s) {
response.write(s);
if (resumeOnBroadcast()) {
resume();
}
return this;
}
@Override
public AtmosphereResource write(byte[] o) {
response.write(o);
if (resumeOnBroadcast()) {
resume();
}
return this;
}
@Override
public Serializer getSerializer() {
return serializer;
}
/**
* Add a {@link AtmosphereResourceEventListener}.
*
* @param e an instance of AtmosphereResourceEventListener
*/
@Override
public AtmosphereResource addEventListener(AtmosphereResourceEventListener e) {
if (listeners.contains(e)) return this;
listeners.add(e);
return this;
}
@Override
public AtmosphereResource removeEventListener(AtmosphereResourceEventListener e) {
listeners.remove(e);
return this;
}
@Override
public AtmosphereResource removeEventListeners() {
listeners.clear();
return this;
}
@Override
public AtmosphereResource notifyListeners() {
notifyListeners(event);
return this;
}
@Override
public AtmosphereResource notifyListeners(AtmosphereResourceEvent event) {
if (listeners.isEmpty()) {
logger.trace("No listener with {}", uuid);
return this;
}
logger.trace("Invoking listener {} for {}", listeners, uuid);
Action oldAction = action;
try {
if (HeartbeatAtmosphereResourceEvent.class.isAssignableFrom(event.getClass())) {
onHeartbeat(event);
} else if (event.isClosedByApplication()) {
onClose(event);
} else if (event.isCancelled() || event.isClosedByClient()) {
if (!disconnected.getAndSet(true)) {
onDisconnect(event);
} else {
logger.trace("Skipping notification, already disconnected {}", event.getResource().uuid());
}
} else if (event.isResuming() || event.isResumedOnTimeout()) {
onResume(event);
} else if (!isSuspendEvent.getAndSet(true) && event.isSuspended()) {
onSuspend(event);
} else if (event.throwable() != null) {
onThrowable(event);
} else {
onBroadcast(event);
}
if (oldAction.type() != action.type()) {
action().type(Action.TYPE.CREATED);
}
} catch (Throwable t) {
AtmosphereResourceEventImpl.class.cast(event).setThrowable(t);
if (event.isSuspended()) {
logger.warn("Exception during suspend() operation {}", t.toString());
logger.debug("", t);
broadcaster.removeAtmosphereResource(this);
if (config.getBroadcasterFactory() != null) {
config.getBroadcasterFactory().removeAllAtmosphereResource(this);
}
} else {
logger.debug("Listener error {}", t);
}
try {
onThrowable(event);
} catch (Throwable t2) {
logger.warn("Listener error {}", t2);
}
}
return this;
}
/**
* Notify {@link AtmosphereResourceEventListener} thah an unexpected exception occured.
*
* @pram a {@link Throwable}
*/
public void onThrowable(Throwable t) {
onThrowable(new AtmosphereResourceEventImpl(this, false, false, t));
}
void onThrowable(AtmosphereResourceEvent e) {
for (AtmosphereResourceEventListener r : listeners) {
r.onThrowable(e);
}
}
/**
*
* Notifies to all listeners that a heartbeat has been sent.
*
*
* @param e the event
*/
void onHeartbeat(AtmosphereResourceEvent e) {
for (AtmosphereResourceEventListener r : listeners) {
r.onHeartbeat(e);
}
}
void onSuspend(AtmosphereResourceEvent e) {
for (AtmosphereResourceEventListener r : listeners) {
if (disableSuspendEvent) {
if (!AllowInterceptor.class.isAssignableFrom(r.getClass())) {
continue;
}
}
r.onSuspend(e);
}
}
void onPreSuspend(AtmosphereResourceEvent e) {
for (AtmosphereResourceEventListener r : listeners) {
if (disableSuspendEvent) {
if (!AllowInterceptor.class.isAssignableFrom(r.getClass())) {
continue;
}
}
r.onPreSuspend(e);
}
}
void onResume(AtmosphereResourceEvent e) {
for (AtmosphereResourceEventListener r : listeners) {
r.onResume(e);
}
}
void onDisconnect(AtmosphereResourceEvent e) {
for (AtmosphereResourceEventListener r : listeners) {
r.onDisconnect(e);
if (transport.equals(TRANSPORT.WEBSOCKET) && WebSocketEventListener.class.isAssignableFrom(r.getClass())) {
WebSocketEventListener.class.cast(r).onDisconnect(new WebSocketEventListener.WebSocketEvent(1005, CLOSE, webSocket));
}
}
}
void onBroadcast(AtmosphereResourceEvent e) {
for (AtmosphereResourceEventListener r : listeners) {
r.onBroadcast(e);
}
}
void onClose(AtmosphereResourceEvent e) {
for (AtmosphereResourceEventListener r : listeners) {
r.onClose(e);
if (transport.equals(TRANSPORT.WEBSOCKET) && WebSocketEventListener.class.isAssignableFrom(r.getClass())) {
WebSocketEventListener.class.cast(r).onDisconnect(new WebSocketEventListener.WebSocketEvent(1005, CLOSE, webSocket));
}
}
}
public ConcurrentLinkedQueue atmosphereResourceEventListener() {
return listeners;
}
public AtmosphereResourceImpl atmosphereHandler(AtmosphereHandler atmosphereHandler) {
this.atmosphereHandler = atmosphereHandler;
return this;
}
public void cancel() throws IOException {
try {
if (!isCancelled.getAndSet(true)) {
suspended.set(false);
logger.trace("Cancelling {}", uuid);
if (config.getBroadcasterFactory() != null) {
config.getBroadcasterFactory().removeAllAtmosphereResource(this);
if (transport.equals(TRANSPORT.WEBSOCKET)) {
String parentUUID = (String) req.getAttribute(SUSPENDED_ATMOSPHERE_RESOURCE_UUID);
AtmosphereResource p = config.resourcesFactory().find(parentUUID);
if (p != null) {
config.getBroadcasterFactory().removeAllAtmosphereResource(p);
}
}
}
asyncSupport.complete(this);
try {
Meteor m = (Meteor) req.getAttribute(AtmosphereResourceImpl.METEOR);
if (m != null) {
m.destroy();
}
} catch (Exception ex) {
logger.trace("Meteor exception {}", ex);
}
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())) {
if (closeOnCancel) {
AtmosphereResponse.class.cast(response).close();
}
AtmosphereResponse.class.cast(response).destroy();
}
if (AtmosphereRequest.class.isAssignableFrom(req.getClass())) {
if (closeOnCancel) {
AtmosphereRequest.class.cast(req).destroy();
}
}
event.destroy();
}
} finally {
unregister();
}
}
private void unregister() {
config.resourcesFactory().remove(uuid);
}
public void _destroy() {
try {
if (!isCancelled.get()) {
if (broadcaster != null) broadcaster.removeAtmosphereResource(this);
if (config.getBroadcasterFactory() != null) {
config.getBroadcasterFactory().removeAllAtmosphereResource(this);
}
}
unregister();
removeEventListeners();
} catch (Throwable t) {
logger.trace("destroyResource", t);
} finally {
unregister();
webSocket = null;
}
}
@Override
public String toString() {
try {
return "AtmosphereResource{" +
"\n\t uuid=" + uuid +
",\n\t transport=" + transport() +
",\n\t isInScope=" + isInScope +
",\n\t isResumed=" + isResumed() +
",\n\t isCancelled=" + isCancelled() +
",\n\t isSuspended=" + isSuspended() +
",\n\t broadcaster=" + broadcaster.getID() + " size: " + broadcaster.getAtmosphereResources().size() +
",\n\t isClosedByClient=" + (event != null ? event.isClosedByClient() : false) +
",\n\t isClosedByApplication=" + (event != null ? event.isClosedByApplication() : false) +
",\n\t action=" + action +
'}';
} catch (NullPointerException ex) {
// Prevent logger
return "AtmosphereResourceImpl{" + uuid + "}";
}
}
public AtmosphereResourceImpl disableSuspend(boolean disableSuspend) {
this.disableSuspend = disableSuspend;
return this;
}
@Override
public HttpSession session(boolean create) {
if (config.isSupportSession() && session == null) {
// http://java.net/jira/browse/GLASSFISH-18856
session = req.getSession(create);
}
return session;
}
@Override
public void close() throws IOException {
event.setCloseByApplication(true);
notifyListeners();
cancel();
if (webSocket != null) {
webSocket.close();
}
}
public void dirtyClose() {
try {
event.setCancelled(true);
notifyListeners();
cancel();
if (webSocket != null) {
webSocket.close();
}
} catch (IOException ex) {
logger.trace("", ex);
}
}
@Override
public AtmosphereResource forceBinaryWrite(boolean forceBinaryWrite) {
this.forceBinaryWrite = forceBinaryWrite;
return this;
}
@Override
public boolean forceBinaryWrite() {
return forceBinaryWrite;
}
@Override
public HttpSession session() {
return session(true);
}
public AtmosphereResourceImpl session(HttpSession session) {
this.session = session;
return this;
}
public AtmosphereResourceImpl cloneState(AtmosphereResource r) {
for (AtmosphereResourceEventListener l : AtmosphereResourceImpl.class.cast(r).atmosphereResourceEventListener()) {
addEventListener(l);
}
AtmosphereResourceImpl.class.cast(r).session(r.session());
setBroadcaster(r.getBroadcaster());
atmosphereHandler(r.getAtmosphereHandler());
return this;
}
public ConcurrentLinkedQueue listeners() {
return listeners;
}
/**
* Disable invocation of {@link AtmosphereResourceEventListener#onSuspend(AtmosphereResourceEvent)} and
* {@link AtmosphereResourceEventListener#onPreSuspend(AtmosphereResourceEvent)}. You normally disable those events
* after the first onSuspend has been called so all transports behave the same way.
*
* {@link AtmosphereResourceEventListener} marked with {@link org.atmosphere.interceptor.AllowInterceptor} will not
* be affected by this property.
*
* @param disableSuspendEvent
* @return this
*/
public AtmosphereResourceImpl disableSuspendEvent(boolean disableSuspendEvent) {
this.disableSuspendEvent = disableSuspendEvent;
return this;
}
/**
* Return true if {@link AtmosphereResourceEventListener#onSuspend(AtmosphereResourceEvent)} and
* {@link AtmosphereResourceEventListener#onPreSuspend(AtmosphereResourceEvent)} events are disabled.
*
* @return true if disabled
*/
public boolean disableSuspendEvent() {
return disableSuspendEvent;
}
public WebSocket webSocket() {
return webSocket;
}
public AtmosphereResourceImpl webSocket(WebSocket webSocket) {
this.webSocket = webSocket;
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AtmosphereResourceImpl that = (AtmosphereResourceImpl) o;
if (!uuid.equals(that.uuid)) return false;
return true;
}
public boolean getAndSetInClosingPhase(){
return inClosingPhase.getAndSet(true);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy