com.sun.grizzly.messagesbus.MessagesBus Maven / Gradle / Ivy
 The 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 com.sun.grizzly.messagesbus;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sun.grizzly.comet.CometContext;
import com.sun.grizzly.comet.CometEngine;
import com.sun.grizzly.comet.CometHandler;
import com.sun.grizzly.comet.NotificationHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 * Servlet implementation of the Grizzly Comet Protocol (GCP). The GCP protocol
 * is a very basic protocol that can be used by browser to share data, using the
 * comet technique, between serveral clients without having to poll for it. 
 * 
 * The protocol is very simple. First, a client must subscribe to a topic:
 * 
 * http://host:port/contextPath?
 *    subscribe=[topic name]&cometTechnique=[polling|log-polling|http-streaming]&message[text]
 * 
 * Mandatory: subscribe and cometTechnique.
 * 
 * When issuing the URL above, the connection will be automatically suspended
 * based on the cometTechnique specified. To share data
 * between application, a browser just need to send the following request:
 * 
 * http://host:port/contextPath?publish=[topic name]&message[text] 
 * 
 * The Servlet can be used as it is or extended to add extra features like
 * filtering messages, security, login, etc.
 * 
 * To use this Servlet, just add in your web.xml:
 * 
 * 
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
      xmlns:j2ee="http://java.sun.com/xml/ns/j2ee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4"
      xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
      <description>Grizzly Messages Bus Servlet</description>
      <display-name>Grizzly Messages Bus Servlet</display-name>
      <servlet>
        <description>MessagesBus</description>
        <servlet-name>MessagesBus</servlet-name>
        <servlet-class>com.sun.grizzly.messagesbus.MessagesBus</servlet-class>
        <load-on-startup>0</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>MessagesBus</servlet-name>
        <url-pattern>/mb</url-pattern>
      </servlet-mapping>
      <session-config>
        <session-timeout>25</session-timeout>
      </session-config>
    </web-app>
 * 
 * 
 * By default, a connection will be timed out after a {@link #expirationDelay}.
 * The default is 30 seconds, but this can be configured by adding, in web.xml:
 * 
   <init-param>
        <param-name>expirationDelay</param-name>
        <param-value>60000</param-value>
   </init-param>
 * 
 * 
 * The Grizzly Comet {@link NotificationHandler} can also be replaced by adding:
 * 
   <init-param>
        <param-name>notificationHandler</param-name>
        <param-value>com.foo.bar.ScriptFilterNotificationHandler</param-value>
   </init-param>
 * 
 * 
 * A request can also be automatically suspended when the request URI only 
 * contains the publish GCP action.
 * 
   <init-param>
        <param-name>suspendOnTheFly</param-name>
        <param-value>true</param-value>
   </init-param>
 * 
 
 * 
 * 
 * @author Jeanfrancois Arcand
 */
public class MessagesBus extends HttpServlet {
    /**
     * The Comet technique supported.
     */ 
    public enum CometType {
        POLLING, LONG_POLLING, HTTP_STREAMING
    }
    
    // 30 seconds by default.
    private long expirationDelay = 30 * 1000;
    
    
    /**
     * The specified {@link NotificationHandler}. When not specified,
     * 
     * 
     */
    private NotificationHandler notificationHandler = null; 
    
    
    /**
     * Default Logger.
     */
    private static final Logger logger = Logger.getAnonymousLogger();
    
    /**
     * true if a connection can be automatically suspended
     * when the request only contains the GCP message publish
     * Default is true
     */
    private boolean suspendOnTheFly = true;
    
    
    /**
     * {@inheritDoc}
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        String ed = config.getInitParameter("expirationDelay");
        if (ed != null) {
            expirationDelay = Long.parseLong(ed);
        }
        
        String nh = config.getInitParameter("notificationHandler");
        if (ed != null) {
            notificationHandler = (NotificationHandler)loadClass(nh);
        }   
        
        String stf = config.getInitParameter("suspendOnTheFly");
        if (stf != null) {
            suspendOnTheFly = Boolean.valueOf(stf);
        } 
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {
        doPost(req, res);
    }
    /**
     * Basic implementation of the Grizzly Comet Protocol (GCP). 
     * @param req The request
     * @param res The response
     * @throws javax.servlet.ServletException
     * @throws java.io.IOException
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {
        CometType cometType = suspend(req, res);
        res.setContentType("text/html");
        // The connection has been suspended.
        if (cometType != null && cometType != CometType.POLLING) {
            res.flushBuffer();
            return;
        }
        push(req,res);
    }
    
    /**
     * Inspect the request, looking for the GCP parameters publish
     * and message. If those parameters are included, the 
     * message will be pushed to all subscriber of the publish's value.
     * 
     * @param req The http request
     * @param res The http response.
     * @throws java.io.IOException
     */
    protected synchronized void push(HttpServletRequest req, HttpServletResponse res) 
            throws IOException {
        String message = req.getParameter("message");
        String topic = req.getParameter("publish");
        // Nothing to send
        if (message == null || topic == null) {
            return;
        }
        
        CometContext context = CometEngine.getEngine().getCometContext(topic);
        if (context == null && suspendOnTheFly) {
            context = createCometContext(topic);
            CometType cometType = CometType.LONG_POLLING;
            CometHandler cometHandler = new MessagesBusCometHandler(context, cometType);
            cometHandler.attach(res);
            context.addCometHandler(cometHandler);            
        } else if (!suspendOnTheFly) {            
            if (logger.isLoggable(Level.INFO)) {
                logger.info("Cannot create message topic:" + topic 
                        + " on the fly. You first have to " + "subscribe");
            }            
            return;
        }
        context.notify(message);
    }
    /**
     * Suspend the connection if the request contains the GCP 
     * suscribe and  cometTechnique action.
     * @param req 
     * @param res
     * @return {@link CometType} used to suspend the connection.
     * @throws java.io.UnsupportedEncodingException
     * @throws java.io.IOException
     */
    protected synchronized CometType suspend(HttpServletRequest req, HttpServletResponse res)
            throws UnsupportedEncodingException, IOException {
        String cometTechnique = req.getParameter("cometTechnique");
        String topic = req.getParameter("subscribe");
        String message = req.getParameter("message");
        
        // This is not a request for suspending the connection.
        if (cometTechnique == null || topic == null) {
            return CometType.POLLING;
        }
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("CometTechnique: " + cometTechnique + " topic:" + topic);
        }
        if (cometTechnique.equals("polling")) {
            return CometType.POLLING;
        }
        CometContext context = CometEngine.getEngine().getCometContext(topic);
        if (context == null) {
            context = createCometContext(topic);
        }
        CometType cometType = null;
        if (cometTechnique.equals("long-polling")) {
            cometType = CometType.LONG_POLLING;
        } else if (cometTechnique.equals("http-streaming")) {
            cometType = CometType.HTTP_STREAMING;
        }
        CometHandler cometHandler = new MessagesBusCometHandler(context, cometType);
        cometHandler.attach(res);
        
        if (message != null){
            context.notify(message);
            // Echo 
            res.getWriter().write(message);
        }
        
        context.addCometHandler(cometHandler);
        return cometType;
    }
    
    /**
     * Create a Grizzly Comet {@link CometContext} 
     * @param topic The name of the  {@link CometContext} 
     * @return a cached or newly created  {@link CometContext} 
     */
    protected final CometContext createCometContext(String topic){
        CometContext  context = CometEngine.getEngine().register(topic);
        if (notificationHandler != null){
            context.setNotificationHandler(notificationHandler);
        }
        context.setExpirationDelay(expirationDelay);
        context.setBlockingNotification(true);
        return context;
    }
    
    
    /**
     * Util to load classes using reflection.
     */
    private static Object loadClass(String clazzName) {
        Class className = null;
        try {
            className = Class.forName(clazzName, true,
                    Thread.currentThread().getContextClassLoader());
            return className.newInstance();
        } catch (Throwable t) {
            logger.log(Level.SEVERE,"Invalid NotificationHandler",t);
        }
        return null;
    }
    
    /**
     * Return the time a connection can stay idle.
     * @return
     */
    public long getExpirationDelay() {
        return expirationDelay;
    }
    
    /**
     * Set the maximum idle time a connection can stay suspended.
     * @param expirationDelay
     */
    public void setExpirationDelay(long expirationDelay) {
        this.expirationDelay = expirationDelay;
    }
}
    © 2015 - 2025 Weber Informatics LLC | Privacy Policy