Package net.zschech.gwt.comet.server

Source Code of net.zschech.gwt.comet.server.CometServlet

/*
* Copyright 2009 Richard Zschech.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package net.zschech.gwt.comet.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.ref.SoftReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import net.zschech.gwt.comet.client.impl.CometTransport;
import net.zschech.gwt.comet.server.impl.AsyncServlet;
import net.zschech.gwt.comet.server.impl.CometServletResponseImpl;
import net.zschech.gwt.comet.server.impl.CometSessionImpl;
import net.zschech.gwt.comet.server.impl.EventSourceCometServletResponse;
import net.zschech.gwt.comet.server.impl.HTTPRequestCometServletResponse;
import net.zschech.gwt.comet.server.impl.IEHTMLFileCometServletResponse;
import net.zschech.gwt.comet.server.impl.OperaEventSourceCometServletResponse;

import com.google.gwt.rpc.server.ClientOracle;
import com.google.gwt.rpc.server.HostedModeClientOracle;
import com.google.gwt.rpc.server.WebModeClientOracle;
import com.google.gwt.user.server.rpc.SerializationPolicy;

/**
* This is the base class for application's Comet servlets. To process a Comet request override
* {@link #doComet(CometServletResponse)} and send messages by calling {@link CometServletResponse#write(Serializable)}
* or enqueue messages using {@link CometServletResponse#getSession()} and {@link CometSession#enqueue(Serializable)}.
*
* @author Richard Zschech
*/
public class CometServlet extends HttpServlet {
 
  public static final String AUTO_CREATE_COMET_SESSION = "net.zschech.gwt.comet.server.auto.create.comet.session.on.comet.request";
 
  private static final long serialVersionUID = 820972291784919880L;
 
  private int heartbeat = 15 * 1000; // 15 seconds by default
 
  private transient AsyncServlet async;
 
  private boolean autoCreateCometSession;
 
  public void setHeartbeat(int heartbeat) {
    this.heartbeat = heartbeat;
  }
 
  public int getHeartbeat() {
    return heartbeat;
  }
 
