org.atmosphere.cpr.AtmosphereFramework Maven / Gradle / Ivy
/*
* Copyright 2014 Jeanfrancois Arcand
*
* 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.annotation.Processor;
import org.atmosphere.cache.BroadcasterCacheInspector;
import org.atmosphere.cache.DefaultBroadcasterCache;
import org.atmosphere.cache.UUIDBroadcasterCache;
import org.atmosphere.config.ApplicationConfiguration;
import org.atmosphere.config.AtmosphereHandlerConfig;
import org.atmosphere.config.AtmosphereHandlerProperty;
import org.atmosphere.config.FrameworkConfiguration;
import org.atmosphere.container.BlockingIOCometSupport;
import org.atmosphere.container.Tomcat7BIOSupportWithWebSocket;
import org.atmosphere.container.WebLogicServlet30WithWebSocket;
import org.atmosphere.handler.AbstractReflectorAtmosphereHandler;
import org.atmosphere.handler.ReflectorServletProcessor;
import org.atmosphere.interceptor.AndroidAtmosphereInterceptor;
import org.atmosphere.interceptor.CacheHeadersInterceptor;
import org.atmosphere.interceptor.CorsInterceptor;
import org.atmosphere.interceptor.HeartbeatInterceptor;
import org.atmosphere.interceptor.IdleResourceInterceptor;
import org.atmosphere.interceptor.InvokationOrder;
import org.atmosphere.interceptor.JSONPAtmosphereInterceptor;
import org.atmosphere.interceptor.JavaScriptProtocol;
import org.atmosphere.interceptor.OnDisconnectInterceptor;
import org.atmosphere.interceptor.PaddingAtmosphereInterceptor;
import org.atmosphere.interceptor.SSEAtmosphereInterceptor;
import org.atmosphere.interceptor.WebSocketMessageSuspendInterceptor;
import org.atmosphere.util.AtmosphereConfigReader;
import org.atmosphere.util.DefaultEndpointMapper;
import org.atmosphere.util.EndpointMapper;
import org.atmosphere.util.IOUtils;
import org.atmosphere.util.IntrospectionUtils;
import org.atmosphere.util.ServletContextFactory;
import org.atmosphere.util.ServletProxyFactory;
import org.atmosphere.util.Version;
import org.atmosphere.util.analytics.FocusPoint;
import org.atmosphere.util.analytics.JGoogleAnalyticsTracker;
import org.atmosphere.util.analytics.ModuleDetection;
import org.atmosphere.websocket.DefaultWebSocketProcessor;
import org.atmosphere.websocket.WebSocket;
import org.atmosphere.websocket.WebSocketHandler;
import org.atmosphere.websocket.WebSocketProcessor;
import org.atmosphere.websocket.WebSocketProtocol;
import org.atmosphere.websocket.protocol.SimpleHttpProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.atmosphere.cpr.ApplicationConfig.ALLOW_QUERYSTRING_AS_REQUEST;
import static org.atmosphere.cpr.ApplicationConfig.ATMOSPHERE_HANDLER;
import static org.atmosphere.cpr.ApplicationConfig.ATMOSPHERE_HANDLER_MAPPING;
import static org.atmosphere.cpr.ApplicationConfig.ATMOSPHERE_HANDLER_PATH;
import static org.atmosphere.cpr.ApplicationConfig.BROADCASTER_CACHE;
import static org.atmosphere.cpr.ApplicationConfig.BROADCASTER_CLASS;
import static org.atmosphere.cpr.ApplicationConfig.BROADCASTER_FACTORY;
import static org.atmosphere.cpr.ApplicationConfig.BROADCASTER_LIFECYCLE_POLICY;
import static org.atmosphere.cpr.ApplicationConfig.BROADCASTER_WAIT_TIME;
import static org.atmosphere.cpr.ApplicationConfig.BROADCAST_FILTER_CLASSES;
import static org.atmosphere.cpr.ApplicationConfig.DISABLE_ONSTATE_EVENT;
import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_ALLOW_SESSION_TIMEOUT_REMOVAL;
import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_ATMOSPHERE_XML;
import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_BLOCKING_COMETSUPPORT;
import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_COMET_SUPPORT;
import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_NATIVE_COMETSUPPORT;
import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_SERVLET_MAPPING;
import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_SESSION_SUPPORT;
import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_THROW_EXCEPTION_ON_CLONED_REQUEST;
import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_USE_STREAM;
import static org.atmosphere.cpr.ApplicationConfig.SUSPENDED_ATMOSPHERE_RESOURCE_UUID;
import static org.atmosphere.cpr.ApplicationConfig.WEBSOCKET_PROCESSOR;
import static org.atmosphere.cpr.ApplicationConfig.WEBSOCKET_PROTOCOL;
import static org.atmosphere.cpr.ApplicationConfig.WEBSOCKET_SUPPORT;
import static org.atmosphere.cpr.Broadcaster.ROOT_MASTER;
import static org.atmosphere.cpr.FrameworkConfig.ATMOSPHERE_CONFIG;
import static org.atmosphere.cpr.FrameworkConfig.CDI_INJECTOR;
import static org.atmosphere.cpr.FrameworkConfig.GUICE_INJECTOR;
import static org.atmosphere.cpr.FrameworkConfig.HAZELCAST_BROADCASTER;
import static org.atmosphere.cpr.FrameworkConfig.JERSEY_BROADCASTER;
import static org.atmosphere.cpr.FrameworkConfig.JERSEY_CONTAINER;
import static org.atmosphere.cpr.FrameworkConfig.JGROUPS_BROADCASTER;
import static org.atmosphere.cpr.FrameworkConfig.JMS_BROADCASTER;
import static org.atmosphere.cpr.FrameworkConfig.RABBITMQ_BROADCASTER;
import static org.atmosphere.cpr.FrameworkConfig.REDIS_BROADCASTER;
import static org.atmosphere.cpr.FrameworkConfig.RMI_BROADCASTER;
import static org.atmosphere.cpr.FrameworkConfig.SPRING_INJECTOR;
import static org.atmosphere.cpr.FrameworkConfig.THROW_EXCEPTION_ON_CLONED_REQUEST;
import static org.atmosphere.cpr.FrameworkConfig.XMPP_BROADCASTER;
import static org.atmosphere.cpr.HeaderConfig.ATMOSPHERE_POST_BODY;
import static org.atmosphere.cpr.HeaderConfig.X_ATMOSPHERE_TRACKING_ID;
import static org.atmosphere.util.IOUtils.realPath;
import static org.atmosphere.websocket.WebSocket.WEBSOCKET_SUSPEND;
/**
* The {@link AtmosphereFramework} is the entry point for the framework. This class can be used to from Servlet/filter
* to dispatch {@link AtmosphereRequest} and {@link AtmosphereResponse}. The framework can also be configured using
* the setXXX method. The life cycle of this class is
*
* AtmosphereFramework f = new AtmosphereFramework();
* f.init();
* f.doCometSupport(AtmosphereRequest, AtmosphereResource);
* f.destroy();
*
*
* @author Jeanfrancois Arcand
*/
public class AtmosphereFramework {
public static final String DEFAULT_ATMOSPHERE_CONFIG_PATH = "/META-INF/atmosphere.xml";
public static final String DEFAULT_LIB_PATH = "/WEB-INF/lib/";
public static final String DEFAULT_HANDLER_PATH = "/WEB-INF/classes/";
public static final String MAPPING_REGEX = "[a-zA-Z0-9-&.*_~=@;\\?]+";
protected static final Logger logger = LoggerFactory.getLogger(AtmosphereFramework.class);
protected final List broadcasterFilters = new ArrayList();
protected final List asyncSupportListeners = new ArrayList();
protected final ArrayList possibleComponentsCandidate = new ArrayList();
protected final HashMap initParams = new HashMap();
protected final AtmosphereConfig config;
protected final AtomicBoolean isCometSupportConfigured = new AtomicBoolean(false);
protected final boolean isFilter;
protected final Map atmosphereHandlers = new ConcurrentHashMap();
protected final ConcurrentLinkedQueue broadcasterTypes = new ConcurrentLinkedQueue();
protected final ConcurrentLinkedQueue objectFactoryType = new ConcurrentLinkedQueue();
protected final ConcurrentLinkedQueue inspectors = new ConcurrentLinkedQueue();
protected String mappingRegex = MAPPING_REGEX;
protected boolean useNativeImplementation = false;
protected boolean useBlockingImplementation = false;
protected boolean useStreamForFlushingComments = true;
protected boolean useServlet30 = true;
protected AsyncSupport asyncSupport;
protected String broadcasterClassName = DefaultBroadcaster.class.getName();
protected boolean isCometSupportSpecified = false;
protected boolean isBroadcasterSpecified = false;
protected boolean isSessionSupportSpecified = false;
protected boolean isThrowExceptionOnClonedRequestSpecified = false;
protected BroadcasterFactory broadcasterFactory;
protected String broadcasterFactoryClassName;
protected String broadcasterCacheClassName;
protected boolean webSocketEnabled = true;
protected String broadcasterLifeCyclePolicy = "NEVER";
protected String webSocketProtocolClassName = SimpleHttpProtocol.class.getName();
protected WebSocketProtocol webSocketProtocol;
protected String handlersPath = DEFAULT_HANDLER_PATH;
protected ServletConfig servletConfig;
protected boolean autoDetectHandlers = true;
private boolean hasNewWebSocketProtocol = false;
protected String atmosphereDotXmlPath = DEFAULT_ATMOSPHERE_CONFIG_PATH;
protected final LinkedList interceptors = new LinkedList();
protected boolean scanDone = false;
protected String annotationProcessorClassName = "org.atmosphere.cpr.DefaultAnnotationProcessor";
protected final List broadcasterListeners = new ArrayList();
protected String webSocketProcessorClassName = DefaultWebSocketProcessor.class.getName();
protected boolean webSocketProtocolInitialized = false;
protected EndpointMapper endpointMapper = new DefaultEndpointMapper();
protected String libPath = DEFAULT_LIB_PATH;
protected boolean isInit;
protected boolean sharedThreadPools = true;
protected final List packages = new ArrayList();
protected final LinkedList annotationPackages = new LinkedList();
protected boolean allowAllClassesScan = true;
protected boolean annotationFound = false;
protected boolean executeFirstSet = false;
protected AtmosphereObjectFactory objectFactory = new DefaultAtmosphereObjectFactory();
protected final AtomicBoolean isDestroyed = new AtomicBoolean();
protected boolean externalizeDestroy = false;
protected AnnotationProcessor annotationProcessor = null;
protected final List excludedInterceptors = new ArrayList();
protected final LinkedList broadcasterCacheListeners = new LinkedList();
protected final List filterManipulators = new ArrayList();
protected AtmosphereResourceFactory arFactory;
protected MetaBroadcaster metaBroadcaster;
protected String defaultSerializerClassName;
protected Class defaultSerializerClass;
protected final Class extends AtmosphereInterceptor>[] defaultInterceptors = new Class[]{
// Add CORS support
CorsInterceptor.class,
// Default Interceptor
CacheHeadersInterceptor.class,
// WebKit & IE Padding
PaddingAtmosphereInterceptor.class,
// Android 2.3.x streaming support
AndroidAtmosphereInterceptor.class,
// Heartbeat
HeartbeatInterceptor.class,
// Add SSE support
SSEAtmosphereInterceptor.class,
// ADD JSONP support
JSONPAtmosphereInterceptor.class,
// ADD Tracking ID Handshake
JavaScriptProtocol.class,
// WebSocket and suspend
WebSocketMessageSuspendInterceptor.class,
// OnDisconnect
OnDisconnectInterceptor.class,
// Idle connection
IdleResourceInterceptor.class
};
/**
* An implementation of {@link AbstractReflectorAtmosphereHandler}.
*/
public final static AtmosphereHandler REFLECTOR_ATMOSPHEREHANDLER = new AbstractReflectorAtmosphereHandler() {
@Override
public void onRequest(AtmosphereResource resource) throws IOException {
logger.trace("VoidHandler", resource.uuid());
}
@Override
public void destroy() {
logger.trace("VoidHandler");
}
};
public static final class AtmosphereHandlerWrapper {
public final AtmosphereHandler atmosphereHandler;
public Broadcaster broadcaster;
public String mapping;
public LinkedList interceptors = new LinkedList();
public boolean create;
public AtmosphereHandlerWrapper(BroadcasterFactory broadcasterFactory, AtmosphereHandler atmosphereHandler, String mapping) {
this.atmosphereHandler = atmosphereHandler;
try {
if (broadcasterFactory != null) {
this.broadcaster = broadcasterFactory.lookup(mapping, true);
} else {
this.mapping = mapping;
}
} catch (Exception t) {
throw new RuntimeException(t);
}
}
public AtmosphereHandlerWrapper(AtmosphereHandler atmosphereHandler, Broadcaster broadcaster) {
this.atmosphereHandler = atmosphereHandler;
this.broadcaster = broadcaster;
}
@Override
public String toString() {
return "AtmosphereHandlerWrapper{ atmosphereHandler=" + atmosphereHandler + ", broadcaster=" +
broadcaster + " }";
}
}
/**
*
* This enumeration represents all possible actions to specify in a meta service file.
*
*
* @author Guillaume DROUET
* @version 1.0
* @since 2.2.0
*/
public static enum MetaServiceAction {
/**
* Install service.
*/
INSTALL(new InstallMetaServiceProcedure()),
/**
* Exclude service.
*/
EXCLUDE(new ExcludeMetaServiceProcedure());
/**
* The procedure to apply.
*/
private MetaServiceProcedure procedure;
/**
*
* Builds a new instance.
*
*
* @param p the enum procedure
*/
private MetaServiceAction(final MetaServiceProcedure p) {
procedure = p;
}
/**
*
* Applies this action to given class.
*
*
* @param fwk the framework
* @param clazz the class
* @throws Exception if procedure fails
*/
public void apply(final AtmosphereFramework fwk, final Class> clazz) throws Exception {
procedure.apply(fwk, clazz);
}
/**
*
* This interface defined a method with a signature like a procedure to process an action.
*
*
* @author Guillaume DROUET
* @version 1.0
* @since 2.2.0
*/
private static interface MetaServiceProcedure {
/**
*
* Processes an action.
*
*
* @param fwk the framework
* @param clazz the class to use during processing
* @throws Exception if procedure fails
*/
void apply(final AtmosphereFramework fwk, final Class> clazz) throws Exception;
}
/**
*
* Install the classes.
*
*
* @author Guillaume DROUET
* @version 1.0
* @since 2.2.0
*/
private static class InstallMetaServiceProcedure implements MetaServiceProcedure {
/**
* {@inheritDoc}
*/
@Override
public void apply(final AtmosphereFramework fwk, final Class c) throws Exception {
if (AtmosphereInterceptor.class.isAssignableFrom(c)) {
fwk.interceptor(fwk.newClassInstance(AtmosphereInterceptor.class, c));
} else if (Broadcaster.class.isAssignableFrom(c)) {
fwk.setDefaultBroadcasterClassName(c.getName());
} else if (BroadcasterListener.class.isAssignableFrom(c)) {
fwk.addBroadcasterListener(fwk.newClassInstance(BroadcasterListener.class, c));
} else if (BroadcasterCache.class.isAssignableFrom(c)) {
fwk.setBroadcasterCacheClassName(c.getName());
} else if (BroadcastFilter.class.isAssignableFrom(c)) {
fwk.broadcasterFilters.add(c.getName());
} else if (BroadcasterCacheInspector.class.isAssignableFrom(c)) {
fwk.inspectors.add(fwk.newClassInstance(BroadcasterCacheInspector.class, c));
} else if (AsyncSupportListener.class.isAssignableFrom(c)) {
fwk.asyncSupportListeners.add(fwk.newClassInstance(AsyncSupportListener.class, c));
} else if (AsyncSupport.class.isAssignableFrom(c)) {
fwk.setAsyncSupport(fwk.newClassInstance(AsyncSupport.class, c));
} else if (BroadcasterCacheListener.class.isAssignableFrom(c)) {
fwk.broadcasterCacheListeners.add(fwk.newClassInstance(BroadcasterCacheListener.class, c));
} else if (BroadcasterConfig.FilterManipulator.class.isAssignableFrom(c)) {
fwk.filterManipulators.add(fwk.newClassInstance(BroadcasterConfig.FilterManipulator.class, c));
} else if (WebSocketProtocol.class.isAssignableFrom(c)) {
fwk.webSocketProtocolClassName = WebSocketProtocol.class.getName();
} else if (WebSocketProcessor.class.isAssignableFrom(c)) {
fwk.webSocketProcessorClassName = WebSocketProcessor.class.getName();
} else {
logger.warn("{} is not a framework service that could be installed", c.getName());
}
}
}
/**
*
* Exclude the services.
*
*
* @author Guillaume DROUET
* @version 1.0
* @since 2.2.0
*/
private static class ExcludeMetaServiceProcedure implements MetaServiceProcedure {
/**
* {@inheritDoc}
*/
@Override
public void apply(final AtmosphereFramework fwk, final Class> c) {
if (AtmosphereInterceptor.class.isAssignableFrom(c)) {
fwk.excludeInterceptor(c.getName());
} else {
logger.warn("{} is not a framework service that could be excluded, pull request is welcome ;-)", c.getName());
}
}
}
}
public static class DefaultAtmosphereObjectFactory implements AtmosphereObjectFactory {
public String toString() {
return "DefaultAtmosphereObjectFactory";
}
@Override
public U newClassInstance(AtmosphereFramework framework,
Class classType,
Class defaultType) throws InstantiationException, IllegalAccessException {
return defaultType.newInstance();
}
}
/**
* Create an AtmosphereFramework.
*/
public AtmosphereFramework() {
this(false, true);
}
/**
* Create an AtmosphereFramework and initialize it via {@link AtmosphereFramework#init(javax.servlet.ServletConfig)}.
*/
public AtmosphereFramework(ServletConfig sc) throws ServletException {
this(false, true);
// TODO: What?
init(sc);
}
/**
* Create an AtmosphereFramework.
*
* @param isFilter true if this instance is used as an {@link AtmosphereFilter}
*/
public AtmosphereFramework(boolean isFilter, boolean autoDetectHandlers) {
this.isFilter = isFilter;
this.autoDetectHandlers = autoDetectHandlers;
config = new AtmosphereConfig(this);
}
/**
* The order of addition is quite important here.
*/
private void populateBroadcasterType() {
broadcasterTypes.add(HAZELCAST_BROADCASTER);
broadcasterTypes.add(XMPP_BROADCASTER);
broadcasterTypes.add(REDIS_BROADCASTER);
broadcasterTypes.add(JGROUPS_BROADCASTER);
broadcasterTypes.add(JMS_BROADCASTER);
broadcasterTypes.add(RMI_BROADCASTER);
broadcasterTypes.add(RABBITMQ_BROADCASTER);
}
/**
* The order of addition is quite important here.
*/
private void populateObjectFactoryType() {
objectFactoryType.add(CDI_INJECTOR);
objectFactoryType.add(SPRING_INJECTOR);
objectFactoryType.add(GUICE_INJECTOR);
}
/**
* Add an {@link AtmosphereHandler} serviced by the {@link Servlet}
* This API is exposed to allow embedding an Atmosphere application.
*
* @param mapping The servlet mapping (servlet path)
* @param h implementation of an {@link AtmosphereHandler}
* @param l An array of {@link AtmosphereInterceptor}.
*/
public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h, List l) {
if (!mapping.startsWith("/")) {
mapping = "/" + mapping;
}
AtmosphereHandlerWrapper w = new AtmosphereHandlerWrapper(broadcasterFactory, h, mapping);
w.interceptors = LinkedList.class.isAssignableFrom(l.getClass()) ? LinkedList.class.cast(l) : new LinkedList(l);
addMapping(mapping, w);
initServletProcessor(h);
if (isInit) {
initHandlerInterceptors(w.interceptors);
} else {
logger.info("Installed AtmosphereHandler {} mapped to context-path: {}", h.getClass().getName(), mapping);
logger.info("Installed the following AtmosphereInterceptor mapped to AtmosphereHandler {}", h.getClass().getName());
if (l.size() > 0) {
for (AtmosphereInterceptor s : l) {
logger.info("\t{} : {}", s.getClass().getName(), s);
}
}
}
return this;
}
/**
* Add an {@link AtmosphereHandler} serviced by the {@link Servlet}
* This API is exposed to allow embedding an Atmosphere application.
*
* @param mapping The servlet mapping (servlet path)
* @param h implementation of an {@link AtmosphereHandler}
*/
public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h) {
addAtmosphereHandler(mapping, h, Collections.emptyList());
return this;
}
private AtmosphereFramework addMapping(String path, AtmosphereHandlerWrapper w) {
// We are using JAXRS mapping algorithm.
if (path.contains("*")) {
path = path.replace("*", mappingRegex);
}
if (path.endsWith("/")) {
path = path + mappingRegex;
}
atmosphereHandlers.put(path, w);
return this;
}
/**
* Add an {@link AtmosphereHandler} serviced by the {@link Servlet}.
* This API is exposed to allow embedding an Atmosphere application.
*
* @param mapping The servlet mapping (servlet path)
* @param h implementation of an {@link AtmosphereHandler}
* @param broadcasterId The {@link Broadcaster#getID} value.
* @param l An attay of {@link AtmosphereInterceptor}
*/
public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h, String broadcasterId, List l) {
if (!mapping.startsWith("/")) {
mapping = "/" + mapping;
}
AtmosphereHandlerWrapper w = new AtmosphereHandlerWrapper(broadcasterFactory, h, mapping);
w.broadcaster.setID(broadcasterId);
w.interceptors = LinkedList.class.isAssignableFrom(l.getClass()) ? LinkedList.class.cast(l) : new LinkedList(l);
initServletProcessor(h);
addMapping(mapping, w);
logger.info("Installed AtmosphereHandler {} mapped to context-path: {}", h.getClass().getName(), mapping);
if (l.size() > 0) {
logger.info("Installed AtmosphereInterceptor {} mapped to AtmosphereHandler {}", l, h.getClass().getName());
}
return this;
}
/**
* Add an {@link AtmosphereHandler} serviced by the {@link Servlet}.
* This API is exposed to allow embedding an Atmosphere application.
*
* @param mapping The servlet mapping (servlet path)
* @param h implementation of an {@link AtmosphereHandler}
* @param broadcasterId The {@link Broadcaster#getID} value
*/
public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h, String broadcasterId) {
addAtmosphereHandler(mapping, h, broadcasterId, Collections.emptyList());
return this;
}
private void initServletProcessor(AtmosphereHandler h) {
if (!isInit) return;
try {
if (h instanceof AtmosphereServletProcessor) {
((AtmosphereServletProcessor) h).init(config);
}
} catch (ServletException e) {
throw new RuntimeException(e);
}
}
/**
* Add an {@link AtmosphereHandler} serviced by the {@link Servlet}.
* This API is exposed to allow embedding an Atmosphere application.
*
* @param mapping The servlet mapping (servlet path)
* @param h implementation of an {@link AtmosphereHandler}
* @param broadcaster The {@link Broadcaster} associated with AtmosphereHandler
* @param l A list of {@link AtmosphereInterceptor}s
*/
public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h, Broadcaster broadcaster, List l) {
if (!mapping.startsWith("/")) {
mapping = "/" + mapping;
}
AtmosphereHandlerWrapper w = new AtmosphereHandlerWrapper(h, broadcaster);
w.interceptors = LinkedList.class.isAssignableFrom(l.getClass()) ? LinkedList.class.cast(l) : new LinkedList(l);
addMapping(mapping, w);
initServletProcessor(h);
if (!isInit) {
logger.info("Installed AtmosphereHandler {} mapped to context-path {} and Broadcaster Class {}",
new String[]{h.getClass().getName(), mapping, broadcaster.getClass().getName()});
} else {
logger.debug("Installed AtmosphereHandler {} mapped to context-path {} and Broadcaster Class {}",
new String[]{h.getClass().getName(), mapping, broadcaster.getClass().getName()});
}
if (l.size() > 0) {
logger.info("Installed AtmosphereInterceptor {} mapped to AtmosphereHandler {}", l, h.getClass().getName());
}
return this;
}
/**
* Add an {@link AtmosphereHandler} serviced by the {@link Servlet}.
* This API is exposed to allow embedding an Atmosphere application.
*
* @param mapping The servlet mapping (servlet path)
* @param h implementation of an {@link AtmosphereHandler}
* @param broadcaster The {@link Broadcaster} associated with AtmosphereHandler.
*/
public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h, Broadcaster broadcaster) {
addAtmosphereHandler(mapping, h, broadcaster, Collections.emptyList());
return this;
}
/**
* Remove an {@link AtmosphereHandler}.
*
* @param mapping the mapping used when invoking {@link #addAtmosphereHandler(String, AtmosphereHandler)};
* @return true if removed
*/
public AtmosphereFramework removeAtmosphereHandler(String mapping) {
if (mapping.endsWith("/")) {
mapping += mappingRegex;
}
atmosphereHandlers.remove(mapping);
return this;
}
/**
* Remove all {@link AtmosphereHandler}s.
*/
public AtmosphereFramework removeAllAtmosphereHandler() {
atmosphereHandlers.clear();
return this;
}
/**
* Remove all init parameters.
*/
public AtmosphereFramework removeAllInitParams() {
initParams.clear();
return this;
}
/**
* Add init-param like if they were defined in web.xml
*
* @param name The name
* @param value The value
*/
public AtmosphereFramework addInitParameter(String name, String value) {
initParams.put(name, value);
return this;
}
protected void readSystemProperties() {
if (System.getProperty(PROPERTY_NATIVE_COMETSUPPORT) != null) {
useNativeImplementation = Boolean
.parseBoolean(System.getProperty(PROPERTY_NATIVE_COMETSUPPORT));
isCometSupportSpecified = true;
}
if (System.getProperty(PROPERTY_BLOCKING_COMETSUPPORT) != null) {
useBlockingImplementation = Boolean
.parseBoolean(System.getProperty(PROPERTY_BLOCKING_COMETSUPPORT));
isCometSupportSpecified = true;
}
atmosphereDotXmlPath = System.getProperty(PROPERTY_ATMOSPHERE_XML, atmosphereDotXmlPath);
if (System.getProperty(DISABLE_ONSTATE_EVENT) != null) {
initParams.put(DISABLE_ONSTATE_EVENT, System.getProperty(DISABLE_ONSTATE_EVENT));
}
}
/**
* Path specific container using their own property.
*/
public void patchContainer() {
System.setProperty("org.apache.catalina.STRICT_SERVLET_COMPLIANCE", "false");
}
/**
* Initialize the AtmosphereFramework. Invoke this method after having properly configured this class using the setters.
*/
public AtmosphereFramework init() {
try {
init(servletConfig == null ? new ServletConfig() {
@Override
public String getServletName() {
return "AtmosphereFramework";
}
@Override
public ServletContext getServletContext() {
return (ServletContext) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{ServletContext.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return ServletProxyFactory.getDefault().proxy(proxy, method, args);
}
}
);
}
@Override
public String getInitParameter(String name) {
return initParams.get(name);
}
@Override
public Enumeration getInitParameterNames() {
return Collections.enumeration(initParams.values());
}
} : servletConfig, false);
} catch (ServletException e) {
logger.error("", e);
}
return this;
}
/**
* Initialize the AtmosphereFramework using the {@link ServletContext}.
*
* @param sc the {@link ServletContext}
*/
public AtmosphereFramework init(final ServletConfig sc) throws ServletException {
return init(sc, true);
}
/**
* Prevent Atmosphere from scanning the entire class path.
*/
protected void preventOOM() {
String s = config.getInitParameter(ApplicationConfig.SCAN_CLASSPATH);
if (s != null) {
allowAllClassesScan = Boolean.parseBoolean(s);
}
try {
Class.forName("org.testng.Assert");
allowAllClassesScan = false;
} catch (ClassNotFoundException e) {
}
}
/**
* Initialize the AtmosphereFramework using the {@link ServletContext}.
*
* @param sc the {@link ServletContext}
*/
public AtmosphereFramework init(final ServletConfig sc, boolean wrap) throws ServletException {
if (isInit) return this;
readSystemProperties();
populateBroadcasterType();
populateObjectFactoryType();
loadMetaService();
try {
ServletConfig scFacade;
if (wrap) {
scFacade = new ServletConfig() {
AtomicBoolean done = new AtomicBoolean();
public String getServletName() {
return sc.getServletName();
}
public ServletContext getServletContext() {
return sc.getServletContext();
}
public String getInitParameter(String name) {
String param = initParams.get(name);
if (param == null) {
return sc.getInitParameter(name);
}
return param;
}
public Enumeration getInitParameterNames() {
if (!done.getAndSet(true)) {
Enumeration en = sc.getInitParameterNames();
if (en != null) {
while (en.hasMoreElements()) {
String name = (String) en.nextElement();
if (!initParams.containsKey(name)) {
initParams.put(name, sc.getInitParameter(name));
}
}
}
}
return Collections.enumeration(initParams.keySet());
}
};
} else {
scFacade = sc;
}
this.servletConfig = scFacade;
ServletContextFactory.getDefault().init(sc.getServletContext());
preventOOM();
doInitParams(scFacade);
doInitParamsForWebSocket(scFacade);
objectFactory = lookupDefaultObjectFactoryType();
asyncSupportListener(newClassInstance(AsyncSupportListener.class, AsyncSupportListenerAdapter.class));
configureObjectFactory();
configureAnnotationPackages();
configureBroadcasterFactory();
configureScanningPackage(scFacade, ApplicationConfig.ANNOTATION_PACKAGE);
configureScanningPackage(scFacade, FrameworkConfig.JERSEY2_SCANNING_PACKAGE);
configureScanningPackage(scFacade, FrameworkConfig.JERSEY_SCANNING_PACKAGE);
// Force scanning of the packages defined.
defaultPackagesToScan();
installAnnotationProcessor(scFacade);
autoConfigureService(scFacade.getServletContext());
// Reconfigure in case an annotation changed the default.
configureBroadcasterFactory();
configureMetaBroadcaster();
patchContainer();
configureBroadcaster();
loadConfiguration(scFacade);
initWebSocket();
initEndpointMapper();
initDefaultSerializer();
autoDetectContainer();
configureWebDotXmlAtmosphereHandler(scFacade);
asyncSupport.init(scFacade);
initAtmosphereHandler(scFacade);
configureAtmosphereInterceptor(scFacade);
analytics();
// http://java.net/jira/browse/ATMOSPHERE-157
if (sc.getServletContext() != null) {
sc.getServletContext().setAttribute(BroadcasterFactory.class.getName(), broadcasterFactory);
}
String s = config.getInitParameter(ApplicationConfig.BROADCASTER_SHARABLE_THREAD_POOLS);
if (s != null) {
sharedThreadPools = Boolean.parseBoolean(s);
}
info();
} catch (Throwable t) {
logger.error("Failed to initialize Atmosphere Framework", t);
if (t instanceof ServletException) {
throw (ServletException) t;
}
throw new ServletException(t);
}
isInit = true;
config.initComplete();
// wlc 12.x
if (WebLogicServlet30WithWebSocket.class.isAssignableFrom(asyncSupport.getClass())) {
servletConfig.getServletContext().setAttribute(AtmosphereConfig.class.getName(), config);
}
return this;
}
public void reconfigureInitParams(boolean reconfigureInitParams) {
if (reconfigureInitParams) {
doInitParams(servletConfig, reconfigureInitParams);
doInitParamsForWebSocket(servletConfig);
}
}
private void info() {
logger.info("Using EndpointMapper {}", endpointMapper.getClass());
for (String i : broadcasterFilters) {
logger.info("Using BroadcastFilter: {}", i);
}
if (broadcasterCacheClassName == null || DefaultBroadcasterCache.class.getName().equals(broadcasterCacheClassName)) {
logger.warn("No BroadcasterCache configured. Broadcasted message between client reconnection will be LOST. " +
"It is recommended to configure the {}", UUIDBroadcasterCache.class.getName());
} else {
logger.info("Using BroadcasterCache: {}", broadcasterCacheClassName);
}
String s = config.getInitParameter(BROADCASTER_WAIT_TIME);
logger.info("Default Broadcaster Class: {}", broadcasterClassName);
logger.info("Broadcaster Polling Wait Time {}", s == null ? DefaultBroadcaster.POLLING_DEFAULT : s);
logger.info("Shared ExecutorService supported: {}", sharedThreadPools);
BroadcasterConfig bc = broadcasterFactory.lookup(Broadcaster.ROOT_MASTER, true).getBroadcasterConfig();
if (bc.getExecutorService() != null) {
ExecutorService executorService = bc.getExecutorService();
if (ThreadPoolExecutor.class.isAssignableFrom(executorService.getClass())) {
long max = ThreadPoolExecutor.class.cast(executorService).getMaximumPoolSize();
logger.info("Messaging Thread Pool Size: {}",
ThreadPoolExecutor.class.cast(executorService).getMaximumPoolSize() == 2147483647 ? "Unlimited" : max);
}
else {
logger.info("Messaging ExecutorService Pool Size unavailable - Not instance of ThreadPoolExecutor");
}
}
if (bc.getAsyncWriteService() != null) {
ExecutorService asyncWriteService = bc.getAsyncWriteService();
if (ThreadPoolExecutor.class.isAssignableFrom(asyncWriteService.getClass())) {
logger.info("Async I/O Thread Pool Size: {}",
ThreadPoolExecutor.class.cast(asyncWriteService).getMaximumPoolSize());
}
else {
logger.info("Async I/O ExecutorService Pool Size unavailable - Not instance of ThreadPoolExecutor");
}
}
/*if (bc.getAsyncWriteService() != null) {
}*/
logger.info("Using BroadcasterFactory: {}", broadcasterFactory.getClass().getName());
logger.info("Using WebSocketProcessor: {}", webSocketProcessorClassName);
if (defaultSerializerClassName != null && !defaultSerializerClassName.isEmpty()) {
logger.info("Using Serializer: {}", defaultSerializerClassName);
}
WebSocketProcessor wp = WebSocketProcessorFactory.getDefault().getWebSocketProcessor(this);
boolean b = false;
if (DefaultWebSocketProcessor.class.isAssignableFrom(wp.getClass())) {
b = DefaultWebSocketProcessor.class.cast(wp).invokeInterceptors();
}
logger.info("Invoke AtmosphereInterceptor on WebSocket message {}", b);
logger.info("HttpSession supported: {}", config.isSupportSession());
logger.info("Atmosphere is using {} for dependency injection and object creation", objectFactory);
logger.info("Atmosphere is using async support: {} running under container: {}",
getAsyncSupport().getClass().getName(), asyncSupport.getContainerName());
logger.info("Atmosphere Framework {} started.", Version.getRawVersion());
logger.info("\n\n\tFor Atmosphere Framework Commercial Support, visit \n\t{} " +
"or send an email to {}\n", "http://www.async-io.org/", "[email protected]");
}
private void configureAnnotationPackages() {
// We must scan the default annotation set.
annotationPackages.add(Processor.class.getPackage().getName());
String s = config.getInitParameter(ApplicationConfig.CUSTOM_ANNOTATION_PACKAGE);
if (s != null) {
String[] l = s.split(",");
for (String p : l) {
annotationPackages.addLast(p);
}
}
}
protected void analytics() {
final String container = getServletContext().getServerInfo();
if (allowAllClassesScan) {
Thread t = new Thread() {
public void run() {
try {
HttpURLConnection urlConnection = (HttpURLConnection)
URI.create("http://async-io.org/version.html").toURL().openConnection();
urlConnection.setRequestProperty("User-Agent", "Mozilla/5.0");
urlConnection.setRequestProperty("Connection", "keep-alive");
urlConnection.setRequestProperty("Cache-Control", "max-age=0");
urlConnection.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
urlConnection.setRequestProperty("Accept-Language", "en-US,en;q=0.8");
urlConnection.setRequestProperty("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.3");
urlConnection.setRequestProperty("If-Modified-Since", "ISO-8859-1,utf-8;q=0.7,*;q=0.3");
urlConnection.setInstanceFollowRedirects(true);
BufferedReader in = new BufferedReader(new InputStreamReader(
urlConnection.getInputStream()));
String inputLine;
String newVersion = Version.getRawVersion();
String clientVersion = "2.2.4";
String nextMajorRelease = "2.3.0";
boolean nextAvailable = false;
try {
while ((inputLine = in.readLine().trim()) != null) {
if (inputLine.startsWith("ATMO22_VERSION=")) {
newVersion = inputLine.substring("ATMO22_VERSION=".length());
} else if (inputLine.startsWith("CLIENT3_VERSION=")) {
clientVersion = inputLine.substring("CLIENT3_VERSION=".length());
break;
} else if (inputLine.startsWith("ATMO_RELEASE_VERSION=")) {
nextMajorRelease = inputLine.substring("ATMO_RELEASE_VERSION=".length());
nextAvailable = true;
}
}
} finally {
logger.info("Latest version of Atmosphere's JavaScript Client {}", clientVersion);
if (newVersion.compareTo(Version.getRawVersion()) != 0) {
String msg = "\n\n\tAtmosphere Framework Updates:\n\tMinor Update available (bugs fixes): {}";
if (nextAvailable && nextMajorRelease.toLowerCase().indexOf("rc") == -1 && nextMajorRelease.toLowerCase().indexOf("beta") == -1) {
msg = "\n\n\tAtmosphere Framework Updates\n\tMinor available (bugs fixes): {}\n\tMajor available (new features): {}";
}
logger.info(msg, newVersion, nextMajorRelease);
}
try {
in.close();
} catch (IOException ex) {
}
urlConnection.disconnect();
}
JGoogleAnalyticsTracker tracker = new JGoogleAnalyticsTracker(ModuleDetection.detect(), Version.getRawVersion(), "UA-31990725-1");
tracker.trackSynchronously(new FocusPoint(container, new FocusPoint("Atmosphere")));
} catch (Throwable e) {
}
}
};
t.setDaemon(true);
t.start();
}
}
/**
* Configure the list of {@link AtmosphereInterceptor}.
*
* @param sc a ServletConfig
*/
protected void configureAtmosphereInterceptor(ServletConfig sc) {
String s = sc.getInitParameter(ApplicationConfig.ATMOSPHERE_INTERCEPTORS);
if (s != null) {
String[] list = s.split(",");
for (String a : list) {
try {
AtmosphereInterceptor ai = newClassInstance(AtmosphereInterceptor.class,
(Class) IOUtils
.loadClass(getClass(), a.trim()));
interceptor(ai);
} catch (Exception e) {
logger.warn("", e);
}
}
}
s = sc.getInitParameter(ApplicationConfig.DISABLE_ATMOSPHEREINTERCEPTOR);
if (s == null || !"true".equalsIgnoreCase(s)) {
logger.info("Installing Default AtmosphereInterceptors");
s = sc.getInitParameter(ApplicationConfig.DISABLE_ATMOSPHEREINTERCEPTORS);
if (s != null) {
excludedInterceptors.addAll(Arrays.asList(s.trim().replace(" ", "").split(",")));
}
// We must reposition
LinkedList copy = null;
if (!interceptors.isEmpty()) {
copy = new LinkedList(interceptors);
interceptors.clear();
}
for (Class extends AtmosphereInterceptor> a : defaultInterceptors) {
if (!excludedInterceptors.contains(a.getName())) {
interceptors.add(newAInterceptor(a));
} else {
logger.info("Dropping Interceptor {}", a.getName());
}
}
if (copy != null) {
for (AtmosphereInterceptor i : copy) {
interceptor(i);
}
}
logger.info("Set {} to disable them.", ApplicationConfig.DISABLE_ATMOSPHEREINTERCEPTOR, interceptors);
}
initInterceptors();
}
protected AtmosphereInterceptor newAInterceptor(Class extends AtmosphereInterceptor> a) {
AtmosphereInterceptor ai = null;
try {
ai = newClassInstance(AtmosphereInterceptor.class,
(Class) IOUtils.loadClass(getClass(), a.getName()));
logger.info("\t{} : {}", a.getName(), ai);
} catch (Exception ex) {
logger.warn("", ex);
}
return ai;
}
protected void initGlobalInterceptors() {
for (AtmosphereInterceptor i : interceptors) {
i.configure(config);
}
}
public void initHandlerInterceptors(LinkedList l) {
if (l != null) {
LinkedList copy = new LinkedList();
copy.addAll(l);
for (AtmosphereInterceptor i : copy) {
//
InvokationOrder.PRIORITY p = InvokationOrder.class.isAssignableFrom(i.getClass()) ?
InvokationOrder.class.cast(i).priority() : InvokationOrder.AFTER_DEFAULT;
// We need to relocate the interceptor
if (!p.equals(InvokationOrder.AFTER_DEFAULT)) {
positionInterceptor(p, i, l);
}
i.configure(config);
}
}
}
protected void initInterceptors() {
initGlobalInterceptors();
for (AtmosphereHandlerWrapper w : atmosphereHandlers.values()) {
initHandlerInterceptors(w.interceptors);
}
}
protected void configureWebDotXmlAtmosphereHandler(ServletConfig sc) {
String s = sc.getInitParameter(ATMOSPHERE_HANDLER);
if (s != null) {
try {
String mapping = sc.getInitParameter(ATMOSPHERE_HANDLER_MAPPING);
if (mapping == null) {
mapping = Broadcaster.ROOT_MASTER;
}
addAtmosphereHandler(mapping, newClassInstance(AtmosphereHandler.class,
(Class) IOUtils.loadClass(getClass(), s)));
} catch (Exception ex) {
logger.warn("Unable to load WebSocketHandle instance", ex);
}
}
}
protected void configureScanningPackage(ServletConfig sc, String value) {
String packageName = sc.getInitParameter(value);
if (packageName != null) {
String[] list = packageName.split(",");
for (String a : list) {
packages.add(a);
}
}
}
protected void defaultPackagesToScan() {
// Atmosphere HA/Pro
packages.add("io.async.control");
packages.add("io.async.satellite");
packages.add("io.async.postman");
}
public void configureBroadcasterFactory() {
try {
// Check auto supported one
if (isBroadcasterSpecified == false) {
broadcasterClassName = lookupDefaultBroadcasterType(broadcasterClassName);
}
if (broadcasterFactoryClassName != null) {
broadcasterFactory = newClassInstance(BroadcasterFactory.class,
(Class) IOUtils.loadClass(getClass(), broadcasterFactoryClassName));
}
if (broadcasterFactory == null) {
Class extends Broadcaster> bc =
(Class extends Broadcaster>) IOUtils.loadClass(getClass(), broadcasterClassName);
broadcasterFactory = newClassInstance(BroadcasterFactory.class, DefaultBroadcasterFactory.class);
broadcasterFactory.configure(bc, broadcasterLifeCyclePolicy, config);
}
for (BroadcasterListener b : broadcasterListeners) {
broadcasterFactory.addBroadcasterListener(b);
}
BroadcasterFactory.setBroadcasterFactory(broadcasterFactory, config);
configureAtmosphereResourceFactory();
} catch (Exception ex) {
logger.error("Unable to configure Broadcaster/Factory/Cache", ex);
}
}
protected void configureBroadcaster() {
try {
Iterator> i = atmosphereHandlers.entrySet().iterator();
AtmosphereHandlerWrapper w;
Entry e;
while (i.hasNext()) {
e = i.next();
w = e.getValue();
if (w.broadcaster == null) {
w.broadcaster = broadcasterFactory.get(w.mapping);
} else {
if (broadcasterCacheClassName != null
&& w.broadcaster.getBroadcasterConfig().getBroadcasterCache().getClass().getName().equals(DefaultBroadcasterCache.class.getName())) {
BroadcasterCache cache = newClassInstance(BroadcasterCache.class,
(Class) IOUtils.loadClass(getClass(), broadcasterCacheClassName));
w.broadcaster.getBroadcasterConfig().setBroadcasterCache(cache);
}
}
}
} catch (Exception ex) {
logger.error("Unable to configure Broadcaster/Factory/Cache", ex);
}
}
protected void installAnnotationProcessor(ServletConfig sc) {
String s = sc.getInitParameter(ApplicationConfig.ANNOTATION_PROCESSOR);
if (s != null) {
annotationProcessorClassName = s;
}
}
protected void doInitParamsForWebSocket(ServletConfig sc) {
String s = sc.getInitParameter(WEBSOCKET_SUPPORT);
if (s != null) {
webSocketEnabled = Boolean.parseBoolean(s);
sessionSupport(false);
}
s = sc.getInitParameter(WEBSOCKET_PROTOCOL);
if (s != null) {
webSocketProtocolClassName = s;
}
s = sc.getInitParameter(WEBSOCKET_PROCESSOR);
if (s != null) {
webSocketProcessorClassName = s;
}
s = config.getInitParameter(ApplicationConfig.WEBSOCKET_SUPPORT_SERVLET3);
if (s != null) {
useServlet30 = Boolean.parseBoolean(s);
}
}
/**
* Read init params from web.xml and apply them.
*
* @param sc {@link ServletConfig}
*/
protected void doInitParams(ServletConfig sc) {
doInitParams(sc, false);
}
/**
* Read init params from web.xml and apply them.
*
* @param sc {@link ServletConfig}
*/
protected void doInitParams(ServletConfig sc, boolean reconfigure) {
String s = sc.getInitParameter(PROPERTY_NATIVE_COMETSUPPORT);
if (s != null) {
useNativeImplementation = Boolean.parseBoolean(s);
if (useNativeImplementation) isCometSupportSpecified = true;
}
s = sc.getInitParameter(PROPERTY_BLOCKING_COMETSUPPORT);
if (s != null) {
useBlockingImplementation = Boolean.parseBoolean(s);
if (useBlockingImplementation) isCometSupportSpecified = true;
}
s = sc.getInitParameter(PROPERTY_USE_STREAM);
if (s != null) {
useStreamForFlushingComments = Boolean.parseBoolean(s);
}
s = sc.getInitParameter(PROPERTY_COMET_SUPPORT);
if (s != null && !reconfigure) {
asyncSupport = new DefaultAsyncSupportResolver(config).newCometSupport(s);
isCometSupportSpecified = true;
}
s = sc.getInitParameter(BROADCASTER_CLASS);
if (s != null) {
broadcasterClassName = s;
isBroadcasterSpecified = true;
}
s = sc.getInitParameter(BROADCASTER_CACHE);
if (s != null) {
broadcasterCacheClassName = s;
}
s = sc.getInitParameter(PROPERTY_SESSION_SUPPORT);
if (s == null) {
s = sc.getServletContext().getInitParameter(PROPERTY_SESSION_SUPPORT);
}
if (s != null || SessionSupport.initializationHint) {
boolean sessionSupport = Boolean.valueOf(s) || SessionSupport.initializationHint;
config.setSupportSession(sessionSupport);
if (sessionSupport && (sc.getServletContext().getMajorVersion() < 3 || !SessionSupport.initializationHint)) {
logger.warn("SessionSupport error. Make sure you define {} as a listener in web.xml instead", SessionSupport.class.getName());
}
isSessionSupportSpecified = true;
}
s = sc.getInitParameter(PROPERTY_ALLOW_SESSION_TIMEOUT_REMOVAL);
if (s != null) {
config.setSessionTimeoutRemovalAllowed(Boolean.valueOf(s));
}
s = sc.getInitParameter(PROPERTY_THROW_EXCEPTION_ON_CLONED_REQUEST);
if (s != null) {
config.setThrowExceptionOnCloned(Boolean.valueOf(s));
isThrowExceptionOnClonedRequestSpecified = true;
}
s = sc.getInitParameter(DISABLE_ONSTATE_EVENT);
if (s != null) {
initParams.put(DISABLE_ONSTATE_EVENT, s);
} else {
initParams.put(DISABLE_ONSTATE_EVENT, "false");
}
s = sc.getInitParameter(BROADCAST_FILTER_CLASSES);
if (s != null) {
broadcasterFilters.addAll(Arrays.asList(s.split(",")));
logger.info("Installing BroadcastFilter class(es) {}", s);
}
s = sc.getInitParameter(BROADCASTER_LIFECYCLE_POLICY);
if (s != null) {
broadcasterLifeCyclePolicy = s;
}
s = sc.getInitParameter(BROADCASTER_FACTORY);
if (s != null) {
broadcasterFactoryClassName = s;
}
s = sc.getInitParameter(ATMOSPHERE_HANDLER_PATH);
if (s != null) {
handlersPath = s;
}
s = sc.getInitParameter(PROPERTY_ATMOSPHERE_XML);
if (s != null) {
atmosphereDotXmlPath = s;
}
s = sc.getInitParameter(ApplicationConfig.HANDLER_MAPPING_REGEX);
if (s != null) {
mappingRegex = s;
}
s = sc.getInitParameter(FrameworkConfig.JERSEY_SCANNING_PACKAGE);
if (s != null) {
packages.add(s);
}
s = sc.getInitParameter(ApplicationConfig.DEFAULT_SERIALIZER);
if (s != null) {
defaultSerializerClassName = s;
}
}
public void loadConfiguration(ServletConfig sc) throws ServletException {
if (!autoDetectHandlers) return;
try {
URL url = sc.getServletContext().getResource(handlersPath);
URLClassLoader urlC = new URLClassLoader(new URL[]{url},
Thread.currentThread().getContextClassLoader());
loadAtmosphereDotXml(sc.getServletContext().
getResourceAsStream(atmosphereDotXmlPath), urlC);
if (atmosphereHandlers.size() == 0) {
autoDetectAtmosphereHandlers(sc.getServletContext(), urlC);
if (atmosphereHandlers.size() == 0) {
detectSupportedFramework(sc);
}
}
autoDetectWebSocketHandler(sc.getServletContext(), urlC);
} catch (Throwable t) {
throw new ServletException(t);
}
}
/**
* Auto-detect Jersey when no atmosphere.xml file is specified.
*
* @param sc {@link ServletConfig}
* @return true if Jersey classes are detected
* @throws ClassNotFoundException
*/
protected boolean detectSupportedFramework(ServletConfig sc) throws Exception {
String broadcasterClassNameTmp = null;
boolean isJersey = false;
try {
IOUtils.loadClass(getClass(), JERSEY_CONTAINER);
isJersey = true;
if (!isBroadcasterSpecified) {
broadcasterClassNameTmp = lookupDefaultBroadcasterType(JERSEY_BROADCASTER);
IOUtils.loadClass(getClass(), broadcasterClassNameTmp);
}
useStreamForFlushingComments = true;
StringBuilder packagesInit = new StringBuilder();
for (String s : packages) {
packagesInit.append(s).append(",");
}
initParams.put(FrameworkConfig.JERSEY_SCANNING_PACKAGE, packagesInit.toString());
} catch (Throwable t) {
logger.trace("", t);
return false;
}
logger.debug("Missing META-INF/atmosphere.xml but found the Jersey runtime. Starting Jersey");
// Atmosphere 1.1 : could add regressions
// Jersey will itself handle the headers.
//initParams.put(WRITE_HEADERS, "false");
ReflectorServletProcessor rsp = newClassInstance(ReflectorServletProcessor.class, ReflectorServletProcessor.class);
if (broadcasterClassNameTmp != null) broadcasterClassName = broadcasterClassNameTmp;
configureDetectedFramework(rsp, isJersey);
sessionSupport(false);
initParams.put(DISABLE_ONSTATE_EVENT, "true");
String mapping = sc.getInitParameter(PROPERTY_SERVLET_MAPPING);
if (mapping == null) {
mapping = sc.getInitParameter(ATMOSPHERE_HANDLER_MAPPING);
if (mapping == null) {
mapping = Broadcaster.ROOT_MASTER;
}
}
Class extends Broadcaster> bc = (Class extends Broadcaster>) IOUtils.loadClass(getClass(), broadcasterClassName);
broadcasterFactory.destroy();
broadcasterFactory = newClassInstance(BroadcasterFactory.class, DefaultBroadcasterFactory.class);
broadcasterFactory.configure(bc, broadcasterLifeCyclePolicy, config);
BroadcasterFactory.setBroadcasterFactory(broadcasterFactory, config);
for (BroadcasterListener b : broadcasterListeners) {
broadcasterFactory.addBroadcasterListener(b);
}
Broadcaster b;
try {
b = broadcasterFactory.get(bc, mapping);
} catch (IllegalStateException ex) {
logger.warn("Two Broadcaster's named {}. Renaming the second one to {}", mapping, sc.getServletName() + mapping);
b = broadcasterFactory.get(bc, sc.getServletName() + mapping);
}
addAtmosphereHandler(mapping, rsp, b);
return true;
}
protected void configureDetectedFramework(ReflectorServletProcessor rsp, boolean isJersey) {
rsp.setServletClassName(JERSEY_CONTAINER);
}
protected String lookupDefaultBroadcasterType(String defaultB) {
String drop = servletConfig != null ? servletConfig.getInitParameter(ApplicationConfig.AUTODETECT_BROADCASTER) : null;
if (drop == null || !Boolean.parseBoolean(drop)) {
for (String b : broadcasterTypes) {
try {
Class.forName(b);
logger.info("Detected a Broadcaster {} on the classpath. " +
"This broadcaster will be used by default and will override any annotated resources. " +
"Set {} to false to change the behavior", b, ApplicationConfig.AUTODETECT_BROADCASTER);
isBroadcasterSpecified = true;
return b;
} catch (ClassNotFoundException e) {
}
}
}
return defaultB;
}
protected AtmosphereObjectFactory lookupDefaultObjectFactoryType() {
for (String b : objectFactoryType) {
try {
Class> c = Class.forName(b);
return (AtmosphereObjectFactory) c.newInstance();
} catch (ClassNotFoundException e) {
logger.trace(e.getMessage() + " not found");
} catch (Exception e) {
logger.trace("", e);
}
}
return objectFactory;
}
public void sessionSupport(boolean sessionSupport) {
if (!isSessionSupportSpecified) {
config.setSupportSession(sessionSupport);
} else if (!config.isSupportSession()) {
// Don't turn off session support. Once it's on, leave it on.
config.setSupportSession(sessionSupport);
}
}
/**
* Initialize {@link AtmosphereServletProcessor}.
*
* @param sc the {@link ServletConfig}
* @throws javax.servlet.ServletException
*/
public void initAtmosphereHandler(ServletConfig sc) throws ServletException {
AtmosphereHandler a;
AtmosphereHandlerWrapper w;
for (Entry h : atmosphereHandlers.entrySet()) {
w = h.getValue();
a = w.atmosphereHandler;
if (a instanceof AtmosphereServletProcessor) {
((AtmosphereServletProcessor) a).init(config);
}
}
if (atmosphereHandlers.size() == 0 && !SimpleHttpProtocol.class.isAssignableFrom(webSocketProtocol.getClass())) {
logger.debug("Adding a void AtmosphereHandler mapped to /* to allow WebSocket application only");
addAtmosphereHandler(Broadcaster.ROOT_MASTER, new AbstractReflectorAtmosphereHandler() {
@Override
public void onRequest(AtmosphereResource r) throws IOException {
logger.debug("No AtmosphereHandler defined.");
if (!r.transport().equals(AtmosphereResource.TRANSPORT.WEBSOCKET)) {
WebSocket.notSupported(r.getRequest(), r.getResponse());
}
}
@Override
public void destroy() {
}
});
}
}
public void initWebSocket() {
if (webSocketProtocolInitialized) return;
if (webSocketProtocol == null) {
try {
webSocketProtocol = newClassInstance(WebSocketProtocol.class,
(Class) IOUtils.loadClass(this.getClass(), webSocketProtocolClassName));
logger.info("Installed WebSocketProtocol {} ", webSocketProtocolClassName);
} catch (Exception ex) {
logger.error("Cannot load the WebSocketProtocol {}", getWebSocketProtocolClassName(), ex);
try {
webSocketProtocol = newClassInstance(WebSocketProtocol.class, SimpleHttpProtocol.class);
} catch (Exception e) {
}
}
}
webSocketProtocolInitialized = true;
webSocketProtocol.configure(config);
}
public void initEndpointMapper() {
String s = servletConfig.getInitParameter(ApplicationConfig.ENDPOINT_MAPPER);
if (s != null) {
try {
endpointMapper = newClassInstance(EndpointMapper.class, (Class) IOUtils.loadClass(this.getClass(), s));
logger.info("Installed EndpointMapper {} ", s);
} catch (Exception ex) {
logger.error("Cannot load the EndpointMapper {}", s, ex);
}
}
endpointMapper.configure(config);
}
public AtmosphereFramework destroy() {
if (isDestroyed.getAndSet(true)) return this;
// Invoke ShutdownHook.
config.destroy();
BroadcasterFactory factory = broadcasterFactory;
if (factory != null) {
factory.destroy();
}
if (asyncSupport != null && AsynchronousProcessor.class.isAssignableFrom(asyncSupport.getClass())) {
((AsynchronousProcessor) asyncSupport).shutdown();
}
// We just need one bc to shutdown the shared thread pool
for (Entry entry : atmosphereHandlers.entrySet()) {
AtmosphereHandlerWrapper handlerWrapper = entry.getValue();
handlerWrapper.atmosphereHandler.destroy();
}
if (metaBroadcaster != null) metaBroadcaster.destroy();
if (arFactory != null) arFactory.destroy();
WebSocketProcessorFactory.getDefault().destroy();
resetStates();
return this;
}
public AtmosphereFramework resetStates() {
isInit = false;
executeFirstSet = false;
broadcasterFilters.clear();
asyncSupportListeners.clear();
possibleComponentsCandidate.clear();
initParams.clear();
atmosphereHandlers.clear();
broadcasterTypes.clear();
objectFactoryType.clear();
inspectors.clear();
broadcasterListeners.clear();
packages.clear();
annotationPackages.clear();
excludedInterceptors.clear();
broadcasterCacheListeners.clear();
filterManipulators.clear();
interceptors.clear();
broadcasterFactory = null;
arFactory = null;
metaBroadcaster = null;
annotationFound = false;
return this;
}
protected void loadMetaService() {
try {
final Map config = IOUtils.readServiceFile(AtmosphereFramework.class.getName());
for (final Map.Entry action : config.entrySet()) {
final Class c = IOUtils.loadClass(AtmosphereFramework.class, action.getKey());
action.getValue().apply(this, c);
}
} catch (Exception ex) {
logger.error("", ex);
}
}
/**
* Load AtmosphereHandler defined under META-INF/atmosphere.xml.
*
* @param stream The input stream we read from.
* @param c The classloader
*/
protected void loadAtmosphereDotXml(InputStream stream, URLClassLoader c)
throws IOException, ServletException {
if (stream == null) {
return;
}
logger.info("Found Atmosphere Configuration under {}", atmosphereDotXmlPath);
AtmosphereConfigReader.getInstance().parse(config, stream);
AtmosphereHandler handler = null;
for (AtmosphereHandlerConfig atmoHandler : config.getAtmosphereHandlerConfig()) {
try {
if (!atmoHandler.getClassName().startsWith("@")) {
if (!ReflectorServletProcessor.class.getName().equals(atmoHandler.getClassName())) {
handler = newClassInstance(AtmosphereHandler.class,
(Class) IOUtils.loadClass(this.getClass(), atmoHandler.getClassName()));
} else {
handler = newClassInstance(AtmosphereHandler.class, ReflectorServletProcessor.class);
}
logger.info("Installed AtmosphereHandler {} mapped to context-path: {}", handler, atmoHandler.getContextRoot());
}
for (ApplicationConfiguration a : atmoHandler.getApplicationConfig()) {
initParams.put(a.getParamName(), a.getParamValue());
}
for (FrameworkConfiguration a : atmoHandler.getFrameworkConfig()) {
initParams.put(a.getParamName(), a.getParamValue());
}
for (AtmosphereHandlerProperty handlerProperty : atmoHandler.getProperties()) {
if (handlerProperty.getValue() != null && handlerProperty.getValue().indexOf("jersey") != -1) {
initParams.put(DISABLE_ONSTATE_EVENT, "true");
useStreamForFlushingComments = true;
broadcasterClassName = lookupDefaultBroadcasterType(JERSEY_BROADCASTER);
broadcasterFactory.destroy();
broadcasterFactory = null;
configureBroadcasterFactory();
configureBroadcaster();
}
if (handler != null) {
IntrospectionUtils.setProperty(handler, handlerProperty.getName(), handlerProperty.getValue());
IntrospectionUtils.addProperty(handler, handlerProperty.getName(), handlerProperty.getValue());
}
}
sessionSupport(Boolean.valueOf(atmoHandler.getSupportSession()));
if (handler != null) {
String broadcasterClass = atmoHandler.getBroadcaster();
Broadcaster b;
/**
* If there is more than one AtmosphereHandler defined, their Broadcaster
* may clash each other with the BroadcasterFactory. In that case we will use the
* last one defined.
*/
if (broadcasterClass != null) {
broadcasterClassName = broadcasterClass;
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class extends Broadcaster> bc = (Class extends Broadcaster>) cl.loadClass(broadcasterClassName);
broadcasterFactory = newClassInstance(BroadcasterFactory.class, DefaultBroadcasterFactory.class);
broadcasterFactory.configure(bc, broadcasterLifeCyclePolicy, config);
BroadcasterFactory.setBroadcasterFactory(broadcasterFactory, config);
}
b = broadcasterFactory.lookup(atmoHandler.getContextRoot(), true);
AtmosphereHandlerWrapper wrapper = new AtmosphereHandlerWrapper(handler, b);
addMapping(atmoHandler.getContextRoot(), wrapper);
String bc = atmoHandler.getBroadcasterCache();
if (bc != null) {
broadcasterCacheClassName = bc;
}
if (atmoHandler.getCometSupport() != null) {
asyncSupport = (AsyncSupport) c.loadClass(atmoHandler.getCometSupport())
.getDeclaredConstructor(new Class[]{AtmosphereConfig.class})
.newInstance(new Object[]{config});
}
if (atmoHandler.getBroadcastFilterClasses() != null) {
broadcasterFilters.addAll(atmoHandler.getBroadcastFilterClasses());
}
LinkedList l = new LinkedList();
if (atmoHandler.getAtmosphereInterceptorClasses() != null) {
for (String a : atmoHandler.getAtmosphereInterceptorClasses()) {
try {
AtmosphereInterceptor ai = newClassInstance(AtmosphereInterceptor.class,
(Class) IOUtils.loadClass(getClass(), a));
l.add(ai);
} catch (Throwable e) {
logger.warn("", e);
}
}
}
wrapper.interceptors = l;
if (l.size() > 0) {
logger.info("Installed AtmosphereInterceptor {} mapped to AtmosphereHandler {}", l, atmoHandler.getClassName());
}
}
} catch (Throwable t) {
logger.warn("Unable to load AtmosphereHandler class: " + atmoHandler.getClassName(), t);
throw new ServletException(t);
}
}
}
/**
* Set the {@link AsyncSupport} implementation. Make sure you don't set an implementation that only works on
* some container. See {@link BlockingIOCometSupport} for an example.
*
* @param asyncSupport
*/
public AtmosphereFramework setAsyncSupport(AsyncSupport asyncSupport) {
this.asyncSupport = asyncSupport;
return this;
}
/**
* @param asyncSupport
* @return
* @Deprecated - Use {@link #setAsyncSupport(AsyncSupport)}
*/
public AtmosphereFramework setCometSupport(AsyncSupport asyncSupport) {
return setAsyncSupport(asyncSupport);
}
/**
* Return the current {@link AsyncSupport}.
*
* @return the current {@link AsyncSupport}
*/
public AsyncSupport getAsyncSupport() {
return asyncSupport;
}
/**
* Return the current {@link AsyncSupport}.
*
* @return the current {@link AsyncSupport}
* @deprecated Use getAsyncSupport
*/
public AsyncSupport getCometSupport() {
return asyncSupport;
}
/**
* Returns an instance of AsyncSupportResolver {@link AsyncSupportResolver}.
*
* @return CometSupportResolver
*/
protected AsyncSupportResolver createAsyncSupportResolver() {
return new DefaultAsyncSupportResolver(config);
}
/**
* Auto detect the underlying Servlet Container we are running on.
*/
protected void autoDetectContainer() {
// Was defined in atmosphere.xml
if (getAsyncSupport() == null) {
setAsyncSupport(createAsyncSupportResolver()
.resolve(useNativeImplementation, useBlockingImplementation, useServlet30));
}
}
/**
* Auto detect instance of {@link AtmosphereHandler} in case META-INF/atmosphere.xml
* is missing.
*
* @param servletContext {@link ServletContext}
* @param classloader {@link URLClassLoader} to load the class.
* @throws java.net.MalformedURLException
* @throws java.net.URISyntaxException
*/
public void autoDetectAtmosphereHandlers(ServletContext servletContext, URLClassLoader classloader)
throws MalformedURLException, URISyntaxException {
// If Handler has been added
if (atmosphereHandlers.size() > 0) return;
logger.info("Auto detecting atmosphere handlers {}", handlersPath);
String realPath = servletContext.getRealPath(handlersPath);
// Weblogic bug
if (realPath == null) {
URL u = servletContext.getResource(handlersPath);
if (u == null) return;
realPath = u.getPath();
}
loadAtmosphereHandlersFromPath(classloader, realPath);
}
public void loadAtmosphereHandlersFromPath(URLClassLoader classloader, String realPath) {
File file = new File(realPath);
if (file.exists() && file.isDirectory()) {
getFiles(file);
scanDone = true;
for (String className : possibleComponentsCandidate) {
try {
className = className.replace('\\', '/');
className = className.replaceFirst("^.*/(WEB-INF|target)(?:/scala-[^/]+)?/(test-)?classes/(.*)\\.class", "$3").replace("/", ".");
Class> clazz = classloader.loadClass(className);
if (AtmosphereHandler.class.isAssignableFrom(clazz)) {
AtmosphereHandler handler = newClassInstance(AtmosphereHandler.class, (Class) clazz);
addMapping("/" + handler.getClass().getSimpleName(),
new AtmosphereHandlerWrapper(broadcasterFactory, handler, "/" + handler.getClass().getSimpleName()));
logger.info("Installed AtmosphereHandler {} mapped to context-path: {}", handler, handler.getClass().getName());
}
} catch (Throwable t) {
logger.trace("failed to load class as an AtmosphereHandler: " + className, t);
}
}
}
}
/**
* Auto detect instance of {@link org.atmosphere.websocket.WebSocketHandler} in case META-INF/atmosphere.xml
* is missing.
*
* @param servletContext {@link ServletContext}
* @param classloader {@link URLClassLoader} to load the class.
* @throws java.net.MalformedURLException
* @throws java.net.URISyntaxException
*/
protected void autoDetectWebSocketHandler(ServletContext servletContext, URLClassLoader classloader)
throws MalformedURLException, URISyntaxException {
if (hasNewWebSocketProtocol) return;
logger.info("Auto detecting WebSocketHandler in {}", handlersPath);
loadWebSocketFromPath(classloader, realPath(servletContext, handlersPath));
}
protected void loadWebSocketFromPath(URLClassLoader classloader, String realPath) {
File file = new File(realPath);
if (file.exists() && file.isDirectory()) {
getFiles(file);
scanDone = true;
for (String className : possibleComponentsCandidate) {
try {
className = className.replace('\\', '/');
className = className.replaceFirst("^.*/(WEB-INF|target)(?:/scala-[^/]+)?/(test-)?classes/(.*)\\.class", "$3").replace("/", ".");
Class> clazz = classloader.loadClass(className);
if (WebSocketProtocol.class.isAssignableFrom(clazz)) {
webSocketProtocol = (WebSocketProtocol) newClassInstance(WebSocketProtocol.class, (Class) clazz);
logger.info("Installed WebSocketProtocol {}", webSocketProtocol);
}
} catch (Throwable t) {
logger.trace("failed to load class as an WebSocketProtocol: " + className, t);
}
}
}
}
/**
* Get a list of possible candidates to load as {@link AtmosphereHandler}.
*
* @param f the real path {@link File}
*/
private void getFiles(File f) {
if (scanDone) return;
File[] files = f.listFiles();
for (File test : files) {
if (test.isDirectory()) {
getFiles(test);
} else {
String clazz = test.getAbsolutePath();
if (clazz.endsWith(".class")) {
possibleComponentsCandidate.add(clazz);
}
}
}
}
/**
* Configure some attributes on the {@link AtmosphereRequest}.
*
* @param req {@link AtmosphereRequest}
*/
public AtmosphereFramework configureRequestResponse(AtmosphereRequest req, AtmosphereResponse res) throws UnsupportedEncodingException {
req.setAttribute(PROPERTY_USE_STREAM, useStreamForFlushingComments);
req.setAttribute(BROADCASTER_CLASS, broadcasterClassName);
req.setAttribute(ATMOSPHERE_CONFIG, config);
req.setAttribute(THROW_EXCEPTION_ON_CLONED_REQUEST, "" + config.isThrowExceptionOnCloned());
boolean skip = true;
String s = config.getInitParameter(ALLOW_QUERYSTRING_AS_REQUEST);
if (s != null) {
skip = Boolean.valueOf(s);
}
if (!skip || req.getAttribute(WEBSOCKET_SUSPEND) == null) {
Map headers = configureQueryStringAsRequest(req);
String body = headers.remove(ATMOSPHERE_POST_BODY);
if (body != null && body.isEmpty()) {
body = null;
}
// Reconfigure the request. Clear the Atmosphere queryString
req.headers(headers)
.method(body != null && req.getMethod().equalsIgnoreCase("GET") ? "POST" : req.getMethod());
if (body != null) {
req.body(URLDecoder.decode(body, req.getCharacterEncoding() == null ? "UTF-8" : req.getCharacterEncoding()));
}
}
s = req.getHeader(X_ATMOSPHERE_TRACKING_ID);
// Lookup for websocket
if (s == null || s.equals("0")) {
String unique = config.getInitParameter(ApplicationConfig.UNIQUE_UUID_WEBSOCKET);
if (unique != null && Boolean.valueOf(unique)) {
s = (String) req.getAttribute(SUSPENDED_ATMOSPHERE_RESOURCE_UUID);
}
}
if (s == null || s.equals("0")) {
s = UUID.randomUUID().toString();
res.setHeader(HeaderConfig.X_FIRST_REQUEST, "true");
res.setHeader(X_ATMOSPHERE_TRACKING_ID, s);
} else {
// This may breaks 1.0.0 application because the WebSocket's associated AtmosphereResource will
// all have the same UUID, and retrieving the original one for WebSocket, so we don't set it at all.
// Null means it is not an HTTP request.
if (req.resource() == null) {
res.setHeader(X_ATMOSPHERE_TRACKING_ID, s);
} else if (req.getAttribute(WebSocket.WEBSOCKET_INITIATED) == null) {
// WebSocket reconnect, in case an application manually set the header
// (impossible to retrieve the headers normally with WebSocket or SSE)
res.setHeader(X_ATMOSPHERE_TRACKING_ID, s);
}
}
if (req.getAttribute(SUSPENDED_ATMOSPHERE_RESOURCE_UUID) == null) {
req.setAttribute(SUSPENDED_ATMOSPHERE_RESOURCE_UUID, s);
}
return this;
}
/**
* Invoke the proprietary {@link AsyncSupport}.
*
* @param req
* @param res
* @return an {@link Action}
* @throws IOException
* @throws ServletException
*/
public Action doCometSupport(AtmosphereRequest req, AtmosphereResponse res) throws IOException, ServletException {
if (isDestroyed.get()) return Action.CANCELLED;
Action a = null;
try {
configureRequestResponse(req, res);
a = asyncSupport.service(req, res);
} catch (IllegalStateException ex) {
boolean isJBoss = ex.getMessage().startsWith("JBoss failed");
if (ex.getMessage() != null && (ex.getMessage().startsWith("Tomcat failed") || isJBoss)) {
if (!isFilter) {
logger.warn("Failed using comet support: {}, error: {} Is the NIO or APR Connector enabled?", asyncSupport.getClass().getName(),
ex.getMessage());
}
logger.error("If you have more than one Connector enabled, make sure they both use the same protocol, " +
"e.g NIO/APR or HTTP for all. If not, {} will be used and cannot be changed.", BlockingIOCometSupport.class.getName(), ex);
AsyncSupport current = asyncSupport;
asyncSupport = asyncSupport.supportWebSocket() && !isJBoss ? new Tomcat7BIOSupportWithWebSocket(config) : new BlockingIOCometSupport(config);
if (current instanceof AsynchronousProcessor) {
((AsynchronousProcessor) current).shutdown();
}
asyncSupport.init(config.getServletConfig());
logger.warn("Using " + asyncSupport.getClass().getName());
a = asyncSupport.service(req, res);
} else {
logger.error("AtmosphereFramework exception", ex);
throw ex;
}
} finally {
if (a != null) {
notify(a.type(), req, res);
}
if (!externalizeDestroy) {
if (req != null && a != null && a.type() != Action.TYPE.SUSPEND) {
req.destroy();
res.destroy();
notify(Action.TYPE.DESTROYED, req, res);
}
}
}
return a;
}
/**
* Return the default {@link Broadcaster} class name.
*
* @return the broadcasterClassName
*/
public String getDefaultBroadcasterClassName() {
return broadcasterClassName;
}
/**
* Set the default {@link Broadcaster} class name.
*
* @param bccn the broadcasterClassName to set
*/
public AtmosphereFramework setDefaultBroadcasterClassName(String bccn) {
if (isBroadcasterSpecified) {
logger.trace("Broadcaster {} already set in web.xml", broadcasterClassName);
return this;
}
isBroadcasterSpecified = true;
broadcasterClassName = bccn;
// Must reconfigure.
broadcasterFactory = null;
configureBroadcasterFactory();
// We must recreate all previously created Broadcaster.
for (AtmosphereHandlerWrapper w : atmosphereHandlers.values()) {
w.broadcaster = broadcasterFactory.lookup(w.broadcaster.getID(), true);
}
return this;
}
/**
* true if Atmosphere uses {@link AtmosphereResponse#getOutputStream()}
* by default for write operation.
*
* @return the useStreamForFlushingComments
*/
public boolean isUseStreamForFlushingComments() {
return useStreamForFlushingComments;
}
public boolean isUseServlet30() {
return useServlet30;
}
/**
* Set to true so Atmosphere uses {@link AtmosphereResponse#getOutputStream()}
* by default for write operation. Default is false.
*
* @param useStreamForFlushingComments the useStreamForFlushingComments to set
*/
public AtmosphereFramework setUseStreamForFlushingComments(boolean useStreamForFlushingComments) {
this.useStreamForFlushingComments = useStreamForFlushingComments;
return this;
}
/**
* Get the {@link BroadcasterFactory} which is used by Atmosphere to construct
* {@link Broadcaster}.
*
* @return {@link BroadcasterFactory}
*/
public BroadcasterFactory getBroadcasterFactory() {
if (broadcasterFactory == null) {
configureBroadcasterFactory();
}
return broadcasterFactory;
}
/**
* Set the {@link BroadcasterFactory} which is used by Atmosphere to construct
* {@link Broadcaster}.
*
* @return {@link BroadcasterFactory}
*/
public AtmosphereFramework setBroadcasterFactory(final BroadcasterFactory broadcasterFactory) {
this.broadcasterFactory = broadcasterFactory;
configureBroadcaster();
return this;
}
/**
* Return the {@link org.atmosphere.cpr.BroadcasterCache} class name.
*
* @return the {@link org.atmosphere.cpr.BroadcasterCache} class name
*/
public String getBroadcasterCacheClassName() {
return broadcasterCacheClassName;
}
/**
* Set the {@link org.atmosphere.cpr.BroadcasterCache} class name.
*
* @param broadcasterCacheClassName
*/
public AtmosphereFramework setBroadcasterCacheClassName(String broadcasterCacheClassName) {
this.broadcasterCacheClassName = broadcasterCacheClassName;
return this;
}
/**
* Add a new Broadcaster class name that AtmosphereServlet can use when initializing requests, and when the
* atmosphere.xml broadcaster element is unspecified.
*
* @param broadcasterTypeString
*/
public AtmosphereFramework addBroadcasterType(String broadcasterTypeString) {
broadcasterTypes.add(broadcasterTypeString);
return this;
}
public ConcurrentLinkedQueue broadcasterTypes() {
return broadcasterTypes;
}
public String getWebSocketProtocolClassName() {
return webSocketProtocolClassName;
}
public AtmosphereFramework setWebSocketProtocolClassName(String webSocketProtocolClassName) {
hasNewWebSocketProtocol = true;
this.webSocketProtocolClassName = webSocketProtocolClassName;
return this;
}
public Map getAtmosphereHandlers() {
return atmosphereHandlers;
}
protected Map configureQueryStringAsRequest(AtmosphereRequest request) {
Map headers = new HashMap();
StringBuilder q = new StringBuilder();
try {
String qs = request.getQueryString();
if (qs != null && !qs.isEmpty()) {
String[] params = qs.split("&");
String[] s;
for (String p : params) {
s = p.split("=", 2);
final String header = s[0];
final String value = s.length > 1 ? s[1] : "";
if (header.equalsIgnoreCase("Content-Type")) {
// Use the one set by the user first.
if (request.getContentType() == null ||
!request.getContentType().equalsIgnoreCase(s.length > 1 ? value : "")) {
request.contentType(s.length > 1 ? URLDecoder.decode(value, "UTF-8") : "");
}
}
if (!header.isEmpty()
&& !header.toLowerCase().startsWith("x-atmo")
&& !header.equalsIgnoreCase(HeaderConfig.X_HEARTBEAT_SERVER)
&& !header.equalsIgnoreCase("Content-Type")
&& !header.equalsIgnoreCase("_")) {
q.append(header).append("=").append(s.length > 1 ? value : "").append("&");
}
headers.put(header, s.length > 1 ? value : "");
}
}
} catch (Exception ex) {
logger.error("Unable to parse query string", ex);
}
String disallowModifyQueryString = config.getInitParameter(ApplicationConfig.DISALLOW_MODIFY_QUERYSTRING);
if (disallowModifyQueryString == null ||
disallowModifyQueryString.length() == 0 || "false".equalsIgnoreCase(disallowModifyQueryString)) {
if (q.length() > 0) {
q.deleteCharAt(q.length() - 1);
}
request.queryString(q.toString());
}
logger.trace("Query String translated to headers {}", headers);
return headers;
}
public WebSocketProtocol getWebSocketProtocol() {
// TODO: Spagetthi code, needs to rework.
// Make sure we initialized the WebSocketProtocol
initWebSocket();
return webSocketProtocol;
}
public boolean isUseNativeImplementation() {
return useNativeImplementation;
}
public AtmosphereFramework setUseNativeImplementation(boolean useNativeImplementation) {
this.useNativeImplementation = useNativeImplementation;
return this;
}
public boolean isUseBlockingImplementation() {
return useBlockingImplementation;
}
public AtmosphereFramework setUseBlockingImplementation(boolean useBlockingImplementation) {
this.useBlockingImplementation = useBlockingImplementation;
return this;
}
public String getAtmosphereDotXmlPath() {
return atmosphereDotXmlPath;
}
public AtmosphereFramework setAtmosphereDotXmlPath(String atmosphereDotXmlPath) {
this.atmosphereDotXmlPath = atmosphereDotXmlPath;
return this;
}
public String getHandlersPath() {
return handlersPath;
}
public AtmosphereFramework setHandlersPath(String handlersPath) {
this.handlersPath = handlersPath;
return this;
}
/**
* Return the location of the JARs containing the application classes. Default is WEB-INF/lib.
*
* @return the location of the JARs containing the application classes. Default is WEB-INF/lib
*/
public String getLibPath() {
return libPath;
}
/**
* Set the location of the JARs containing the application.
*
* @param libPath the location of the JARs containing the application.
* @return this
*/
public AtmosphereFramework setLibPath(String libPath) {
this.libPath = libPath;
return this;
}
/**
* The current {@link org.atmosphere.websocket.WebSocketProcessor} used to handle websocket requests.
*
* @return {@link org.atmosphere.websocket.WebSocketProcessor}
*/
public String getWebSocketProcessorClassName() {
return webSocketProcessorClassName;
}
/**
* Set the {@link org.atmosphere.websocket.WebSocketProcessor} class name used to process WebSocket requests. Default is
* {@link DefaultWebSocketProcessor}
*
* @param webSocketProcessorClassName {@link org.atmosphere.websocket.WebSocketProcessor}
* @return this
*/
public AtmosphereFramework setWebsocketProcessorClassName(String webSocketProcessorClassName) {
this.webSocketProcessorClassName = webSocketProcessorClassName;
return this;
}
/**
* Add an {@link AtmosphereInterceptor} implementation. The adding order of {@link AtmosphereInterceptor} will be used, e.g
* the first added {@link AtmosphereInterceptor} will always be called first.
*
* @param c {@link AtmosphereInterceptor}
* @return this
*/
public AtmosphereFramework interceptor(AtmosphereInterceptor c) {
boolean found = false;
for (AtmosphereInterceptor interceptor : interceptors) {
if (interceptor.getClass().equals(c.getClass())) {
found = true;
break;
}
}
if (isInit) {
c.configure(config);
}
if (!found) {
InvokationOrder.PRIORITY p = InvokationOrder.class.isAssignableFrom(c.getClass()) ? InvokationOrder.class.cast(c).priority() : InvokationOrder.AFTER_DEFAULT;
positionInterceptor(p, c, interceptors);
logger.info("Installed AtmosphereInterceptor {} with priority {} ", c, p.name());
}
return this;
}
protected void positionInterceptor(InvokationOrder.PRIORITY p, AtmosphereInterceptor c, LinkedList interceptors) {
switch (p) {
case AFTER_DEFAULT:
interceptors.remove(c);
if (!checkDuplicate(c)) return;
interceptors.addLast(c);
break;
case BEFORE_DEFAULT:
interceptors.remove(c);
if (!checkDuplicate(c)) return;
int pos = executeFirstSet ? 1 : 0;
this.interceptors.add(pos, c);
break;
case FIRST_BEFORE_DEFAULT:
if (executeFirstSet) {
logger.warn("More than one AtmosphereInterceptor are defined to execute before the defaults. {} will be added before {}", c, interceptors.get(0));
}
interceptors.remove(c);
if (!checkDuplicate(c)) return;
logger.info("AtmosphereInterceptor {} will always be executed first", c);
interceptors.addFirst(c);
executeFirstSet = true;
break;
}
}
private boolean checkDuplicate(AtmosphereInterceptor c) {
for (AtmosphereInterceptor i : interceptors) {
if (i.getClass().equals(c.getClass()))
return false;
}
return true;
}
/**
* Return the list of {@link AtmosphereInterceptor}.
*
* @return the list of {@link AtmosphereInterceptor}
*/
public LinkedList interceptors() {
return interceptors;
}
/**
* Set the {@link AnnotationProcessor} class name.
*
* @param annotationProcessorClassName the {@link AnnotationProcessor} class name.
* @return this
*/
public AtmosphereFramework annotationProcessorClassName(String annotationProcessorClassName) {
this.annotationProcessorClassName = annotationProcessorClassName;
return this;
}
/**
* Add an {@link AsyncSupportListener}.
*
* @param asyncSupportListener an {@link AsyncSupportListener}
* @return this;
*/
public AtmosphereFramework asyncSupportListener(AsyncSupportListener asyncSupportListener) {
asyncSupportListeners.add(asyncSupportListener);
return this;
}
/**
* Return the list of {@link AsyncSupportListener}s.
*
* @return
*/
public List asyncSupportListeners() {
return asyncSupportListeners;
}
/**
* Add {@link BroadcasterListener} to all created {@link Broadcaster}s.
*/
public AtmosphereFramework addBroadcasterListener(BroadcasterListener b) {
broadcasterFactory.addBroadcasterListener(b);
broadcasterListeners.add(b);
return this;
}
/**
* Add {@link BroadcasterCacheListener} to the {@link BroadcasterCache}.
*/
public AtmosphereFramework addBroadcasterCacheListener(BroadcasterCacheListener b) {
broadcasterCacheListeners.add(b);
return this;
}
public List broadcasterCacheListeners() {
return broadcasterCacheListeners;
}
/**
* Add a {@link BroadcasterCacheInspector} which will be associated with the defined {@link BroadcasterCache}.
*
* @param b {@link BroadcasterCacheInspector}
* @return this;
*/
public AtmosphereFramework addBroadcasterCacheInjector(BroadcasterCacheInspector b) {
inspectors.add(b);
return this;
}
/**
* Return the list of {@link BroadcasterCacheInspector}s.
*
* @return the list of {@link BroadcasterCacheInspector}s
*/
public ConcurrentLinkedQueue inspectors() {
return inspectors;
}
/**
* Return a configured instance of {@link AtmosphereConfig}.
*
* @return a configured instance of {@link AtmosphereConfig}
*/
public AtmosphereConfig getAtmosphereConfig() {
return config;
}
/**
* Return the {@link ServletContext}
*
* @return the {@link ServletContext}
*/
public ServletContext getServletContext() {
return servletConfig.getServletContext();
}
public ServletConfig getServletConfig() {
return servletConfig;
}
/**
* Return the list of {@link BroadcastFilter}s.
*
* @return the list of {@link BroadcastFilter}s
*/
public List broadcasterFilters() {
return broadcasterFilters;
}
/**
* Add a {@link BroadcastFilter}.
*
* @return
*/
public AtmosphereFramework broadcasterFilters(BroadcastFilter f) {
broadcasterFilters.add(f.getClass().getName());
for (Broadcaster b : config.getBroadcasterFactory().lookupAll()) {
b.getBroadcasterConfig().addFilter(f);
}
return this;
}
/**
* Returns true if {@link java.util.concurrent.ExecutorService} is shared among all components.
*
* @return true if {@link java.util.concurrent.ExecutorService} is shared amongst all components
*/
public boolean isShareExecutorServices() {
return sharedThreadPools;
}
/**
* Set to true to have a {@link java.util.concurrent.ExecutorService} shared among all components.
*
* @param sharedThreadPools
* @return this
*/
public AtmosphereFramework shareExecutorServices(boolean sharedThreadPools) {
this.sharedThreadPools = sharedThreadPools;
return this;
}
protected void autoConfigureService(ServletContext sc) throws IOException {
String path = handlersPath != DEFAULT_HANDLER_PATH ? handlersPath : realPath(sc, handlersPath);
try {
annotationProcessor = newClassInstance(AnnotationProcessor.class,
(Class) IOUtils.loadClass(getClass(), annotationProcessorClassName));
logger.info("Atmosphere is using {} for processing annotation", annotationProcessorClassName);
annotationProcessor.configure(this);
if (packages.size() > 0) {
for (String s : packages) {
annotationProcessor.scan(s);
}
}
// Second try.
if (!annotationFound) {
if (path != null) {
annotationProcessor.scan(new File(path));
}
String pathLibs = libPath != DEFAULT_LIB_PATH ? libPath : realPath(sc, DEFAULT_LIB_PATH);
if (pathLibs != null) {
File libFolder = new File(pathLibs);
File jars[] = libFolder.listFiles(new FilenameFilter() {
@Override
public boolean accept(File arg0, String arg1) {
return arg1.endsWith(".jar");
}
});
if (jars != null) {
for (File file : jars) {
annotationProcessor.scan(file);
}
}
}
}
if (!annotationFound && allowAllClassesScan) {
logger.debug("Scanning all classes on the classpath");
annotationProcessor.scanAll();
}
} catch (Throwable e) {
logger.error("", e);
return;
} finally {
if (annotationProcessor != null) {
annotationProcessor.destroy();
}
}
}
/**
* The current {@link EndpointMapper} used to map requests to {@link AtmosphereHandler}.
*
* @return {@link EndpointMapper}
*/
public EndpointMapper endPointMapper() {
return endpointMapper;
}
/**
* Set the {@link EndpointMapper}.
*
* @param endpointMapper {@link EndpointMapper}
* @return this
*/
public AtmosphereFramework endPointMapper(EndpointMapper endpointMapper) {
this.endpointMapper = endpointMapper;
return this;
}
/**
* Add support for package detection of Atmosphere's Component.
*
* @param clazz a Class
* @return this.
*/
public AtmosphereFramework addAnnotationPackage(Class> clazz) {
if (clazz.getPackage() == null) {
logger.error("Class {} must have a package defined", clazz);
} else {
packages.add(clazz.getPackage().getName());
}
return this;
}
public void notify(Action.TYPE type, AtmosphereRequest request, AtmosphereResponse response) {
for (AsyncSupportListener l : asyncSupportListeners()) {
try {
switch (type) {
case TIMEOUT:
l.onTimeout(request, response);
break;
case CANCELLED:
l.onClose(request, response);
break;
case SUSPEND:
l.onSuspend(request, response);
break;
case RESUME:
l.onResume(request, response);
break;
case DESTROYED:
l.onDestroyed(request, response);
break;
}
} catch (Throwable t) {
logger.warn("", t);
}
}
}
/**
* Add an {@link WebSocketHandler} mapped to "/*".
* return this
*/
public AtmosphereFramework addWebSocketHandler(WebSocketHandler handler) {
addWebSocketHandler(ROOT_MASTER, handler);
return this;
}
/**
* Add an {@link WebSocketHandler} mapped to the path.
* return this
*/
public AtmosphereFramework addWebSocketHandler(String path, WebSocketHandler handler) {
addWebSocketHandler(path, handler, REFLECTOR_ATMOSPHEREHANDLER, Collections.EMPTY_LIST);
return this;
}
/**
* Add an {@link WebSocketHandler} mapped to the path and the {@link AtmosphereHandler} in case {@link Broadcaster} are
* used.
*
* @param path a path
* @param handler a {@link WebSocketHandler}
* @param h an {@link AtmosphereHandler}
* @return this
*/
public AtmosphereFramework addWebSocketHandler(String path, WebSocketHandler handler, AtmosphereHandler h) {
addWebSocketHandler(path, handler, REFLECTOR_ATMOSPHEREHANDLER, Collections.EMPTY_LIST);
return this;
}
/**
* Add an {@link WebSocketHandler} mapped to the path and the {@link AtmosphereHandler} in case {@link Broadcaster} are
* used.
*
* @param path a path
* @param handler a {@link WebSocketHandler}
* @param h an {@link AtmosphereHandler}
* @param l {@link AtmosphereInterceptor}
* @return this
*/
public AtmosphereFramework addWebSocketHandler(String path, WebSocketHandler handler, AtmosphereHandler h, List l) {
WebSocketProcessorFactory.getDefault().getWebSocketProcessor(this)
.registerWebSocketHandler(path,
new WebSocketProcessor.WebSocketHandlerProxy(broadcasterFactory.lookup(path, true).getClass(), handler, interceptors));
addAtmosphereHandler(path, h, l);
return this;
}
/**
* Invoked when a {@link AnnotationProcessor} found an annotation.
*
* @param b true when found
* @return this
*/
public AtmosphereFramework annotationScanned(boolean b) {
annotationFound = b;
return this;
}
/**
* Return true if the {@link #init()} has been sucessfully executed.
*
* @return true if the {@link #init()} has been sucessfully executed.
*/
public boolean initialized() {
return isInit;
}
public List packages() {
return packages;
}
/**
* Return the list of packages the framework should look for {@link org.atmosphere.config.AtmosphereAnnotation}.
*
* @return the list of packages the framework should look for {@link org.atmosphere.config.AtmosphereAnnotation}
*/
public List customAnnotationPackages() {
return annotationPackages;
}
/**
* Add a package containing classes annotated with {@link org.atmosphere.config.AtmosphereAnnotation}.
*
* @param p a package
* @return this;
*/
public AtmosphereFramework addCustomAnnotationPackage(Class p) {
annotationPackages.addLast(p.getPackage().getName());
return this;
}
/**
* Instantiate a class
*
* @param classType The Required Class's Type
* @param defaultType The default implementation of the Class's Type.
* @return the an instance of defaultType
* @throws InstantiationException
* @throws IllegalAccessException
*/
public T newClassInstance(Class classType, Class defaultType) throws InstantiationException, IllegalAccessException {
return objectFactory.newClassInstance(this, classType, defaultType);
}
/**
* Set an object used for class instantiation.
* Allows for integration with dependency injection frameworks.
*
* @param objectFactory
*/
public void objectFactory(AtmosphereObjectFactory objectFactory) {
this.objectFactory = objectFactory;
}
/**
* If set to true, the task of finishing the request/response lifecycle will not be handled by this class.
*
* @param externalizeDestroy
* @return this
*/
public AtmosphereFramework externalizeDestroy(boolean externalizeDestroy) {
this.externalizeDestroy = externalizeDestroy;
return this;
}
/**
* Return the {@link AnnotationProcessor}
*
* @return the {@link AnnotationProcessor}
*/
public AnnotationProcessor annotationProcessor() {
return annotationProcessor;
}
/**
* Was a {@link Broadcaster} defined in web.xml or programmatically added.
*
* @return true is defined.
*/
public boolean isBroadcasterSpecified() {
return isBroadcasterSpecified;
}
protected void configureObjectFactory() {
String s = config.getInitParameter(ApplicationConfig.OBJECT_FACTORY);
if (s != null) {
try {
AtmosphereObjectFactory aci = (AtmosphereObjectFactory) IOUtils.loadClass(getClass(), s).newInstance();
if (aci != null) {
logger.debug("Found ObjectFactory {}", aci.getClass().getName());
objectFactory(aci);
}
} catch (Exception ex) {
logger.warn("Unable to load AtmosphereClassInstantiator instance", ex);
}
}
if (!DefaultAtmosphereObjectFactory.class.isAssignableFrom(objectFactory.getClass())) {
logger.trace("ObjectFactory already set to {}", objectFactory);
return;
}
}
/**
* Exclude an {@link AtmosphereInterceptor} from being added, at startup, by Atmosphere. The default's {@link #defaultInterceptors}
* are candidates for being excluded.
*
* @param interceptor an {@link AtmosphereInterceptor} class name
* @return this
*/
public AtmosphereFramework excludeInterceptor(String interceptor) {
excludedInterceptors.add(interceptor);
return this;
}
public AtmosphereFramework filterManipulator(BroadcasterConfig.FilterManipulator m) {
filterManipulators.add(m);
return this;
}
public List filterManipulators() {
return filterManipulators;
}
public boolean isAServletFilter() {
return isFilter;
}
public ConcurrentLinkedQueue objectFactoryType() {
return objectFactoryType;
}
public String mappingRegex() {
return mappingRegex;
}
public AtmosphereFramework mappingRegex(String mappingRegex) {
this.mappingRegex = mappingRegex;
return this;
}
public void setUseServlet30(boolean useServlet30) {
this.useServlet30 = useServlet30;
}
public boolean webSocketEnabled() {
return webSocketEnabled;
}
public AtmosphereFramework webSocketEnabled(boolean webSocketEnabled) {
this.webSocketEnabled = webSocketEnabled;
return this;
}
public String broadcasterLifeCyclePolicy() {
return broadcasterLifeCyclePolicy;
}
public AtmosphereFramework broadcasterLifeCyclePolicy(String broadcasterLifeCyclePolicy) {
this.broadcasterLifeCyclePolicy = broadcasterLifeCyclePolicy;
return this;
}
public List broadcasterListeners() {
return broadcasterListeners;
}
public boolean sharedThreadPools() {
return sharedThreadPools;
}
public AtmosphereFramework sharedThreadPools(boolean sharedThreadPools) {
this.sharedThreadPools = sharedThreadPools;
return this;
}
public boolean allowAllClassesScan() {
return allowAllClassesScan;
}
public AtmosphereFramework allowAllClassesScan(boolean allowAllClassesScan) {
this.allowAllClassesScan = allowAllClassesScan;
return this;
}
public AtmosphereObjectFactory objectFactory() {
return objectFactory;
}
public boolean externalizeDestroy() {
return externalizeDestroy;
}
public List excludedInterceptors() {
return excludedInterceptors;
}
public Class extends AtmosphereInterceptor>[] defaultInterceptors() {
return defaultInterceptors;
}
public AtmosphereResourceFactory atmosphereFactory() {
if (arFactory == null) {
configureAtmosphereResourceFactory();
}
return arFactory;
}
private AtmosphereFramework configureAtmosphereResourceFactory() {
try {
arFactory = newClassInstance(AtmosphereResourceFactory.class, DefaultAtmosphereResourceFactory.class);
} catch (InstantiationException e) {
logger.error("", e);
} catch (IllegalAccessException e) {
logger.error("", e);
}
arFactory.configure(config);
return this;
}
public MetaBroadcaster metaBroadcaster() {
if (metaBroadcaster == null) {
configureMetaBroadcaster();
}
return metaBroadcaster;
}
private AtmosphereFramework configureMetaBroadcaster() {
if (metaBroadcaster == null) {
metaBroadcaster = new MetaBroadcaster(config);
}
return this;
}
/**
* Get the default {@link org.atmosphere.cpr.Serializer} class name to use for {@link org.atmosphere.cpr.AtmosphereResource}s.
*
* @return the class name as a string, might be null if not configured
*/
public String getDefaultSerializerClassName() {
return defaultSerializerClassName;
}
/**
* Get the default {@link org.atmosphere.cpr.Serializer} class to use for {@link org.atmosphere.cpr.AtmosphereResource}s.
*
* @return the class, might be null if not configured
*/
public Class getDefaultSerializerClass() {
return defaultSerializerClass;
}
/**
* Set the default {@link org.atmosphere.cpr.Serializer} class name to use for {@link org.atmosphere.cpr.AtmosphereResource}s.
*
* @param defaultSerializerClassName the class name to use
* @return this
*/
public AtmosphereFramework setDefaultSerializerClassName(String defaultSerializerClassName) {
this.defaultSerializerClassName = defaultSerializerClassName;
initDefaultSerializer();
return this;
}
private void initDefaultSerializer() {
if (defaultSerializerClassName != null && !defaultSerializerClassName.isEmpty()) {
try {
@SuppressWarnings("unchecked")
Class clazz = (Class) IOUtils.loadClass(Serializer.class, defaultSerializerClassName);
if (Serializer.class.isAssignableFrom(clazz)) {
defaultSerializerClass = clazz;
}
else {
logger.error("Default Serializer class name does not implement Serializer interface");
defaultSerializerClassName = null;
defaultSerializerClass = null;
}
} catch (Exception e) {
logger.error("Unable to set default Serializer", e);
defaultSerializerClassName = null;
defaultSerializerClass = null;
}
}
else {
defaultSerializerClassName = null;
defaultSerializerClass = null;
}
}
/**
* Return true is the {@link #destroy()} method has been invoked.
* @return true is the {@link #destroy()} method has been invoked.
*/
public boolean isDestroyed(){
return isDestroyed.get();
}
}