Package er.ajax

Source Code of er.ajax.AjaxPushRequestHandler

package er.ajax;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.log4j.Logger;

import com.webobjects.appserver.WORequest;
import com.webobjects.appserver.WORequestHandler;
import com.webobjects.appserver.WOResponse;
import com.webobjects.appserver.WOSession;
import com.webobjects.foundation.NSData;
import com.webobjects.foundation.NSNotification;
import com.webobjects.foundation.NSNotificationCenter;

import er.extensions.appserver.ERXKeepAliveResponse;
import er.extensions.foundation.ERXSelectorUtilities;

/**
* Request handler that offers push-style notifications. <br>
* Gets registered under "/push/" on framework load.<br>
* You should open an Ajax.Request, implement onInteractive: and the do
* something useful when you get new data. Changes should be pushed with
* push(sessionID, someString);
* <p>
* TODO:
* <li>currently the request stays open even when the client closed it (which is bad)
* <li>implement a boundary scheme to tell when a "message" is complete. This
* means we need a special Ajax.Request that does it.
* <li>implement various client-side stuff to be actually useful (chats, EO
* notifications).
* <li>ask Frank about his EO layer
* <li>use the request handler path as a "topic", so we can have more than one on a page.
*
* @author ak
*/
public class AjaxPushRequestHandler extends WORequestHandler {

  public static final String AjaxCometRequestHandlerKey = "push";

  protected static Logger log = Logger.getLogger(AjaxPushRequestHandler.class);

  private static ConcurrentHashMap<String, ConcurrentHashMap<String, ERXKeepAliveResponse>> responses = new ConcurrentHashMap<String, ConcurrentHashMap<String, ERXKeepAliveResponse>>();

  public AjaxPushRequestHandler() {
    NSNotificationCenter.defaultCenter().addObserver(this, ERXSelectorUtilities.notificationSelector("sessionDidTimeOut"), WOSession.SessionDidTimeOutNotification, null);
  }

  /**
   * Remove stale responses when a session times out.
   *
   * @param n the session timeout notification
   */
  public void sessionDidTimeOut(NSNotification n) {
    String id = (String) n.object();
    ConcurrentHashMap<String, ERXKeepAliveResponse> sessionResponses = responses.get(id);
    if (sessionResponses != null) {
      for (ERXKeepAliveResponse response : sessionResponses.values()) {
        response.reset();
      }
      responses.remove(id);
    }
  }

  /**
   * Get/Create the current request for the session and return it.
   *
   * @param request the request
   */
  @Override
  public WOResponse handleRequest(WORequest request) {
    String sessionID = request.sessionID();
    String name = request.requestHandlerPath();
    ERXKeepAliveResponse response = responseForSessionIDNamed(sessionID, name);
    response.reset();
    return response;
  }

  /**
   * Return or create the correct response for the session ID.
   *
   * @param sessionID the session id of the response
   * @param name the name of the response
   * @return response for ID
   */
  private static ERXKeepAliveResponse responseForSessionIDNamed(String sessionID, String name) {
    ERXKeepAliveResponse response = null;
    if (sessionID != null) {
      if(name == null)  {
        name = "";
      }
      ConcurrentHashMap<String, ERXKeepAliveResponse> sessionResponses = responses.get(sessionID);
      if (sessionResponses == null) {
        ConcurrentHashMap<String, ERXKeepAliveResponse> newSessionResponses = new ConcurrentHashMap<String, ERXKeepAliveResponse>();
        ConcurrentHashMap<String, ERXKeepAliveResponse> prevSessionResponses = responses.putIfAbsent(sessionID, newSessionResponses);
        sessionResponses = (prevSessionResponses == null) ? newSessionResponses : prevSessionResponses;
      }
      response = sessionResponses.get(name);
      if (response == null) {
        ERXKeepAliveResponse newResponse = new ERXKeepAliveResponse();
        ERXKeepAliveResponse prevResponse = sessionResponses.putIfAbsent(name, newResponse);
        response = (prevResponse == null) ? newResponse : prevResponse;
      }
    }
    return response;
  }

  /**
   * Returns whether or not there is a response open for the given session id and name.
   *
   * @param sessionID the session id of the push response
   * @param name the name of the push response
   * @return whether or not there is still a response open
   */
  public static boolean isResponseOpen(String sessionID, String name) {
    ERXKeepAliveResponse response = responseForSessionIDNamed(sessionID, name);
    return response != null;
  }
 
  /**
   * Push a string message to the client. At the moment, there is no boundary
   * handling, so be aware that you could get only half of a message.
   *
   * @param sessionID the session id of the push response
   * @param name the name of the push response
   */
  public static void stop(String sessionID, String name) {
    Map<String, ERXKeepAliveResponse> sessionResponses = responses.get(sessionID);
    if (sessionResponses != null) {
      ERXKeepAliveResponse response = sessionResponses.get(name);
      if (response != null) {
        response.reset();
        sessionResponses.remove(name);
      }
      // not going to do an empty check on sessionResponses, because we'd have to synchronize on
      // the top-level responses to do it safely
    }
  }
 
  /**
   * Push a string message to the client. At the moment, there is no boundary
   * handling, so be aware that you could get only half of a message.
   *
   * @param sessionID the session id of the push response
   * @param name the name of the push response
   * @param message the message to push
   */
  public static void push(String sessionID, String name, String message) {
    ERXKeepAliveResponse response = responseForSessionIDNamed(sessionID, name);
    if (response != null) {
      StringBuilder sb = new StringBuilder();
      sb.append(message.length());
      sb.append(':');
      response.push(sb.toString());
      response.push(message);
    }
  }

  /**
   * Push a data message to the client. At the moment, there is no boundary
   * handling, so be aware that you could get only half of a message.
   *
   * @param sessionID the session id of the push response
   * @param name the name of the push response
   * @param message the message to push
   */
  public static void push(String sessionID, String name, NSData message) {
    ERXKeepAliveResponse response = responseForSessionIDNamed(sessionID, name);
    if (response != null) {
      StringBuilder sb = new StringBuilder();
      sb.append(message.length());
      sb.append(':');
      response.push(sb.toString());
      response.push(message.bytes());
    }
  }
}
TOP

Related Classes of er.ajax.AjaxPushRequestHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.