All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.atmosphere.cpr.AtmosphereServlet Maven / Gradle / Ivy

There is a newer version: 0.7.2
Show newest version
/*
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 *
 * Contributor(s):
 *
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 *
 */

package org.atmosphere.cpr;

import org.apache.catalina.CometEvent;
import org.apache.catalina.CometProcessor;
import org.atmosphere.container.BlockingIOCometSupport;
import org.atmosphere.container.GoogleAppEngineCometSupport;
import org.atmosphere.container.JBossWebCometSupport;
import org.atmosphere.container.TomcatCometSupport;
import org.atmosphere.container.WebLogicCometSupport;
import org.atmosphere.handler.ReflectorServletProcessor;
import org.atmosphere.util.AtmosphereConfigReader;
import org.atmosphere.util.AtmosphereConfigReader.Property;
import org.atmosphere.util.IntrospectionUtils;
import org.atmosphere.util.LoggerUtils;
import org.atmosphere.util.Version;
import org.atmosphere.util.gae.GAEBroadcasterConfig;
import org.atmosphere.websocket.JettyWebSocketSupport;
import org.atmosphere.websocket.WebSocketAtmosphereHandler;
import org.eclipse.jetty.websocket.WebSocket;
import org.jboss.servlet.http.HttpEvent;
import org.jboss.servlet.http.HttpEventServlet;
import weblogic.servlet.http.AbstractAsyncServlet;
import weblogic.servlet.http.RequestResponseKey;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * The {@link AtmosphereServlet} acts as a dispatcher for {@link AtmosphereHandler}
 * defined in META-INF/atmosphere.xml, or if atmosphere.xml is missing, all classes
 * that implements {@link AtmosphereHandler} will be discovered and mapped using
 * the class's name.
 * 

* This {@link Servlet} can be defined inside an application's web.xml using the following: *


 *  <servlet>
 *      <description>AtmosphereServlet</description>
 *      <servlet-name>AtmosphereServlet</servlet-name>
 *      <servlet-class>org.atmosphere.cpr.AtmosphereServlet</servlet-class>
 *      <load-on-startup>0 </load-on-startup>
 *  </servlet>
 *  <servlet-mapping>
 *      <servlet-name>AtmosphereServlet</servlet-name>
 *      <url-pattern>/Atmosphere </url-pattern>
 *  </servlet-mapping>
 * 

* You can force this Servlet to use native API of the Web Server instead of * the Servlet 3.0 Async API you are deploying on by adding *


 *  <init-param>
 *      <param-name>org.atmosphere.useNative</param-name>
 *      <param-value>true</param-value>
 *  </init-param>
 * 

* You can force this Servlet to use one Thread per connection instead of * native API of the Web Server you are deploying on by adding *


 *  <init-param>
 *      <param-name>org.atmosphere.useBlocking</param-name>
 *      <param-value>true</param-value>
 *  </init-param>
 * 

* You can also define {@link Broadcaster}by adding: *


 *  <init-param>
 *      <param-name>org.atmosphere.cpr.broadcasterClass</param-name>
 *      <param-value>class-name</param-value>
 *  </init-param>
 * 

* You can also for Atmosphere to use {@link java.io.OutputStream} for all write operations. *


 *  <init-param>
 *      <param-name>org.atmosphere.useStream</param-name>
 *      <param-value>true</param-value>
 *  </init-param>
 * 

* You can also configure {@link org.atmosphere.cpr.BroadcasterCache} that persist message when Browser is disconnected. *


 *  <init-param>
 *      <param-name>org.atmosphere.cpr.broadcasterCacheClass</param-name>
 *      <param-value>class-name</param-value>
 *  </init-param>
 * 

* You can also configure Atmosphere to use http session or not *


 *  <init-param>
 *      <param-name>org.atmosphere.cpr.sessionSupport</param-name>
 *      <param-value>false</param-value>
 *  </init-param>
 * 

* You can also configure {@link BroadcastFilter} that will be applied at all newly created {@link Broadcaster} *


 *  <init-param>
 *      <param-name>org.atmosphere.cpr.broadcastFilterClasses</param-name>
 *      <param-value>BroadcastFilter class name separated by coma</param-value>
 *  </init-param>
 * 

* The Atmosphere Framework can also be used as a Servlet Filter ({@link AtmosphereFilter}). *