  @Override
  public void init() throws ServletException {
    ServletConfig servletConfig = getServletConfig();
    String heartbeat = servletConfig.getInitParameter("heartbeat");
    if (heartbeat != null) {
      this.heartbeat = Integer.parseInt(heartbeat);
    }
    this.autoCreateCometSession = "true".equals(getServletConfig().getInitParameter(AUTO_CREATE_COMET_SESSION));
    async = AsyncServlet.initialize(getServletContext());
  }
 
  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    try {
      int requestHeartbeat = getHeartbeat();
      String requestedHeartbeat = request.getParameter("heartbeat");
      if (requestedHeartbeat != null) {
        try {
          requestHeartbeat = Integer.parseInt(requestedHeartbeat);
          if (requestHeartbeat <= 0) {
            throw new IOException("invalid heartbeat parameter");
          }
        }
        catch (NumberFormatException e) {
          throw new IOException("invalid heartbeat parameter");
        }
      }
     
      ClientOracle clientOracle = getClientOracle(request);
      SerializationPolicy serializationPolicy = clientOracle == null ? createSerializationPolicy() : null;
      CometServletResponseImpl cometServletResponse = createCometServletResponse(request, response, serializationPolicy, clientOracle, requestHeartbeat);
      doCometImpl(cometServletResponse);
    }
    catch (IOException e) {
      CometServletResponseImpl cometServletResponse = createCometServletResponse(request, response, null, null, 0);
      cometServletResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
    }
  }
 
  private CometServletResponseImpl createCometServletResponse(HttpServletRequest request, HttpServletResponse response, SerializationPolicy serializationPolicy, ClientOracle clientOracle, int requestHeartbeat) {
   
    String accept = request.getHeader("Accept");
    String userAgent = request.getHeader("User-Agent");
    if ("text/event-stream".equals(accept)) {
      return new EventSourceCometServletResponse(request, response, serializationPolicy, clientOracle, this, async, requestHeartbeat);
    }
    else if ("application/comet".equals(accept)) {
      return new HTTPRequestCometServletResponse(request, response, serializationPolicy, clientOracle, this, async, requestHeartbeat);
    }
    else if (userAgent != null && userAgent.contains("Opera")) {
      return new OperaEventSourceCometServletResponse(request, response, serializationPolicy, clientOracle, this, async, requestHeartbeat);
    }
    else {
      return new IEHTMLFileCometServletResponse(request, response, serializationPolicy, clientOracle, this, async, requestHeartbeat);
    }
  }
 
  private void doCometImpl(CometServletResponseImpl response) throws IOException {
    try {
      // setup the request
      response.initiate();
     
      if (autoCreateCometSession) {
        response.getSession();
      }
     
      // call the application code
      doComet(response);
    }
    catch (IOException e) {
      log("Error calling doComet()", e);
      response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
    }
    catch (ServletException e) {
      log("Error calling doComet()", e);
      response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
    }
   
    // at this point the application may have spawned threads to process this response
    // so we have to be careful about concurrency from here on
    response.suspend();
  }
 
  /**
   * Override this method to process a new comet request. All required information from the {@link HttpServletRequest}
   * must be retrieved {@link CometServletResponse#getRequest()} in this method as it will not be available after this
   * method returns and the request is suspended. This method may write data to the Comet response but should not
   * block. Writing data from this method before the request is suspended can improve the efficiency because padding
   * data may not be needed to cause the browser to start processing the stream.
   *
   * @param cometResponse
   * @throws ServletException
   * @throws IOException
   */
  protected void doComet(CometServletResponse cometResponse) throws ServletException, IOException {
  }
 
  /**
   * Override this method to be notified of the Comet connection being terminated.
   *
   * @param cometResponse
   * @param serverInitiated
   */
  public void cometTerminated(CometServletResponse cometResponse, boolean serverInitiated) {
  }
 
  /**
   * Override this method to override the requested heartbeat. By default only requested heartbeats > this.heartbeat
   * are allowed.
   *
   * @param requestedHeartbeat
   * @return
   */
  protected int getHeartbeat(int requestedHeartbeat) {
    return requestedHeartbeat < heartbeat ? heartbeat : requestedHeartbeat;
  }
 
  protected SerializationPolicy createSerializationPolicy() {
    return new SerializationPolicy() {
      @Override
      public boolean shouldDeserializeFields(final Class<?> clazz) {
        throw new UnsupportedOperationException("shouldDeserializeFields");
      }
     
      @Override
      public boolean shouldSerializeFields(final Class<?> clazz) {
        return Object.class != clazz;
      }
     
      @Override
      public void validateDeserialize(final Class<?> clazz) {
        throw new UnsupportedOperationException("validateDeserialize");
      }
     
      @Override
      public void validateSerialize(final Class<?> clazz) {
      }
    };
  }
 
  private final Map<String, SoftReference<ClientOracle>> clientOracleCache = new HashMap<String, SoftReference<ClientOracle>>();
 
  protected ClientOracle getClientOracle(HttpServletRequest request) throws IOException {
    String permutationStrongName = request.getParameter(CometTransport.STRONG_NAME_PARAMETER);
    if (permutationStrongName == null) {
      return null;
    }
   
    ClientOracle toReturn;
    synchronized (clientOracleCache) {
      if (clientOracleCache.containsKey(permutationStrongName)) {
        toReturn = clientOracleCache.get(permutationStrongName).get();
        if (toReturn != null) {
          return toReturn;
        }
      }
     
      if ("HostedMode".equals(permutationStrongName)) {
        // if (!allowHostedModeConnections()) {
        // throw new SecurityException("Blocked hosted mode request");
        // }
        toReturn = new HostedModeClientOracle();
      }
      else {
        String moduleBase = request.getParameter(CometTransport.MODULE_BASE_PARAMETER);
        if (moduleBase == null) {
          return null;
        }
       
        String basePath = new URL(moduleBase).getPath();
        if (basePath == null) {
          throw new MalformedURLException("Blocked request without GWT base path parameter (XSRF attack?)");
        }
       
        String contextPath = getServletContext().getContextPath();
        if (!basePath.startsWith(contextPath)) {
          throw new MalformedURLException("Blocked request with invalid GWT base path parameter (XSRF attack?)");
        }
        basePath = basePath.substring(contextPath.length());
       
        InputStream in = findClientOracleData(basePath, permutationStrongName);
       
        toReturn = WebModeClientOracle.load(in);
      }
      clientOracleCache.put(permutationStrongName, new SoftReference<ClientOracle>(toReturn));
    }
   
    return toReturn;
  }
 
  protected static final String CLIENT_ORACLE_EXTENSION = ".gwt.rpc";
 
  protected InputStream findClientOracleData(String requestModuleBasePath, String permutationStrongName) throws IOException {
    String resourcePath = requestModuleBasePath + permutationStrongName + CLIENT_ORACLE_EXTENSION;
    InputStream in = getServletContext().getResourceAsStream(resourcePath);
    if (in == null) {
      throw new IOException("Could not find ClientOracle data for permutation " + permutationStrongName);
    }
    return in;
  }
 
  public static CometSession getCometSession(HttpSession httpSession) {
    return getCometSession(httpSession, new ConcurrentLinkedQueue<Serializable>());
  }
 
  public static CometSession getCometSession(HttpSession httpSession, Queue<Serializable> queue) {
    return getCometSession(httpSession, true, queue);
  }
 
  public static CometSession getCometSession(HttpSession httpSession, boolean create) {
    return getCometSession(httpSession, create, create ? new ConcurrentLinkedQueue<Serializable>() : null);
  }
 
  public static CometSession getCometSession(HttpSession httpSession, boolean create, Queue<Serializable> queue) {
    synchronized (httpSession) {
      CometSession session = (CometSession) httpSession.getAttribute(CometSession.HTTP_SESSION_KEY);
      if (session == null) {
        if (create) {
          session = new CometSessionImpl(httpSession, queue, AsyncServlet.initialize(httpSession.getServletContext()));
          httpSession.setAttribute(CometSession.HTTP_SESSION_KEY, session);
        }
      }
      return session;
    }
  }
}
TOP

Related Classes of net.zschech.gwt.comet.server.CometServlet

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.