com.sun.grizzly.comet.CometContext Maven / Gradle / Ivy
/* * * 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 com.sun.grizzly.comet; import com.sun.grizzly.http.SelectorThread; import java.io.IOException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.Level; import java.util.logging.Logger; /** * The main object used by {@link CometHandler}. * The {@link CometContext} is always available for {@link CometHandler} * and can be used to notify other {@link CometHandler}. * * Attributes can be added/removed the same way
* method will be invoked: *HttpServletSession
* is doing. It is not recommended to use attributes if this * {@link CometContext} is not shared amongs multiple * context path (uses HttpServletSession instead). * * @author Jeanfrancois Arcand */ public class CometContext{ /** * Generic error message */ protected final static String INVALID_COMET_HANDLER = "CometHandler cannot be null. " + "This CometHandler was probably resumed and an invalid " + "reference was made to it."; /** * Main logger */ private final static Logger logger = SelectorThread.logger(); /** * Attributes placeholder. */ private ConcurrentHashMap attributes; /** * The context path associated with this instance. */ private String contextPath; /** * Is the {@link CometContext} instance been cancelled. */ protected boolean cancelled = false; /** * The list of registered {@link CometHandler} */ protected ConcurrentHashMap handlers; /** * The {@link CometSelector} used to register {@link SelectionKey} * for upcoming bytes. */ private CometSelector cometSelector; /** * The {@link CometContext} continuationType. See {@link CometEngine} */ protected int continuationType = CometEngine.AFTER_SERVLET_PROCESSING; /** * The default delay expiration before a {@link CometContext}'s * {@link CometHandler} are interrupted. */ private long expirationDelay = 30 * 1000; /** * true if the caller of CometContext.notify should block when * notifying other CometHandler. */ protected boolean blockingNotification = false; /** * The default NotificationHandler. */ protected NotificationHandler notificationHandler; /** * SelectionKey that are in the process of being parked. */ private static ConcurrentLinkedQueue inProgressSelectionKey = null; /** * Current associated list of {@link CometTask} */ private ConcurrentLinkedQueue activeTasks = new ConcurrentLinkedQueue (); // ---------------------------------------------------------------------- // /** * Create a new instance * @param contextPath the context path * @param type when the Comet processing will happen (see CometEngine). */ public CometContext(String contextPath, int continuationType) { this.contextPath = contextPath; this.continuationType = continuationType; attributes = new ConcurrentHashMap(); handlers = new ConcurrentHashMap (); inProgressSelectionKey = new ConcurrentLinkedQueue (); } /** * Get the context path associated with this instance. * @return contextPath the context path associated with this instance */ public String getContextPath(){ return contextPath; } /** * Add an attibute. * @param key the key * @param value the value */ public void addAttribute(Object key, Object value){ attributes.put(key,value); } /** * Retrive an attribute. * @param key the key * @return Object the value. */ public Object getAttribute(Object key){ return attributes.get(key); } /** * Remove an attribute. * @param key the key * @return Object the value */ public Object removeAttribute(Object key){ return attributes.remove(key); } /** * Add a {@link CometHandler}. Client of this method might * make sure the {@link CometHandler} is removed when the * CometHandler.onInterrupt
is invoked. * @param handler a new {@link CometHandler} * @param completeExecution Add the Comethandler but don't block waiting * for event. */ public int addCometHandler(CometHandler handler, boolean completeExecution){ Long threadId = Thread.currentThread().getId(); SelectionKey key = CometEngine.getEngine(). activateContinuation(threadId,this,completeExecution); if (key == null){ throw new IllegalStateException("Grizzly Comet hasn't been registered"); } if (handler == null){ throw new IllegalStateException(INVALID_COMET_HANDLER); } if (!completeExecution){ handlers.putIfAbsent(handler,key); } else { handlers.putIfAbsent(handler,new SelectionKey() { public void cancel() { } public SelectableChannel channel() { throw new IllegalStateException(); } public int interestOps() { throw new IllegalStateException(); } public SelectionKey interestOps(int ops) { throw new IllegalStateException(); } public boolean isValid() { return true; } public int readyOps() { throw new IllegalStateException(); } public Selector selector() { throw new IllegalStateException(); } }); } return handler.hashCode(); } /** * Add a {@link CometHandler}. Client on this method might * make sure the {@link CometHandler} is removed when the *CometHandler.onInterrupt
is invoked. * @param handler a new {@link CometHandler} */ public int addCometHandler(CometHandler handler){ return addCometHandler(handler,false); } /** * Retrive a {@link CometHandler} using its hashKey; */ public CometHandler getCometHandler(int hashCode){ Iteratoriterator = handlers.keySet().iterator(); CometHandler cometHandler = null; while (iterator.hasNext()){ cometHandler = iterator.next(); if ( cometHandler.hashCode() == hashCode ){ return cometHandler; } } return null; } /** * Retrive a {@link CometHandler} using its SelectionKey. The * {@link SelectionKey} is not exposed to the Comet API, hence this * method must be protected. */ protected CometHandler getCometHandler(SelectionKey key){ Iterator iterator = handlers.keySet().iterator(); CometHandler cometHandler = null; while (iterator.hasNext()){ cometHandler = iterator.next(); if (handlers.get(cometHandler) == key){ return cometHandler; } } return null; } /** * Notify all {@link CometHandler}. The attachment can be null. * The type
will determine which code>CometHandler* @param attachment An object shared amongst {@link CometHandler}. * @param type The type of notification. * @param key The SelectionKey associated with the CometHandler. */ protected void notify(CometEvent event, int eventType, SelectionKey key) throws IOException{ CometHandler cometHandler = getCometHandler(key); if (cometHandler == null){ throw new IllegalStateException(INVALID_COMET_HANDLER); } event.setCometContext(CometContext.this); cometHandler.onEvent(event); } /** * Remove a {@link CometHandler}. If the continuation (connection) * associated with this {@link CometHandler} no longer have * {@link CometHandler} associated to it, it will be resumed. */ public void removeCometHandler(CometHandler handler){ removeCometHandler(handler,true); } /** * Remove a {@link CometHandler}. If the continuation (connection) * associated with this {@link CometHandler} no longer have * {@link CometHandler} associated to it, it will be resumed. * @param handler The CometHandler to remove. * @param resume True is the connection can be resumed if no CometHandler * are associated with the underlying SelectionKey. */ private void removeCometHandler(CometHandler handler,boolean resume){ SelectionKey key = handlers.remove(handler); if (resume && !handlers.containsValue(key)){ CometEngine.getEngine().resume(key); } } /** * Remove a {@link CometHandler} based on its hashcode. */ public void removeCometHandler(int hashCode){ Iterator* CometEvent.INTERRUPT ->
CometHandler.onInterrupt
* CometEvent.NOTIFY ->CometHandler.onEvent
* CometEvent.INITIALIZE ->CometHandler.onInitialize
* CometEvent.TERMINATE ->CometHandler.onTerminate
* CometEvent.READ ->CometHandler.onEvent
* CometEvent.WRITE ->CometHandler.onEvent
*iterator = handlers.keySet().iterator(); CometHandler cometHandler = null; while (iterator.hasNext()){ cometHandler = iterator.next(); if (cometHandler.hashCode() == hashCode){ SelectionKey key = handlers.get(cometHandler); if (key == null){ throw new IllegalStateException("Invalid CometHandler"); } if (inProgressSelectionKey.contains(key)){ throw new IllegalStateException("Cannot resume an in progress connection."); } iterator.remove(); return; } } } /** * Resume the Comet request and remove it from the active CometHandler list. Once resumed, * a CometHandler should never manipulate the HttpServletRequest or HttpServletResponse as * those are recycled. If you cache them for later reuse by another thread there is a * possibility to introduce corrupted responses next time a request is made. */ public void resumeCometHandler(CometHandler handler){ resumeCometHandler(handler,true); } /** * Resume the Comet request. * @param handler The CometHandler associated with the current continuation. * @param remove true if the CometHandler needs to be removed. */ protected void resumeCometHandler(CometHandler handler, boolean remove){ SelectionKey key = handlers.get(handler); if (key == null){ throw new IllegalStateException("Invalid CometHandler"); } if (inProgressSelectionKey.contains(key)){ throw new IllegalStateException("Cannot resume an in progress connection"); } if (remove){ removeCometHandler(handler,false); } // Retrieve the CometSelector key. SelectionKey cometKey = cometSelector.cometKeyFor(key.channel()); if (cometKey != null){ CometTask task = (CometTask)cometKey.attachment(); if (task != null){ activeTasks.remove(task); } cometKey.attach(null); cometKey.cancel(); } CometEngine.getEngine().resume(key); } /** * Return true if this CometHandler is still active, e.g. there is * still a continuation associated with it. */ public boolean isActive(CometHandler cometHandler){ if (cometHandler == null){ throw new IllegalStateException(INVALID_COMET_HANDLER); } SelectionKey key = handlers.get(cometHandler); return (key != null && !inProgressSelectionKey.contains(key)); } /** * Notify all {@link CometHandler}. The attachment can be null. All * CometHandler.onEvent()
will be invoked. * @param attachment An object shared amongst {@link CometHandler}. */ public void notify(final E attachment) throws IOException{ CometEvent event = new CometEvent(); event.setType(CometEvent.NOTIFY); event.attach(attachment); event.setCometContext(CometContext.this); Iterator iterator = handlers.keySet().iterator(); notificationHandler.setBlockingNotification(blockingNotification); notificationHandler.notify(event,iterator); resetSuspendIdleTimeout(); } /** * Notify a single {@link CometHandler}. The attachment can be null. * The type
will determine which code>CometHandler * method will be invoked: ** @param attachment An object shared amongst {@link CometHandler}. * @param type The type of notification. * @param cometHandlerID Notify a single CometHandler. */ public void notify(final E attachment,final int eventType,final int cometHandlerID) throws IOException{ CometHandler cometHandler = getCometHandler(cometHandlerID); if (cometHandler == null){ throw new IllegalStateException(INVALID_COMET_HANDLER); } CometEvent event = new CometEvent* CometEvent.INTERRUPT ->
CometHandler.onInterrupt
* CometEvent.NOTIFY ->CometHandler.onEvent
* CometEvent.INITIALIZE ->CometHandler.onInitialize
* CometEvent.TERMINATE ->CometHandler.onTerminate
* CometEvent.READ ->CometHandler.onEvent
*(); event.setType(eventType); event.attach(attachment); event.setCometContext(CometContext.this); notificationHandler.setBlockingNotification(blockingNotification); notificationHandler.notify(event,cometHandler); if (event.getType() == CometEvent.TERMINATE || event.getType() == CometEvent.INTERRUPT) { resumeCometHandler(cometHandler); } else { resetSuspendIdleTimeout(); } } /** * Initialize the newly added {@link CometHandler}. * * @param attachment An object shared amongst {@link CometHandler}. * @param type The type of notification. * @param key The SelectionKey representing the CometHandler. */ protected void initialize(SelectionKey key) throws IOException { CometEvent event = new CometEvent (); event.setType(CometEvent.INITIALIZE); event.setCometContext(this); Iterator iterator = handlers.keySet().iterator(); CometHandler cometHandler = null; while(iterator.hasNext()){ cometHandler = iterator.next(); if(handlers.get(cometHandler).equals(key)){ cometHandler.onInitialize(event); break; } } } /** * Notify all {@link CometHandler}. The attachment can be null. * The type
will determine which code>CometHandler * method will be invoked: ** @param attachment An object shared amongst {@link CometHandler}. * @param type The type of notification. */ public void notify(final E attachment,final int eventType) throws IOException{ // XXX Use a pool of CometEvent instance. CometEvent event = new CometEvent* CometEvent.INTERRUPT ->
CometHandler.onInterrupt
* CometEvent.NOTIFY ->CometHandler.onEvent
* CometEvent.INITIALIZE ->CometHandler.onInitialize
* CometEvent.TERMINATE ->CometHandler.onTerminate
* CometEvent.READ ->CometHandler.onEvent
*(); event.setType(eventType); event.attach(attachment); event.setCometContext(CometContext.this); Iterator iterator = handlers.keySet().iterator(); notificationHandler.setBlockingNotification(blockingNotification); notificationHandler.notify(event,iterator); if (event.getType() == CometEvent.TERMINATE || event.getType() == CometEvent.INTERRUPT) { while(iterator.hasNext()){ resumeCometHandler(iterator.next()); } } else { resetSuspendIdleTimeout(); } } /** * Reset the current timestamp on a suspended connection. */ protected synchronized void resetSuspendIdleTimeout(){ CometTask cometTask; Iterator it = activeTasks.iterator(); while(it.hasNext()){ cometTask = it.next(); cometTask.setExpireTime(System.currentTimeMillis()); } } /** * Register for asynchronous read. If your client supports http pipelining, * invoking this method might result in a state where your CometHandler * is invoked with a {@link CometRead} that will read the next http request. In that * case, it is strongly recommended to not use that method unless your * CometHandler can handle the http request. * @oaram handler The CometHandler that will be invoked. */ public boolean registerAsyncRead(CometHandler handler){ SelectionKey key = null; if (handler != null) { key = handlers.get(handler); } if (handler == null || key == null) { throw new IllegalStateException(INVALID_COMET_HANDLER); } // Retrieve the CometSelector key. SelectionKey cometKey = cometSelector.cometKeyFor(key.channel()); if (cometKey != null){ cometKey.interestOps(cometKey.interestOps() | SelectionKey.OP_READ); if (cometKey.attachment() != null){ ((CometTask)cometKey.attachment()).setAsyncReadSupported(true); } return true; } else { return false; } } /** * Register for asynchronous write. */ public boolean registerAsyncWrite(CometHandler handler){ SelectionKey key = null; if (handler != null) { key = handlers.get(handler); } if (handler == null || key == null) { throw new IllegalStateException(INVALID_COMET_HANDLER); } // Retrieve the CometSelector key. SelectionKey cometKey = cometSelector.cometKeyFor(key.channel()); if (cometKey != null){ cometKey.interestOps(cometKey.interestOps() | SelectionKey.OP_WRITE); return true; } else { return false; } } /** * Recycle this object. */ protected void recycle(){ handlers.clear(); attributes.clear(); cancelled = false; activeTasks.clear(); } /** * Is this instance beeing cancelled by the {@link CometSelector} * @return boolean cancelled or not. */ protected boolean isCancelled() { return cancelled; } /** * Cancel this object or "uncancel". * @param cancelled true or false. */ protected void setCancelled(boolean cancelled) { this.cancelled = cancelled; } /** * Set the {@link CometSelector} associated with this instance. * @param CometSelector the {@link CometSelector} associated with * this instance. */ protected void setCometSelector(CometSelector cometSelector) { this.cometSelector = cometSelector; } /** * Helper. */ @Override public String toString(){ return contextPath; } /** * Return the long
delay before a request is resumed. * @return long thelong
delay before a request is resumed. */ public long getExpirationDelay() { return expirationDelay; } /** * Set thelong
delay before a request is resumed. * @param long thelong
delay before a request is resumed. */ public void setExpirationDelay(long expirationDelay) { this.expirationDelay = expirationDelay; } /** * Interrupt a {@link CometHandler} by invoking {@link CometHandler#onInterrupt} */ protected void interrupt(CometTask task){ CometEvent event = new CometEvent(); event.setType(CometEvent.INTERRUPT); event.attach(null); event.setCometContext(this); Iterator iterator = handlers.keySet().iterator(); CometHandler handler; while(iterator.hasNext()){ handler = iterator.next(); if (handlers.get(handler).equals(task.getSelectionKey()) ){ try{ handler.onInterrupt(event); iterator.remove(); } catch (IOException ex){ logger.log(Level.WARNING,"Exception: ",ex); } break; } } activeTasks.remove(task); } /** * Add a {@link CometTask} to the active list. * @param cometTask */ protected void addActiveCometTask(CometTask cometTask){ activeTasks.offer(cometTask); } /** * Return true if the invoker of notify() should block when * notifying Comet Handlers. */ public boolean isBlockingNotification() { return blockingNotification; } /** * Set to true if the invoker of notify() should block when * notifying Comet Handlers. */ public void setBlockingNotification(boolean blockingNotification) { this.blockingNotification = blockingNotification; } /** * Set the current {@link NotificationHandler} * @param notificationHandler */ public void setNotificationHandler(NotificationHandler notificationHandler){ this.notificationHandler = notificationHandler; } /** * Return the associated {@link NotificationHandler} * @return */ public NotificationHandler getNotificationHandler(){ return notificationHandler; } /** * Add a {@link SelectionKey} to the list of current operations. * @param key */ protected static void addInProgressSelectionKey(SelectionKey key){ inProgressSelectionKey.add(key); } /** * Remove a {@link SelectionKey} to the list of current operations. * @param key * @return */ protected static boolean removeInProgressSelectionKey(SelectionKey key){ return inProgressSelectionKey.remove(key); } }
© 2015 - 2025 Weber Informatics LLC | Privacy Policy