* If you are planning to use JSP, Servlet or JSF, you can instead use the * {@link MeteorServlet}, which allow the use of {@link Meteor} inside those * components. * * @author Jeanfrancois Arcand */ public class AtmosphereServlet extends AbstractAsyncServlet implements CometProcessor, HttpEventServlet { public final static String JERSEY_BROADCASTER = "org.atmosphere.jersey.JerseyBroadcaster"; public final static String JERSEY_CONTAINER = "com.sun.jersey.spi.container.servlet.ServletContainer"; public final static String GAE_BROADCASTER = org.atmosphere.util.gae.GAEDefaultBroadcaster.class.getName(); public final static String PROPERTY_SERVLET_MAPPING = "org.atmosphere.jersey.servlet-mapping"; public final static String PROPERTY_BLOCKING_COMETSUPPORT = "org.atmosphere.useBlocking"; public final static String PROPERTY_NATIVE_COMETSUPPORT = "org.atmosphere.useNative"; public final static String WEBSOCKET_SUPPORT = "org.atmosphere.useWebSocket"; public final static String PROPERTY_USE_STREAM = "org.atmosphere.useStream"; public final static String BROADCASTER_FACTORY = "org.atmosphere.cpr.broadcasterFactory"; public final static String BROADCASTER_CLASS = "org.atmosphere.cpr.broadcasterClass"; public final static String BROADCASTER_CACHE = "org.atmosphere.cpr.broadcasterCacheClass"; public final static String PROPERTY_COMET_SUPPORT = "org.atmosphere.cpr.cometSupport"; public final static String PROPERTY_SESSION_SUPPORT = "org.atmosphere.cpr.sessionSupport"; public final static String PRIMEFACES_SERVLET = "org.primefaces.comet.PrimeFacesCometServlet"; public final static String DISABLE_ONSTATE_EVENT = "org.atmosphere.disableOnStateEvent"; public final static String WEB_INF = "/WEB-INF/classes/"; public final static String RESUME_ON_BROADCAST = "org.atmosphere.resumeOnBroadcast"; public final static String ATMOSPHERE_SERVLET = AtmosphereServlet.class.getName(); public final static String ATMOSPHERE_RESOURCE = AtmosphereResource.class.getName(); public final static String SUPPORT_SESSION = "org.atmosphere.cpr.AsynchronousProcessor.supportSession"; public final static String ATMOSPHERE_HANDLER = AtmosphereHandler.class.getName(); public final static String WEBSOCKET_ATMOSPHEREHANDLER = WebSocketAtmosphereHandler.class.getName(); public final static Logger logger = LoggerUtils.getLogger(); public final static String RESUME_AND_KEEPALIVE = AtmosphereServlet.class.getName() + ".resumeAndKeepAlive"; public final static String RESUMED_ON_TIMEOUT = AtmosphereServlet.class.getName() + ".resumedOnTimeout"; public final static String DEFAULT_NAMED_DISPATCHER = "default"; public final static String BROADCAST_FILTER_CLASSES = "org.atmosphere.cpr.broadcastFilterClasses"; protected final ArrayList possibleAtmosphereHandlersCandidate = new ArrayList(); protected final HashMap initParams = new HashMap(); protected final AtmosphereConfig config = new AtmosphereConfig(); protected final AtomicBoolean isCometSupportConfigured = new AtomicBoolean(false); protected final boolean isFilter; /** * The list of {@link AtmosphereHandler} and their associated mapping. */ private final Map atmosphereHandlers = new ConcurrentHashMap(); // If we detect Servlet 3.0, should we still use the default // native Comet API. protected boolean useNativeImplementation = false; protected boolean useBlockingImplementation = false; protected boolean useStreamForFlushingComments = false; protected CometSupport cometSupport; protected static String broadcasterClassName = DefaultBroadcaster.class.getName(); protected boolean isCometSupportSpecified = false; protected boolean isBroadcasterSpecified = false; protected boolean isSessionSupportSpecified = false; private BroadcasterFactory broadcasterFactory; private static BroadcasterConfig broadcasterConfig = new BroadcasterConfig(); private String broadcasterCacheClassName; private boolean webSocketEnabled = false; public final static class AtmosphereHandlerWrapper { public final AtmosphereHandler atmosphereHandler; public Broadcaster broadcaster; public AtmosphereHandlerWrapper(AtmosphereHandler atmosphereHandler) { this.atmosphereHandler = atmosphereHandler; try { this.broadcaster = BroadcasterFactory.getDefault().get(); } catch (Exception t) { throw new RuntimeException(t); } } public AtmosphereHandlerWrapper(AtmosphereHandler atmosphereHandler, Broadcaster broadcaster) { this.atmosphereHandler = atmosphereHandler; this.broadcaster = broadcaster; } } /** * Return a configured instance of {@link AtmosphereConfig} * * @return a configured instance of {@link AtmosphereConfig} */ public AtmosphereConfig getAtmosphereConfig() { return config; } public class AtmosphereConfig { private boolean supportSession = true; private BroadcasterFactory broadcasterFactory; private String dispatcherName = DEFAULT_NAMED_DISPATCHER; protected Map handlers() { return AtmosphereServlet.this.atmosphereHandlers; } public ServletContext getServletContext() { return AtmosphereServlet.this.getServletContext(); } public String getDispatcherName() { return dispatcherName; } public void setDispatcherName(String dispatcherName) { this.dispatcherName = dispatcherName; } public String getInitParameter(String name) { // First looks locally String s = initParams.get(name); if (s != null) { return s; } return AtmosphereServlet.this.getInitParameter(name); } public Enumeration getInitParameterNames() { return AtmosphereServlet.this.getInitParameterNames(); } public ServletConfig getServletConfig() { return AtmosphereServlet.this.getServletConfig(); } public String getWebServerName() { return AtmosphereServlet.this.cometSupport.getContainerName(); } public boolean mapBroadcasterToAtmosphereHandler(Broadcaster bc, AtmosphereHandlerWrapper ahw) { if (atmosphereHandlers.get(bc.getID()) == null) { atmosphereHandlers.put(bc.getID(), ahw); return true; } return false; } /** * Return the {@link AtmosphereHandler} associated with this {@link Broadcaster}. * * @param bc The {@link Broadcaster} * @return the {@link AtmosphereHandler} associated with this {@link Broadcaster}. */ public AtmosphereHandler getAtmosphereHandler(Broadcaster bc) { AtmosphereHandler h = atmosphereHandlers.get(bc.getID()).atmosphereHandler; if (h == null) { for (AtmosphereHandlerWrapper ah : atmosphereHandlers.values()) { if (ah.broadcaster == bc) { atmosphereHandlers.put(bc.getID(), ah); return ah.atmosphereHandler; } } throw new IllegalStateException("Unable to find associated AtmosphereHandler"); } else { return h; } } /** * Return an instance of a {@link DefaultBroadcasterFactory} * * @return an instance of a {@link DefaultBroadcasterFactory} */ public BroadcasterFactory getBroadcasterFactory() { return broadcasterFactory; } public boolean isSupportSession() { return supportSession; } public void setSupportSession(boolean supportSession) { this.supportSession = supportSession; } public AtmosphereServlet getServlet() { return AtmosphereServlet.this; } } /** * Simple class/struck that hold the current state. */ public static class Action { public enum TYPE { SUSPEND, RESUME, TIMEOUT, CANCELLED, KEEP_ALIVED } public long timeout = -1L; public TYPE type; public Action() { type = TYPE.CANCELLED; } public Action(TYPE type) { this.type = type; } public Action(TYPE type, long timeout) { this.timeout = timeout; this.type = type; } } /** * Create an Atmosphere Servlet. */ public AtmosphereServlet() { this(false); } /** * Create an Atmosphere Servlet. * * @param isFilter true if this instance is used as an {@link AtmosphereFilter} */ public AtmosphereServlet(boolean isFilter) { this.isFilter = isFilter; readSystemProperties(); } /** * 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 void addAtmosphereHandler(String mapping, AtmosphereHandler h) { if (!mapping.startsWith("/")) { mapping = "/" + mapping; } AtmosphereHandlerWrapper w = new AtmosphereHandlerWrapper(h); atmosphereHandlers.put(mapping, w); } /** * 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 void addAtmosphereHandler(String mapping, AtmosphereHandler h, String broadcasterId) { if (!mapping.startsWith("/")) { mapping = "/" + mapping; } AtmosphereHandlerWrapper w = new AtmosphereHandlerWrapper(h); w.broadcaster.setID(broadcasterId); atmosphereHandlers.put(mapping, w); } /** * 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 void addAtmosphereHandler(String mapping, AtmosphereHandler h, Broadcaster broadcaster) { if (!mapping.startsWith("/")) { mapping = "/" + mapping; } AtmosphereHandlerWrapper w = new AtmosphereHandlerWrapper(h, broadcaster); atmosphereHandlers.put(mapping, w); } /** * Remove an {@link AtmosphereHandler} * * @param mapping the mapping used when invoking {@link #addAtmosphereHandler(String, AtmosphereHandler)}; * @return true if removed */ public boolean removeAtmosphereHandler(String mapping) { return atmosphereHandlers.remove(mapping) == null ? false : true; } /** * Remove all {@link AtmosphereHandler} */ public void removeAllAtmosphereHandler() { atmosphereHandlers.clear(); } /** * Remove all init parameters. */ public void removeAllInitParams() { initParams.clear(); } /** * Add init-param like if they were defined in web.xml * * @param name The name * @param value The value */ public void addInitParameter(String name, String value) { initParams.put(name, value); } 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; } if (System.getProperty(DISABLE_ONSTATE_EVENT) != null) { initParams.put(DISABLE_ONSTATE_EVENT, System.getProperty(DISABLE_ONSTATE_EVENT)); } } /** * Load the {@link AtmosphereHandler} associated with this AtmosphereServlet. * * @param sc the {@link ServletContext} */ @Override public void init(final ServletConfig sc) throws ServletException { try { super.init(sc); ServletConfig scFacade = new ServletConfig() { public String getServletName() { return sc.getServletName(); } public ServletContext getServletContext() { return sc.getServletContext(); } public String getInitParameter(String name) { String param = sc.getInitParameter(name); if (param == null) { return initParams.get(name); } return param; } public Enumeration getInitParameterNames() { return sc.getInitParameterNames(); } }; doInitParams(scFacade); detectGoogleAppEngine(scFacade); loadConfiguration(scFacade); autoDetectContainer(); configureBroadcaster(); cometSupport.init(scFacade); initAtmosphereServletProcessor(scFacade); logger.log(Level.INFO, "Atmosphere Framework " + Version.getRawVersion() + " started."); } catch (Throwable t) { logger.log(Level.SEVERE, "", t); if (t instanceof ServletException) throw (ServletException) t; throw new ServletException(t.getCause()); } } protected void configureBroadcaster() throws ClassNotFoundException, InstantiationException, IllegalAccessException { if (broadcasterFactory == null) { broadcasterFactory = new DefaultBroadcasterFactory((Class) Thread.currentThread().getContextClassLoader().loadClass(broadcasterClassName), broadcasterConfig); config.broadcasterFactory = broadcasterFactory; } 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(); } else { w.broadcaster.setBroadcasterConfig(broadcasterConfig); } w.broadcaster.setID(e.getKey()); } if (broadcasterCacheClassName != null) { broadcasterConfig.setBroadcasterCache((BroadcasterCache) Thread.currentThread().getContextClassLoader().loadClass(broadcasterCacheClassName).newInstance()); } logger.info("Using " + broadcasterClassName + " with a Thread Pool size of : " + BroadcasterConfig.numOfProcessor); } /** * Read init param from web.xml and apply them. * * @param sc {@link ServletConfig} */ protected void doInitParams(ServletConfig sc) { 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) { cometSupport = new DefaultCometSupportResolver(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) { config.supportSession = Boolean.valueOf(s); isSessionSupportSpecified = true; } s = sc.getInitParameter(WEBSOCKET_ATMOSPHEREHANDLER); if (s != null) { addAtmosphereHandler("/*", new WebSocketAtmosphereHandler()); webSocketEnabled = true; sessionSupport(false); } s = sc.getInitParameter(WEBSOCKET_SUPPORT); if (s != null) { webSocketEnabled = true; sessionSupport(false); } 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(RESUME_AND_KEEPALIVE); if (s != null) { initParams.put(RESUME_AND_KEEPALIVE, s); } s = sc.getInitParameter(BROADCAST_FILTER_CLASSES); if (s != null) { configureBroadcasterFilter(s.split(",")); } } protected void loadConfiguration(ServletConfig sc) throws ServletException { try { //TODO -> Add support for WEB-INF/lib/*.jar URL url = sc.getServletContext().getResource("/WEB-INF/classes/"); URLClassLoader urlC = new URLClassLoader(new URL[]{url}, Thread.currentThread().getContextClassLoader()); loadAtmosphereDotXml(sc.getServletContext(). getResourceAsStream("/META-INF/atmosphere.xml"), urlC); if (atmosphereHandlers.size() == 0) { autoDetectAtmosphereHandlers(sc.getServletContext(), urlC); if (atmosphereHandlers.size() == 0) { detectSupportedFramework(sc); } } } catch (Throwable t) { throw new ServletException(t); } } void configureBroadcasterFilter(String[] list){ for (String broadcastFilter: list) { try { broadcasterConfig.addFilter(BroadcastFilter.class.cast( Thread.currentThread().getContextClassLoader().loadClass(broadcastFilter).newInstance())); } catch (InstantiationException e) { logger.log(Level.WARNING,String.format("Error trying to instanciate BroadcastFilter %s",broadcastFilter),e); } catch (IllegalAccessException e) { logger.log(Level.WARNING,String.format("Error trying to instanciate BroadcastFilter %s",broadcastFilter),e); } catch (ClassNotFoundException e) { logger.log(Level.WARNING,String.format("Error trying to instanciate BroadcastFilter %s",broadcastFilter),e); } } } /** * Auto-detect Jersey when no atmosphere.xml file are specified. * * @param sc {@link ServletConfig} * @return true if Jersey classes are detected * @throws ClassNotFoundException */ protected boolean detectSupportedFramework(ServletConfig sc) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { // If Primefaces is detected, never starts Jersey. // TODO: Remove this hack once properly implemented in PrimeFaces ClassLoader cl = Thread.currentThread().getContextClassLoader(); try { cl.loadClass(PRIMEFACES_SERVLET); return false; } catch (Throwable ignored) { } try { cl.loadClass(JERSEY_CONTAINER); useStreamForFlushingComments = true; } catch (Throwable t) { return false; } logger.warning("Missing META-INF/atmosphere.xml but found the Jersey runtime. Starting Jersey"); ReflectorServletProcessor rsp = new ReflectorServletProcessor(); if (!isBroadcasterSpecified) broadcasterClassName = JERSEY_BROADCASTER; rsp.setServletClassName(JERSEY_CONTAINER); sessionSupport(false); initParams.put(DISABLE_ONSTATE_EVENT, "true"); String mapping = sc.getInitParameter(PROPERTY_SERVLET_MAPPING); if (mapping == null) { mapping = "/*"; } Class bc = (Class) cl.loadClass(broadcasterClassName); Broadcaster b = bc.getDeclaredConstructor(new Class[]{String.class}).newInstance(mapping); addAtmosphereHandler(mapping, rsp, b); return true; } protected void sessionSupport(boolean sessionSupport) { if (!isSessionSupportSpecified) { config.supportSession = sessionSupport; } } /** * Auto-Detect Google App Engine. * * @param sc (@link ServletConfig} * @return true if detected */ boolean detectGoogleAppEngine(ServletConfig sc) { if (sc.getServletContext().getServerInfo().startsWith("Google")) { broadcasterClassName = GAE_BROADCASTER; isBroadcasterSpecified = true; cometSupport = new GoogleAppEngineCometSupport(config); broadcasterConfig = new GAEBroadcasterConfig(); return true; } else { return false; } } /** * Initialize {@link AtmosphereServletProcessor} * * @param sc the {@link ServletConfig} * @throws javax.servlet.ServletException */ void initAtmosphereServletProcessor(ServletConfig sc) throws ServletException { AtmosphereHandler a; for (Entry h : atmosphereHandlers.entrySet()) { a = h.getValue().atmosphereHandler; if (a instanceof AtmosphereServletProcessor) { ((AtmosphereServletProcessor) a).init(sc); } } } @Override public void destroy() { if (cometSupport != null && AsynchronousProcessor.class.isAssignableFrom(cometSupport.getClass())) { ((AsynchronousProcessor) cometSupport).shutdown(); } AtmosphereHandler a; for (Entry h : atmosphereHandlers.entrySet()) { a = h.getValue().atmosphereHandler; if (a instanceof AtmosphereServletProcessor) { ((AtmosphereServletProcessor) a).destroy(); } h.getValue().broadcaster.destroy(); } BroadcasterFactory.factory = null; BroadcasterFactory.getDefault().destroy(); broadcasterConfig = new BroadcasterConfig(); } /** * 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; } AtmosphereConfigReader reader = new AtmosphereConfigReader(stream); Map atmosphereHandlerNames = reader.getAtmosphereHandlers(); Set> entries = atmosphereHandlerNames.entrySet(); for (Entry entry : entries) { AtmosphereHandler g; try { if (!entry.getValue().equals(ReflectorServletProcessor.class.getName())) { g = (AtmosphereHandler) c.loadClass(entry.getValue()).newInstance(); } else { g = new ReflectorServletProcessor(); } logger.info("Sucessfully loaded " + g + " mapped to context-path " + entry.getKey()); AtmosphereHandlerWrapper wrapper = new AtmosphereHandlerWrapper(g); atmosphereHandlers.put(entry.getKey(), wrapper); boolean isJersey = false; for (Property p : reader.getProperty(entry.getKey())) { if (p.value != null && p.value.indexOf("jersey") != -1) { isJersey = true; initParams.put(DISABLE_ONSTATE_EVENT, "true"); useStreamForFlushingComments = true; } IntrospectionUtils.setProperty(g, p.name, p.value); } config.supportSession = !isJersey; if (!reader.supportSession().equals("")) { sessionSupport(Boolean.valueOf(reader.supportSession())); } for (Property p : reader.getProperty(entry.getKey())) { IntrospectionUtils.addProperty(g, p.name, p.value); } String broadcasterClass = reader.getBroadcasterClass(entry.getKey()); /** * 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 bc = (Class) cl.loadClass(broadcasterClassName); wrapper.broadcaster = BroadcasterFactory.getDefault().get(bc, entry.getKey()); } String bc = reader.getBroadcasterCache(entry.getKey()); if (bc != null) { broadcasterCacheClassName = bc; } if (reader.getCometSupportClass() != null) { cometSupport = (CometSupport) c.loadClass(reader.getCometSupportClass()).newInstance(); } if (reader.getBroadcastFilterClasses() != null){ configureBroadcasterFilter(reader.getBroadcastFilterClasses()); } } catch (Throwable t) { logger.log(Level.WARNING, "Unable to load AtmosphereHandler class: " + entry.getValue(), t); throw new ServletException(t); } } } /** * Set the {@link CometSupport} implementation. Make sure you don't set * an implementation that only works on some Container. See {@link BlockingIOCometSupport} * for an example. * * @param cometSupport */ public void setCometSupport(CometSupport cometSupport) { this.cometSupport = cometSupport; } /** * Return the current {@link CometSupport} * * @return the current {@link CometSupport} */ public CometSupport getCometSupport() { return cometSupport; } /** * Returns an instance of CometSupportResolver {@link CometSupportResolver} * * @return CometSupportResolver */ protected CometSupportResolver createCometSupportResolver() { return new DefaultCometSupportResolver(config); } /** * Auto detect the underlying Servlet Container we are running on. */ protected void autoDetectContainer() { // Was defined in atmosphere.xml if (getCometSupport() == null) { setCometSupport(createCometSupportResolver().resolve(useNativeImplementation, useBlockingImplementation, webSocketEnabled)); } logger.info("Atmosphere is using for CometSupport: " + getCometSupport().getClass().getName() + " running under container " + cometSupport.getContainerName()); } /** * Auto detect instance of {@link AtmosphereHandler} in case META-INF/atmosphere.xml * is missing. * * @param sc {@link ServletContext} * @param c {@link URLClassLoader} to load the class. * @throws java.net.MalformedURLException * @throws java.net.URISyntaxException */ protected void autoDetectAtmosphereHandlers(ServletContext sc, URLClassLoader c) throws MalformedURLException, URISyntaxException { String s = sc.getRealPath(WEB_INF); // Weblogic bug if (s == null) { URL u = sc.getResource(WEB_INF); if (u == null) return; s = u.getPath(); } File f = new File(s); if (f.isDirectory()) { getFiles(f); for (String className : possibleAtmosphereHandlersCandidate) { try { className = className.replace('\\', '/'); className = className.substring(className.indexOf(WEB_INF) + WEB_INF.length(), className.lastIndexOf(".")).replace('/', '.'); Class clazz = c.loadClass(className); if (AtmosphereHandler.class.isAssignableFrom(clazz)) { AtmosphereHandler g = (AtmosphereHandler) clazz.newInstance(); logger.info("Successfully loaded " + g + " mapped to context-path " + g.getClass().getSimpleName()); atmosphereHandlers.put("/" + g.getClass().getSimpleName(), new AtmosphereHandlerWrapper(g, null)); } } catch (Throwable t) { logger.finest(className + " is not a AtmosphereHandler"); } } } logger.info("Atmosphere using Broadcaster " + broadcasterClassName); } /** * Get the list of possible candidate to load as {@link AtmosphereHandler} * * @param f the real path {@link File} */ protected void getFiles(File f) { File[] files = f.listFiles(); for (File test : files) { if (test.isDirectory()) { getFiles(test); } else { String clazz = test.getAbsolutePath(); if (clazz.endsWith(".class")) { possibleAtmosphereHandlersCandidate.add(clazz); } } } } /** * Delegate the request processing to an instance of {@link CometSupport} * * @param req the {@link HttpServletRequest} * @param res the {@link HttpServletResponse} * @throws java.io.IOException * @throws javax.servlet.ServletException */ @Override public void doHead(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { doPost(req, res); } /** * Delegate the request processing to an instance of {@link CometSupport} * * @param req the {@link HttpServletRequest} * @param res the {@link HttpServletResponse} * @throws java.io.IOException * @throws javax.servlet.ServletException */ @Override public void doOptions(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { doPost(req, res); } /** * Delegate the request processing to an instance of {@link CometSupport} * * @param req the {@link HttpServletRequest} * @param res the {@link HttpServletResponse} * @throws java.io.IOException * @throws javax.servlet.ServletException */ @Override public void doTrace(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { doPost(req, res); } /** * Delegate the request processing to an instance of {@link CometSupport} * * @param req the {@link HttpServletRequest} * @param res the {@link HttpServletResponse} * @throws java.io.IOException * @throws javax.servlet.ServletException */ @Override public void doDelete(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { doPost(req, res); } /** * Delegate the request processing to an instance of {@link CometSupport} * * @param req the {@link HttpServletRequest} * @param res the {@link HttpServletResponse} * @throws java.io.IOException * @throws javax.servlet.ServletException */ @Override public void doPut(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { doPost(req, res); } /** * Delegate the request processing to an instance of {@link CometSupport} * * @param req the {@link HttpServletRequest} * @param res the {@link HttpServletResponse} * @throws java.io.IOException * @throws javax.servlet.ServletException */ @Override public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { doPost(req, res); } /** * Delegate the request processing to an instance of {@link CometSupport} * * @param req the {@link HttpServletRequest} * @param res the {@link HttpServletResponse} * @throws java.io.IOException * @throws javax.servlet.ServletException */ @Override public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { doCometSupport(req, res); } /** * Invoke the proprietary {@link CometSupport} * * @param req * @param res * @return an {@link Action} * @throws IOException * @throws ServletException */ protected Action doCometSupport(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { req.setAttribute(BROADCASTER_FACTORY, broadcasterFactory); req.setAttribute(PROPERTY_USE_STREAM, useStreamForFlushingComments); try { return cometSupport.service(req, res); } catch (IllegalStateException ex) { if (ex.getMessage() != null && ex.getMessage().startsWith("Tomcat failed")) { if (!isFilter) { logger.warning(ex.getMessage()); logger.warning("Using the BlockingIOCometSupport."); } cometSupport = new BlockingIOCometSupport(config); service(req, res); } else { logger.log(Level.SEVERE, "AtmosphereServlet exception", ex); throw ex; } } return null; } /** * Hack to support Tomcat AIO like other WebServer. This method is invoked * by Tomcat when it detect a {@link Servlet} implements the interface * {@link CometProcessor} without invoking {@link Servlet#service} * * @param cometEvent the {@link CometEvent} * @throws java.io.IOException * @throws javax.servlet.ServletException */ public void event(CometEvent cometEvent) throws IOException, ServletException { HttpServletRequest req = cometEvent.getHttpServletRequest(); HttpServletResponse res = cometEvent.getHttpServletResponse(); req.setAttribute(TomcatCometSupport.COMET_EVENT, cometEvent); if (!isCometSupportSpecified && !isCometSupportConfigured.getAndSet(true)) { synchronized (cometSupport) { if (!cometSupport.getClass().equals(TomcatCometSupport.class)) { logger.warning("TomcatCometSupport is enabled, switching to it"); cometSupport = new TomcatCometSupport(config); } } } doCometSupport(req, res); } /** * Hack to support JBossWeb AIO like other WebServer. This method is invoked * by Tomcat when it detect a {@link Servlet} implements the interface * {@link HttpEventServlet} without invoking {@link Servlet#service} * * @param httpEvent the {@link CometEvent} * @throws java.io.IOException * @throws javax.servlet.ServletException */ public void event(HttpEvent httpEvent) throws IOException, ServletException { HttpServletRequest req = httpEvent.getHttpServletRequest(); HttpServletResponse res = httpEvent.getHttpServletResponse(); req.setAttribute(JBossWebCometSupport.HTTP_EVENT, httpEvent); if (!isCometSupportSpecified && !isCometSupportConfigured.getAndSet(true)) { synchronized (cometSupport) { if (!cometSupport.getClass().equals(JBossWebCometSupport.class)) { logger.warning("JBossWebCometSupport is enabled, switching to it"); cometSupport = new JBossWebCometSupport(config); } } } doCometSupport(req, res); } /** * Weblogic specific comet based implementation. * * @param rrk * @return true if suspended * @throws java.io.IOException * @throws javax.servlet.ServletException */ protected boolean doRequest(RequestResponseKey rrk) throws IOException, ServletException { try { rrk.getRequest().getSession().setAttribute(WebLogicCometSupport.RRK, rrk); Action action = doCometSupport(rrk.getRequest(), rrk.getResponse()); if (action.type == Action.TYPE.SUSPEND) { if (action.timeout == -1) { rrk.setTimeout(Integer.MAX_VALUE); } else { rrk.setTimeout((int) action.timeout); } } return action.type == Action.TYPE.SUSPEND; } catch (IllegalStateException ex) { logger.log(Level.SEVERE, "AtmosphereServlet.doRequest exception", ex); throw ex; } } /** * Weblogic specific comet based implementation. * * @param rrk * @throws java.io.IOException * @throws javax.servlet.ServletException */ protected void doResponse(RequestResponseKey rrk, Object context) throws IOException, ServletException { rrk.getResponse().flushBuffer(); } /** * Weblogic specific comet based implementation. * * @param rrk * @throws java.io.IOException * @throws javax.servlet.ServletException */ protected void doTimeout(RequestResponseKey rrk) throws IOException, ServletException { ((AsynchronousProcessor) cometSupport).timedout(rrk.getRequest(), rrk.getResponse()); } /** * Return the default {@link Broadcaster} class name. * * @return the broadcasterClassName */ public static String getDefaultBroadcasterClassName() { return broadcasterClassName; } /** * Set the default {@link Broadcaster} class name * * @param broadcasterClassName the broadcasterClassName to set */ public static void setDefaultBroadcasterClassName(String broadcasterClassName) { broadcasterClassName = broadcasterClassName; } /** * true if Atmosphere uses {@link HttpServletResponse#getOutputStream()} * by default for write operation. * * @return the useStreamForFlushingComments */ public boolean isUseStreamForFlushingComments() { return useStreamForFlushingComments; } /** * Set to true so Atmosphere uses {@link HttpServletResponse#getOutputStream()} * by default for write operation. Default is false. * * @param useStreamForFlushingComments the useStreamForFlushingComments to set */ public void setUseStreamForFlushingComments(boolean useStreamForFlushingComments) { this.useStreamForFlushingComments = useStreamForFlushingComments; } /** * Get the {@link BroadcasterFactory} which is used by Atmosphere to construct * {@link Broadcaster} * * @return {@link BroadcasterFactory} */ public BroadcasterFactory getBroadcasterFactory() { return broadcasterFactory; } /** * Set the {@link BroadcasterFactory} which is used by Atmosphere to construct * {@link Broadcaster} * * @return {@link BroadcasterFactory} */ public AtmosphereServlet setBroadcasterFactory(final BroadcasterFactory broadcasterFactory) throws IllegalAccessException, InstantiationException, ClassNotFoundException { this.broadcasterFactory = broadcasterFactory; configureBroadcaster(); return this; } /** * Return the {@link BroadcasterConfig} used by this instance. * * @return {@link BroadcasterConfig} */ public static BroadcasterConfig getBroadcasterConfig() { return broadcasterConfig; } /** * 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 void setBroadcasterCacheClassName(String broadcasterCacheClassName) throws IllegalAccessException, InstantiationException, ClassNotFoundException { this.broadcasterCacheClassName = broadcasterCacheClassName; configureBroadcaster(); } /** * Jetty 7 and up WebSocket support. * * @param request * @param protocol * @return a {@link WebSocket}} */ @Override protected WebSocket doWebSocketConnect(final HttpServletRequest request, final String protocol) { if (logger.isLoggable(Level.INFO)) { logger.info("WebSocket upgrade requested"); } return new WebSocket() { private WebSocketProcessor webSocketProcessor; public void onConnect(WebSocket.Outbound outbound) { webSocketProcessor = new WebSocketProcessor(AtmosphereServlet.this, new JettyWebSocketSupport(outbound)); try { webSocketProcessor.connect(request); } catch (IOException e) { logger.log(Level.WARNING, "", e); } } public void onMessage(byte frame, String data) { webSocketProcessor.broadcast(frame, data); } public void onMessage(byte frame, byte[] data, int offset, int length) { webSocketProcessor.broadcast(frame, new String(data, offset, length)); } public void onDisconnect() { webSocketProcessor.close(); } }; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy