FileDocCategorySizeDatePackage
CometEngine.javaAPI DocGlassfish v2 API20112Tue Jul 10 13:20:24 BST 2007com.sun.enterprise.web.connector.grizzly.comet

CometEngine

public class CometEngine extends Object
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 cometContext path on which Comet support will be applied: CometEngine cometEngine = CometEngine.getEngine() CometContext cometContext = cometEngine.register(contextPath) (2) Second, add an instance of CometHandler to the CometContext returned by the register method: cometContext.addCometHandler(handler); (3) Finally, you can notify other CometHandler by doing: cometContext.notify(Object)(handler); You can also select the stage where the request polling happens when registering the cometContext path (see register(String,int);
author
Jeanfrancois Arcand

Fields Summary
public static final int
BEFORE_REQUEST_PROCESSING
The token used to support BEFORE_REQUEST_PROCESSING polling.
public static final int
AFTER_SERVLET_PROCESSING
The token used to support AFTER_SERVLET_PROCESSING polling.
public static final int
AFTER_RESPONSE_PROCESSING
The token used to support BEFORE_RESPONSE_PROCESSING polling.
private static final Logger
logger
Main logger
private com.sun.enterprise.web.connector.grizzly.Pipeline
pipeline
The Pipeline used to execute CometTask
private static CometEngine
cometEngine
The single instance of this class.
private ConcurrentHashMap
activeContexts
The current active CometContext keyed by context path.
private ConcurrentLinkedQueue
cometTasks
Cache of CometTask instance
private ConcurrentLinkedQueue
cometContexts
Cache of CometContext instance.
private CometSelector
cometSelector
The CometSelector used to poll requests.
private static String
notificationHandlerClassName
The default class to use when deciding which NotificationHandler to use. The default is DefaultNotificationHandler.
private ConcurrentHashMap
threadsId
Temporary repository that associate a Thread ID with a Key. NOTE: A ThreadLocal might be more efficient.
private ConcurrentHashMap
updatedCometContexts
Store modified CometContext.
Constructors Summary
private CometEngine()
Creat a singleton and initialize all lists required. Also create and start the CometSelector

    
    // --------------------------------------------------------------------- //
    
    
                       
      
        activeContexts = new ConcurrentHashMap<String,CometContext>();
        cometTasks = new ConcurrentLinkedQueue<CometTask>();
        cometContexts = new ConcurrentLinkedQueue<CometContext>();
        
        cometSelector = new CometSelector(this);
        cometSelector.start();
        
        threadsId = new ConcurrentHashMap<Long,SelectionKey>(); 
        updatedCometContexts = new ConcurrentHashMap<Long,CometContext>(); 
    
Methods Summary
protected java.nio.channels.SelectionKeyactivateContinuation(java.lang.Long threadId, CometContext cometContext, boolean continueExecution)
Tell the CometEngine to activate Grizzly ARP on that CometContext. This method is called when CometContext.addCometHandler() is invoked.

param
threadId the Thread.getId().
param
cometContext An instance of CometContext.
return
key The SelectionKey associated with the current request.

        if (!continueExecution){
            updatedCometContexts.put(threadId,cometContext); 
        }
        return threadsId.remove(threadId);
    
private voidexecuteServlet(int continuationType, com.sun.enterprise.web.connector.grizzly.async.AsyncProcessorTask apt)
Bring the cometContext path target (most probably a Servlet) to the processing stage we need for Comet request processing.

param
cometContext The CometContext associated with the Servlet
param
apt the AsyncProcessorTask

        
        switch (continuationType){
            case BEFORE_REQUEST_PROCESSING:
                apt.setStage(AsyncTask.PRE_EXECUTE);
                break;
            case AFTER_SERVLET_PROCESSING:
                apt.getProcessorTask().invokeAdapter();
                return;
            case AFTER_RESPONSE_PROCESSING:
                apt.setStage(AsyncTask.POST_EXECUTE);
                break;
            default:
                throw new IllegalStateException("Invalid state");
        }
        
        try{
            apt.doTask();
        } catch (IOException ex){
            logger.log(Level.SEVERE,"executeServlet",ex);
        }
    
private voidfinalizeContext(CometContext cometContext)
Cleanup the CometContext

param
cometContext the CometContext to clean

        Iterator<String> iterator = activeContexts.keySet().iterator();
        String contextPath;
        while(iterator.hasNext()){
            contextPath = iterator.next();
            if ( activeContexts.get(contextPath).equals(cometContext) ){
                activeContexts.remove(contextPath);
                break;
            }
        }
        
        ConcurrentLinkedQueue<AsyncProcessorTask> asyncTasks =
                cometContext.getAsyncProcessorTask();
        for (AsyncProcessorTask apt: asyncTasks){
            flushResponse(apt);
        }
        cometContext.recycle();
        cometContexts.offer(cometContext);
    
private voidflushResponse(com.sun.enterprise.web.connector.grizzly.async.AsyncProcessorTask apt)
Complete the asynchronous request.

        apt.setStage(AsyncTask.POST_EXECUTE);
        try{
            apt.doTask();
        } catch (IllegalStateException ex){
            if (logger.isLoggable(Level.FINEST)){
                logger.log(Level.FINEST,"flushResponse failed",ex);
            }
        } catch (IOException ex) {
            logger.log(Level.SEVERE,"flushResponse failed",ex);
        }
    
public CometContextgetCometContext(java.lang.String contextPath)
Return the CometContext associated with the cometContext path. XXX: This is not secure as a you can get a CometContext associated with another cometContext path. But this allow interesting application... MUST BE REVISTED.

param
contextPath the request's cometContext path.

        return activeContexts.get(contextPath);
    
protected CometTaskgetCometTask(CometContext cometContext, java.nio.channels.SelectionKey key)
Return a clean and configured CometTask

param
cometContext the CometContext to clean
param
key The current SelectionKey
return
a new CometContext

        CometTask cometTask = cometTasks.poll();
        if (cometTask == null){
            cometTask = new CometTask();
        }
        cometTask.setCometContext(cometContext);
        cometTask.setSelectionKey(key);
        cometTask.setCometSelector(cometSelector);
        cometTask.setPipeline(pipeline);
        return cometTask;
    
public static final synchronized com.sun.enterprise.web.connector.grizzly.comet.CometEnginegetEngine()
Return a singleton of this Class.

return
CometEngine the singleton.

        if (cometEngine == null) {
            cometEngine = new CometEngine();
        }
        return cometEngine;
    
public static java.lang.StringgetNotificationHandlerClassName()

        return notificationHandlerClassName;
    
protected booleanhandle(com.sun.enterprise.web.connector.grizzly.async.AsyncProcessorTask apt)
Handle an interrupted(or polled) request by matching the current context path with the registered one. If required, the bring the target component (Servlet) to the proper execution stage and then notify the CometHandler

param
apt the current apt representing the request.
return
boolean true if the request can be polled.

        
        if (pipeline == null){
            pipeline = apt.getPipeline();
        }
        
        String contextPath = apt.getProcessorTask().getRequestURI();
        CometContext cometContext = null;       
        if (contextPath != null){
            cometContext = activeContexts.get(contextPath);
            if (cometContext != null){
                NotificationHandler notificationHandler = 
                        cometContext.getNotificationHandler();
                if (notificationHandler != null && (notificationHandler 
                        instanceof DefaultNotificationHandler)){
                    ((DefaultNotificationHandler)notificationHandler)
                        .setPipeline(pipeline);
                }
            }
        }
        
        /*
         * If the cometContext is null, it means the context has never 
         * been registered. The registration might happens during the
         * Servlet.service() execution so we need to keep a reference
         * to the current thread so we can later retrieve the associated
         * SelectionKey. The SelectionKey is required in order to park the
         * request.
         */
        boolean activateContinuation = true;      
        SelectionKey key = apt.getProcessorTask().getSelectionKey();
        threadsId.put(Thread.currentThread().getId(),key);
        
        int continuationType = (cometContext == null)?
            AFTER_SERVLET_PROCESSING:cometContext.continuationType;
                
        /*
         * Execute the Servlet.service method. CometEngine.register() or
         * CometContext.addCometHandler() might be invoked during the
         * execution.
         */
        executeServlet(continuationType,apt);

        /*
         * Will return a CometContext instance if and only if the 
         * Servlet.service() have invoked CometContext.addCometHandler().
         * If the returned CometContext is null, it means we need to 
         * execute a synchronous request.
         */
        cometContext = updatedCometContexts.remove(Thread.currentThread().getId());   
        
        CometTask cometTask = null;
        if (cometContext == null){
            activateContinuation = false;
        } else {
            cometTask = getCometTask(cometContext,key);
            cometTask.setSelectorThread(
                    apt.getProcessorTask().getSelectorThread());  
        }
        
        boolean parkRequest = true;
        if (activateContinuation) {
            // Disable keep-alive
            key.attach(null);
            cometContext.addAsyncProcessorTask(apt);
            cometContext.initialize(key);
            cometTask.setExpirationDelay(cometContext.getExpirationDelay());
            cometTask.setSelectorThread(apt.getSelectorThread());
            cometSelector.registerKey(key,cometTask);
        } else {
            parkRequest = false;
            if (cometTask != null){
                cometTask.recycle();
                cometTasks.offer(cometTask);
            }
        }
        return parkRequest;
    
protected voidinterrupt(java.nio.channels.SelectionKey key)
The CometSelector is expiring idle SelectionKey, hence we need to resume the current request.

param
key the expired SelectionKey

        CometTask cometTask = (CometTask)key.attachment();
        
        key.attach(null);
        
        if (cometTask == null)
            throw new IllegalStateException("cometTask cannot be null");
                       
        SelectionKey akey = cometTask.getSelectionKey();
        
        try{
            if (!akey.isValid()) return;

            CometContext cometContext = cometTask.getCometContext();
            Iterator<AsyncProcessorTask> iterator =
                    cometContext.getAsyncProcessorTask().iterator();

            AsyncHandler ah = null;
            while (iterator.hasNext()){
                AsyncProcessorTask apt = iterator.next();
                ah = apt.getAsyncExecutor().getAsyncHandler();
                if (apt.getProcessorTask().getSelectionKey() == akey){
                    iterator.remove();
                    ah.removeFromInterruptedQueue(apt);

                    flushResponse(apt);            
                    if (akey != null){
                        akey.attach(null);
                    }
                    break;
                }
            }           
        } finally {
            cometTask.recycle();
            cometTasks.offer(cometTask);
        }
    
private static NotificationHandlerloadNotificationHandlerInstance(java.lang.String className)
Util to load classes using reflection.

        
        Class clazz = null;                               
        try{                              
            clazz = Class.forName(className,true,
                    Thread.currentThread().getContextClassLoader());
            return (NotificationHandler)clazz.newInstance();
        } catch (Throwable t) {
            logger.log(Level.WARNING,"Invalid NotificationHandler: ",t);
        }   
        return new DefaultNotificationHandler();
    
public static final java.util.logging.Loggerlogger()
Return the current logger.

        return logger;
    
public CometContextregister(java.lang.String contextPath)
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.

param
contextPath the context path used to create the CometContext
return
CometContext a configured CometContext.

        return register(contextPath,AFTER_SERVLET_PROCESSING);
    
public CometContextregister(java.lang.String contextPath, int type)
Register a context path with this CometEngine. The CometContext returned will be of type type.

param
contextPath the context path used to create the CometContext
return
CometContext a configured CometContext.

        CometContext cometContext = activeContexts.get(contextPath);
        if (cometContext == null){
            cometContext = cometContexts.poll();
            if (cometContext == null){
                cometContext = new CometContext(contextPath,type);
                cometContext.setCometSelector(cometSelector);
                NotificationHandler notificationHandler
                        = loadNotificationHandlerInstance
                             (notificationHandlerClassName);
                cometContext.setNotificationHandler(notificationHandler);
                if (notificationHandler != null && (notificationHandler 
                        instanceof DefaultNotificationHandler)){
                    ((DefaultNotificationHandler)notificationHandler)
                        .setPipeline(pipeline);
                }
            }
            activeContexts.put(contextPath,cometContext);
        }
        return cometContext;
    
protected voidresume(java.nio.channels.SelectionKey key, CometContext cometContext)
Resume the long polling request by unblocking the current SelectionKey

        Iterator<AsyncProcessorTask> iterator =
                cometContext.getAsyncProcessorTask().iterator();
        
        while (iterator.hasNext()){
            AsyncProcessorTask apt = iterator.next();            
            if (apt.getProcessorTask().getSelectionKey() == key){
                iterator.remove();
                apt.getAsyncExecutor().getAsyncHandler()
                      .removeFromInterruptedQueue(apt);
                flushResponse(apt);
                break;
            }
        }
    
public static voidsetNotificationHandlerClassName(java.lang.String aNotificationHandlerClassName)

        notificationHandlerClassName = aNotificationHandlerClassName;
    
public CometContextunregister(java.lang.String contextPath)
Unregister the CometHandler to the list of the CometContext.

        CometContext cometContext = activeContexts.get(contextPath);
        try{
            cometContext.notify(cometContext,CometEvent.TERMINATE);
        } catch (IOException ex){
            logger.log(Level.WARNING,"unregister",ex);
        }
        finalizeContext(cometContext);
        
        return activeContexts.remove(contextPath);