/*
 * Copyright (c) 2007, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.grizzly.comet;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.glassfish.grizzly.localization.LogMessages;

Main class allowing Comet support on top of Grizzly Asynchronous Request Processing mechanism. This class is the entry point to any component interested to execute Comet request style. Components can be Servlets, JSP, JSF or pure Java class. A component interested to support Comet request must do:
 (1) First, register the topic on which Comet support will be applied: CometEngine cometEngine = CometEngine.getEngine() CometContext cometContext = cometEngine.register(topic) (2) Second, add an instance of CometHandler to the CometContext returned by the register method: CometContext.addCometHandler. Executing this operation will tells Grizzly to suspend the response. (3) Finally, you can Object.notify other CometHandler to share information between CometHandler. When notified, CometHandler can decides to push back the data, resume the response, or simply ignore the content of the notification. 
You can also select the stage where the suspension of the response happens when registering the CometContext's topic (see register), which can be before, during or after invoking a Servlet There is known limitation related to HTTP pipelining, it can't work properly when CometContext.isDetectClosedConnections() is enabled. So if you want to support HTTP pipelining, the closed connection detection mechanism should be disabled via CometContext.setDetectClosedConnections(boolean).
Author:Jeanfrancois Arcand, Gustav Trede
/** * Main class allowing Comet support on top of Grizzly Asynchronous Request Processing mechanism. This class is the * entry point to any component interested to execute Comet request style. Components can be Servlets, JSP, JSF or pure * Java class. A component interested to support Comet request must do: * * <pre> * <code> * (1) First, register the topic on which Comet support will be applied: * CometEngine cometEngine = CometEngine.getEngine() * CometContext cometContext = cometEngine.register(topic) * (2) Second, add an instance of {@link CometHandler} to the * {@link CometContext} returned by the register method: * {@link CometContext#addCometHandler}. Executing this operation * will tells Grizzly to suspend the response. * (3) Finally, you can {@link CometContext#notify} other {@link CometHandler} * to share information between {@link CometHandler}. When notified, * {@link CometHandler} can decides to push back the data, resume the * response, or simply ignore the content of the notification. * </code> * </pre> * * You can also select the stage where the suspension of the response happens when registering the * {@link CometContext}'s topic (see {@link #register}), which can be before, during or after invoking a * <code>Servlet</code> * * There is known limitation related to <tt>HTTP pipelining</tt>, it can't work properly when * {@link CometContext#isDetectClosedConnections()} is enabled. So if you want to support <tt>HTTP pipelining</tt>, the * closed connection detection mechanism should be disabled via * {@link CometContext#setDetectClosedConnections(boolean)}. * * @author Jeanfrancois Arcand * @author Gustav Trede */
public class CometEngine { // Disable suspended connection time out for a {@link CometContext#setExpirationDelay} @Deprecated public final static int DISABLE_SUSPEND_TIMEOUT = -1; // Disable client detection close for a {@link CometContext#setExpirationDelay} @Deprecated public final static int DISABLE_CLIENT_DISCONNECTION_DETECTION = 0;
The token used to support BEFORE_REQUEST_PROCESSING polling.
/** * The token used to support BEFORE_REQUEST_PROCESSING polling. */
@Deprecated public final static int BEFORE_REQUEST_PROCESSING = 0;
The token used to support AFTER_SERVLET_PROCESSING polling.
/** * The token used to support AFTER_SERVLET_PROCESSING polling. */
@Deprecated public final static int AFTER_SERVLET_PROCESSING = 1;
The token used to support BEFORE_RESPONSE_PROCESSING polling.
/** * The token used to support BEFORE_RESPONSE_PROCESSING polling. */
@Deprecated public final static int AFTER_RESPONSE_PROCESSING = 2;
Main logger
/** * Main logger */
protected final static Logger logger = Logger.getLogger(CometEngine.class.getName());
The ExecutorService used to execute
/** * The {@link ExecutorService} used to execute */
protected ExecutorService threadPool;
The single instance of this class.
/** * The single instance of this class. */
private final static CometEngine cometEngine = new CometEngine();
The current active CometContext keyed by context path.
/** * The current active {@link CometContext} keyed by context path. */
protected final Map<String, CometContext> activeContexts;
Is Grizzly ARP enabled? By default we set it to false.
/** * Is Grizzly ARP enabled? By default we set it to false. */
private boolean isCometSupported;
Create a singleton and initialize all lists required.
/** * Create a singleton and initialize all lists required. */
protected CometEngine() { activeContexts = new ConcurrentHashMap<>(16, 0.75f, 64); }
Return true if comet is enabled.
/** * Return true if comet is enabled. */
protected boolean isCometEnabled() { return isCometSupported; } public void setCometSupported(boolean supported) { isCometSupported = supported; }
Return a singleton of this Class.
Returns:CometEngine the singleton.
/** * Return a singleton of this Class. * * @return CometEngine the singleton. */
public static CometEngine getEngine() { return cometEngine; }
Calls through to deregister(String).
Deprecated:
/** * Calls through to {@link #deregister(String)}. * * @deprecated */
@Deprecated public CometContext unregister(String topic) { return deregister(topic); }
Deregister the CometHandler to the list of the CometContext. Invoking this method will invoke all CometHandler.onTerminate(CometEvent) before removing the associated CometContext. Invoking that method will also resume the underlying connection associated with the CometHandler, similar to what CometContext.resumeCometHandler(CometHandler) do.
/** * Deregister the {@link CometHandler} to the list of the {@link CometContext}. Invoking this method will invoke all * {@link CometHandler#onTerminate(CometEvent)} before removing the associated {@link CometContext}. Invoking that * method will also resume the underlying connection associated with the {@link CometHandler}, similar to what * {@link CometContext#resumeCometHandler(CometHandler)} do. */
public CometContext deregister(String topic) { CometContext cometContext = activeContexts.remove(topic); if (cometContext != null) { cometContext.recycle(); } return cometContext; }
Register a context path with this CometEngine. The CometContext returned will be of type type.
Params:
  • topic – the context path used to create the CometContext
  • type – when the request will be suspended
Returns:CometContext a configured CometContext.
Deprecated:Use register(String) instead
/** * Register a context path with this {@link CometEngine}. The {@link CometContext} returned will be of type * <code>type</code>. * * @param topic the context path used to create the {@link CometContext} * @param type when the request will be suspended * * @return CometContext a configured {@link CometContext}. * @deprecated Use {@link #register(String)} instead */
@Deprecated public <E> CometContext<E> register(String topic, int type) { return register(topic); }
Register a context path with this CometEngine. The CometContext returned will be of type AFTER_SERVLET_PROCESSING, which means the request target (most probably a Servlet) will be executed first and then polled.
Params:
Returns:CometContext a configured CometContext.
/** * Register a context path with this {@link CometEngine}. The {@link CometContext} returned will be of type * AFTER_SERVLET_PROCESSING, which means the request target (most probably a Servlet) will be executed first and then * polled. * * @param topic the context path used to create the {@link CometContext} * * @return CometContext a configured {@link CometContext}. */
public <E> CometContext<E> register(String topic) { return register(topic, DefaultNotificationHandler.class); }
Instantiate a new CometContext.
Params:
Returns:a new CometContext if not already created, or the existing one.
/** * Instantiate a new {@link CometContext}. * * @param topic the topic the new {@link CometContext} will represent. * @return a new {@link CometContext} if not already created, or the existing one. */
@SuppressWarnings({ "unchecked" }) public <E> CometContext<E> register(String topic, Class<? extends NotificationHandler> notificationClass) { // Double checked locking used used to prevent the otherwise static/global // locking, cause example code does heavy usage of register calls // for existing topics from http get calls etc. CometContext<E> cometContext = activeContexts.get(topic); if (cometContext == null) { synchronized (activeContexts) { cometContext = activeContexts.get(topic); if (cometContext == null) { cometContext = new CometContext<>(topic); NotificationHandler notificationHandler; try { notificationHandler = notificationClass.newInstance(); } catch (Throwable t) { if (logger.isLoggable(Level.SEVERE)) { logger.log(Level.SEVERE, LogMessages.SEVERE_GRIZZLY_COMET_ENGINE_INVALID_NOTIFICATION_HANDLER_ERROR(notificationClass.getName()), t); } notificationHandler = new DefaultNotificationHandler(); } cometContext.setNotificationHandler(notificationHandler); if (notificationHandler != null && notificationHandler instanceof DefaultNotificationHandler) { ((DefaultNotificationHandler) notificationHandler).setThreadPool(threadPool); } activeContexts.put(topic, cometContext); } } } return cometContext; }
Return the CometContext associated with the topic.
Params:
/** * Return the {@link CometContext} associated with the topic. * * @param topic the topic used to creates the {@link CometContext} */
@SuppressWarnings({ "unchecked" }) public <E> CometContext<E> getCometContext(String topic) { return activeContexts.get(topic); }
Interrupt a CometHandler by invoking CometHandler.onInterrupt
Params:
  • handler – The CometHandler encapsulating the suspended connection.
  • finishExecution – Finish the current execution.
See Also:
Deprecated:use the CometContext version
/** * Interrupt a {@link CometHandler} by invoking {@link CometHandler#onInterrupt} * * @param handler The {@link CometHandler} encapsulating the suspended connection. * @param finishExecution Finish the current execution. * * @see CometContext#interrupt(CometHandler, boolean) * @deprecated use the CometContext version */
@Deprecated @SuppressWarnings({ "deprecation" }) protected boolean interrupt(CometHandler handler, boolean finishExecution) throws IOException { final CometContext cometContext = handler.getCometContext(); final boolean removed = cometContext.removeCometHandler(handler, finishExecution); if (removed && !finishExecution) { interrupt0(handler, finishExecution); } return removed; }
Interrupt logic in its own method, so it can be executed either async or sync.
cometHandler.onInterrupt is performed async due to its functionality is unknown, hence not safe to run in the performance critical selector thread.
Params:
  • handler – The CometHandler encapsulating the suspended connection.
  • finishExecution – Finish the current execution.
See Also:
Deprecated:use the CometContext version
/** * Interrupt logic in its own method, so it can be executed either async or sync.<br> * cometHandler.onInterrupt is performed async due to its functionality is unknown, hence not safe to run in the * performance critical selector thread. * * @param handler The {@link CometHandler} encapsulating the suspended connection. * @param finishExecution Finish the current execution. * * @see CometContext#interrupt0(CometHandler, boolean) * @deprecated use the CometContext version */
@Deprecated protected void interrupt0(CometHandler handler, boolean finishExecution) throws IOException { if (finishExecution) { try { handler.onInterrupt(handler.getCometContext().eventInterrupt); } catch (IOException e) { } } handler.getResponse().finish(); }
Return the current logger.
/** * Return the current logger. */
public static Logger logger() { return logger; } }