Package com.comcast.cmb.common.controller

Source Code of com.comcast.cmb.common.controller.CMBControllerServlet

/**

* Copyright 2012 Comcast Corporation
*
* 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 com.comcast.cmb.common.controller;

import com.comcast.cmb.common.model.IAuthModule;
import com.comcast.cmb.common.model.ReceiptModule;
import com.comcast.cmb.common.model.User;
import com.comcast.cmb.common.model.UserAuthModule;
import com.comcast.cmb.common.persistence.IUserPersistence;
import com.comcast.cmb.common.persistence.PersistenceFactory;
import com.comcast.cmb.common.util.CMBErrorCodes;
import com.comcast.cmb.common.util.CMBException;
import com.comcast.cmb.common.util.CMBProperties;
import com.comcast.cmb.common.util.Util;
import com.comcast.cmb.common.util.ValueAccumulator;
import com.comcast.cmb.common.util.ValueAccumulator.AccumulatorName;
import com.comcast.cns.controller.CNSControllerServlet;
import com.comcast.cqs.controller.CQSCache;
import com.comcast.cqs.controller.CQSControllerServlet;
import com.comcast.cqs.controller.CQSHttpServletRequest;
import com.comcast.cqs.controller.CQSLongPollReceiver;
import com.comcast.cqs.io.CQSMessagePopulator;
import com.comcast.cqs.model.CQSMessage;
import com.comcast.cqs.model.CQSQueue;
import com.comcast.cqs.util.CQSConstants;

import org.apache.log4j.Logger;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
* Abstract class for all servlets
* @author aseem, bwolf, baosen, vvenkatraman, jorge, michael
*
*/
abstract public class CMBControllerServlet extends HttpServlet {

  private static final long serialVersionUID = 1L;

  private static Logger logger = Logger.getLogger(CMBControllerServlet.class);

  public static volatile ScheduledThreadPoolExecutor workerPool;

  private static volatile boolean initialized = false;

  protected IUserPersistence userHandler = null;
  protected IAuthModule authModule = null;

  /**
   * This instance of the acccumulator is used throughout the code to accumulate response times
   * for a particular thread
   */
  public final static ValueAccumulator valueAccumulator = new ValueAccumulator();

  public final static String VERSION = "2.2.42";
 
  public final static int HARD_TIMEOUT_SEC = CMBProperties.getInstance().getCMBRequestTimeoutSec();

  public volatile static ConcurrentHashMap<String, AtomicLong> callStats;
  public volatile static ConcurrentHashMap<String, AtomicLong> callFailureStats;

  public final static int NUM_MINUTES = 60;
  public final static int NUM_BUCKETS = 10;

  public volatile static AtomicLong[][] callResponseTimes1MS = new AtomicLong[NUM_MINUTES][NUM_BUCKETS];
  public volatile static AtomicLong[][] callResponseTimes10MS = new AtomicLong[NUM_MINUTES][NUM_BUCKETS];
  public volatile static AtomicLong[][] callResponseTimes100MS = new AtomicLong[NUM_MINUTES][NUM_BUCKETS];
  public volatile static AtomicLong[][] callResponseTimes1000MS = new AtomicLong[NUM_MINUTES][NUM_BUCKETS];
  public volatile static AtomicLong[][] callResponseTimesRedisMS = new AtomicLong[NUM_MINUTES][NUM_BUCKETS];
  public volatile static AtomicLong[][] callResponseTimesCassandraMS = new AtomicLong[NUM_MINUTES][NUM_BUCKETS];

  public volatile static ConcurrentHashMap<String, AtomicLong[][]> callResponseTimesByApi;

  public volatile static AtomicInteger currentMinute;

  public volatile static String[] recentErrors = new String[10];
  public volatile static int recentErrorIdx = -1;

  public static final long startTime = System.currentTimeMillis();

  @Override   
  public void init() throws ServletException {

    try {

      if (!initialized) {

        Util.initLog4j();
        CMBProperties.getInstance();
        workerPool = new ScheduledThreadPoolExecutor(CMBProperties.getInstance().getCMBWorkerPoolSize());
        initStats();
        initialized = true;
      }

    } catch (Exception ex) {
      throw new ServletException(ex);
    }
  }

  protected void initRequest() {

    userHandler = PersistenceFactory.getUserPersistence();
    authModule = new UserAuthModule();
    authModule.setUserPersistence(userHandler);
  }

  public static void initStats() {

    callStats = new ConcurrentHashMap<String, AtomicLong>();
    callFailureStats = new ConcurrentHashMap<String, AtomicLong>();

    callResponseTimes1MS = new AtomicLong[NUM_MINUTES][NUM_BUCKETS];
    callResponseTimes10MS = new AtomicLong[NUM_MINUTES][NUM_BUCKETS];
    callResponseTimes100MS = new AtomicLong[NUM_MINUTES][NUM_BUCKETS];
    callResponseTimes1000MS = new AtomicLong[NUM_MINUTES][NUM_BUCKETS];
    callResponseTimesRedisMS = new AtomicLong[NUM_MINUTES][NUM_BUCKETS];
    callResponseTimesCassandraMS = new AtomicLong[NUM_MINUTES][NUM_BUCKETS];
    callResponseTimesByApi = new ConcurrentHashMap<String, AtomicLong[][]>();

    for (int i=0; i<NUM_MINUTES; i++) {
      for (int k=0; k<NUM_BUCKETS; k++) {
        callResponseTimes1MS[i][k] = new AtomicLong();
        callResponseTimes10MS[i][k] = new AtomicLong();
        callResponseTimes100MS[i][k] = new AtomicLong();
        callResponseTimes1000MS[i][k] = new AtomicLong();
        callResponseTimesRedisMS[i][k] = new AtomicLong();
        callResponseTimesCassandraMS[i][k] = new AtomicLong();
      }
    }

    currentMinute = new AtomicInteger(new Date(System.currentTimeMillis()).getMinutes());

    recentErrors = new String[10];
    recentErrorIdx = -1;
  }

  /**
   * handles the action
   * @param action
   * @param user the user making the call
   * @param request
   * @param response
   * @return true if this action was performed, false otherwise
   * @throws Exception
   */
  abstract protected boolean handleAction(String action, User user, AsyncContext asyncContext) throws Exception;

  /**
   *
   * @param action
   * @return true if the action is supported by this servlet, false otherwise
   * @throws ServletException
   */
  abstract protected boolean isValidAction(String action) throws ServletException;


  /**
   * @param action
   * @return true if validation is required. False otherwise
   * Should be overriden by subclasses
   */
  protected boolean isAuthenticationRequired(String action) {
    return true;
  }

  private String getParameterString(AsyncContext asyncContext) {

    HttpServletRequest request = (HttpServletRequest)asyncContext.getRequest();
    Enumeration<String> keys = request.getParameterNames();
    StringBuffer params = new StringBuffer("");
    boolean logMessageBodyFlag = true;

    while (keys.hasMoreElements()) {

      String key = keys.nextElement();
      String value = request.getParameter(key);
      int length = 0;

      if(value != null){
        length = value.length();
      }
     
      if (key.equals("MessageBody")) {
        if (CMBProperties.getInstance().getMaxMessagePayloadLogLength() > 0) {
          if (value != null && value.length() > CMBProperties.getInstance().getMaxMessagePayloadLogLength()) {
            value = value.substring(0, CMBProperties.getInstance().getMaxMessagePayloadLogLength())
                + "...";
            logMessageBodyFlag = true;
          }
        } else {
          logMessageBodyFlag = false;
          value = null;
        }
      }
     
      if (value != null) {
        if (value.indexOf('\n') >= 0) {
          value = value.replace("\n", "\\n");
        }
        if (value.indexOf('\r') >= 0) {
          value = value.replace("\r", "\\r");
        }
      }

      if (!key.equals("MessageBody") || logMessageBodyFlag == true){
        params.append(key).append("=").append(value).append(" ");
      }
      if (key.equals("MessageBody")) {
        params.append("msg_size=").append(length).append(" ");
      }
    }

    return params.toString();
  }

  private String getUrlQueryString(AsyncContext asyncContext) {

    HttpServletRequest request = (HttpServletRequest)asyncContext.getRequest();
    Enumeration<String> keys = request.getParameterNames();
    StringBuffer params = new StringBuffer("?");

    while (keys.hasMoreElements()) {

      String key = keys.nextElement();
      String value = request.getParameter(key);

      if (value != null && value.length() > CMBProperties.getInstance().getMaxMessagePayloadLogLength()) {
        value = value.substring(0, CMBProperties.getInstance().getMaxMessagePayloadLogLength()) + "...";
      }

      if (value != null) {
        if (value.indexOf('\n') >= 0) {
          value = value.replace("\n", "\\n");
        }
        if (value.indexOf('\r') >= 0) {
          value = value.replace("\r", "\\r");
        }
      }

      params.append(key).append("=").append(value).append("&");
    }

    return params.toString();
  }

  private void logStats(String action, long responseTimeMS, long redisTimeMS, long cassandraTimeMS) {
   
    try {
     
      if (!initialized) {
        return;
      }

      if (action != null && !action.equals("")) {

        callStats.putIfAbsent(action, new AtomicLong());

        if (callStats.get(action).incrementAndGet() == Long.MAX_VALUE - 100) {
          callStats = new ConcurrentHashMap<String, AtomicLong>();
        }

        int newMinute = new Date(System.currentTimeMillis()).getMinutes();
        int oldMinute = currentMinute.getAndSet(newMinute);

        if (newMinute != oldMinute) {
          int eraseIdx = (newMinute+1)%60;
          for (int i=0; i<NUM_BUCKETS; i++) {
            callResponseTimes1MS[eraseIdx][i].set(0);
            callResponseTimes10MS[eraseIdx][i].set(0);
            callResponseTimes100MS[eraseIdx][i].set(0);
            callResponseTimes1000MS[eraseIdx][i].set(0);
            callResponseTimesRedisMS[eraseIdx][i].set(0);
            callResponseTimesCassandraMS[eraseIdx][i].set(0);
            for (String ac : callResponseTimesByApi.keySet()) {
              callResponseTimesByApi.get(ac)[eraseIdx][i].set(0);
            }
          }
        }

        int responseTimeIdx;

        // response time percentiles

        responseTimeIdx = (int)(responseTimeMS)/1;

        if (responseTimeIdx > NUM_BUCKETS-1) {
          responseTimeIdx = NUM_BUCKETS-1;
        } else if (responseTimeIdx < 0) {
          responseTimeIdx = 0;
        }

        callResponseTimes1MS[newMinute][responseTimeIdx].incrementAndGet();

        responseTimeIdx = (int)(responseTimeMS)/10;

        if (responseTimeIdx > NUM_BUCKETS-1) {
          responseTimeIdx = NUM_BUCKETS-1;
        } else if (responseTimeIdx < 0) {
          responseTimeIdx = 0;
        }

        callResponseTimes10MS[newMinute][responseTimeIdx].incrementAndGet();

        responseTimeIdx = (int)(responseTimeMS)/100;

        if (responseTimeIdx > NUM_BUCKETS-1) {
          responseTimeIdx = NUM_BUCKETS-1;
        } else if (responseTimeIdx < 0) {
          responseTimeIdx = 0;
        }

        callResponseTimes100MS[newMinute][responseTimeIdx].incrementAndGet();

        responseTimeIdx = (int)(responseTimeMS)/1000;

        if (responseTimeIdx > NUM_BUCKETS-1) {
          responseTimeIdx = NUM_BUCKETS-1;
        } else if (responseTimeIdx < 0) {
          responseTimeIdx = 0;
        }

        callResponseTimes1000MS[newMinute][responseTimeIdx].incrementAndGet();

        AtomicLong[][] callResponseTimes = callResponseTimesByApi.get(action);

        if (callResponseTimes != null) {
       
          // resolution for the api specific response time array is always 10 ms
 
          responseTimeIdx = (int)(responseTimeMS)/10;
 
          if (responseTimeIdx > NUM_BUCKETS-1) {
            responseTimeIdx = NUM_BUCKETS-1;
          } else if (responseTimeIdx < 0) {
            responseTimeIdx = 0;
          }
         
          callResponseTimes[newMinute][responseTimeIdx].incrementAndGet();
        }

        // redis time

        responseTimeIdx = (int)(redisTimeMS)/1;

        if (responseTimeIdx > NUM_BUCKETS-1) {
          responseTimeIdx = NUM_BUCKETS-1;
        } else if (responseTimeIdx < 0) {
          responseTimeIdx = 0;
        }

        callResponseTimesRedisMS[newMinute][responseTimeIdx].incrementAndGet();

        // cassandra time

        responseTimeIdx = (int)(cassandraTimeMS)/1;

        if (responseTimeIdx > NUM_BUCKETS-1) {
          responseTimeIdx = NUM_BUCKETS-1;
        } else if (responseTimeIdx < 0) {
          responseTimeIdx = 0;
        }

        callResponseTimesCassandraMS[newMinute][responseTimeIdx].incrementAndGet();
      }
    } catch (Exception ex) {
      logger.warn("event=failed_to_log_stats", ex);
    }
  }

  private String getLogLine(AsyncContext asyncContext, CQSHttpServletRequest request, User user, long responseTimeMS, String status) {

    StringBuffer logLine = new StringBuffer("");

    logLine.append("event=req status="+status+" client=").append(request.getRemoteAddr());

    logLine.append(((this instanceof CQSControllerServlet) ? (" queue_url=" + request.getPathInfo()) : ""));

    if (request.getReceiptHandles() != null && request.getReceiptHandles().size() > 0) {
      for (int i=0; i<request.getReceiptHandles().size(); i++) {
        logLine.append(" ReceiptHandle=").append(request.getReceiptHandles().get(i));
      }
    }

    logLine.append(" ").append(getParameterString(asyncContext));
    logLine.append((user != null ? "user=" + user.getUserName() : ""));
   
    if (request.getAttribute("lp") == null) {
      //if status is timeout for normal action, it does not have below info
      if((status != null) && (!status.equals("timeout"))){
        logLine.append(" resp_ms=").append(responseTimeMS);
        logLine.append(" cass_ms=" + valueAccumulator.getCounter(AccumulatorName.CassandraTime));
        logLine.append(" cass_num_rd=" + valueAccumulator.getCounter(AccumulatorName.CassandraRead));
        logLine.append(" cass_num_wr=" + valueAccumulator.getCounter(AccumulatorName.CassandraWrite));
        logLine.append(((this instanceof CNSControllerServlet) ? (" cnscqs_ms=" + CMBControllerServlet.valueAccumulator.getCounter(AccumulatorName.CNSCQSTime)) : ""));
        logLine.append(((this instanceof CQSControllerServlet) ? (" redis_ms=" + valueAccumulator.getCounter(AccumulatorName.RedisTime)) : ""));
        logLine.append(" io_ms=" + valueAccumulator.getCounter(AccumulatorName.IOTime));
        logLine.append(" asyncq_ms=" + valueAccumulator.getCounter(AccumulatorName.AsyncQueueTime));
        logLine.append(" auth_ms=" + valueAccumulator.getCounter(AccumulatorName.CMBControllerPreHandleAction));       
      }
    } else if (request.getAttribute("lp").equals("yy")) {  // long poll receive with messages

      logLine.append(" resp_ms=").append(responseTimeMS);
      logLine.append(" cass_ms=" + request.getAttribute("cass_ms"));
      logLine.append(" cass_num_rd=" + request.getAttribute("cass_num_rd"));
      logLine.append(" cass_num_wr=" + request.getAttribute("cass_num_wr"));
      logLine.append(" redis_ms=" + request.getAttribute("redis_ms"));
      logLine.append(" io_ms=" + request.getAttribute("io_ms"));
      logLine.append(" lp_ms=").append(System.currentTimeMillis()-request.getRequestReceivedTimestamp());

    } else if (request.getAttribute("lp").equals("yn")) { // long poll receive without messages
      logLine.append(" lp_ms=").append(System.currentTimeMillis()-request.getRequestReceivedTimestamp());     
    }
   
    logLine.append(" async_pool_queue=").append(CMBControllerServlet.workerPool.getQueue().size()).
    append(" async_pool_size=").append(CMBControllerServlet.workerPool.getActiveCount());
    if(CMBProperties.getInstance().getCQSServiceEnabled()){
      logLine.append(" cqs_pool_size=").append(CMB.cqsServer.getThreadPool().getThreads());
    }
    if(CMBProperties.getInstance().getCNSServiceEnabled()){
      logLine.append(" cns_pool_size=").append(CMB.cnsServer.getThreadPool().getThreads());
    }
    // log external headers from proxy if present
   
    String rid = request.getHeader("CMB-RID");

    if (rid != null) {
      logLine.append(" rid=").append(rid);
    }
   
    String extStartTime = request.getHeader("START-TIME");
   
    if (extStartTime != null) {
      logLine.append(" ext_ts=").append(extStartTime);
    }

    return logLine.toString();
  }
 
  private String getFullRequestUrl(AsyncContext asyncContext) {
    HttpServletRequest request = (HttpServletRequest)asyncContext.getRequest();
    return "event=raw_request url="  + request.getRequestURL() + "/" + this.getUrlQueryString(asyncContext);
  }

  private void handleRequest(AsyncContext asyncContext) throws ServletException, IOException

    CQSHttpServletRequest request = (CQSHttpServletRequest)asyncContext.getRequest();
    HttpServletResponse response = (HttpServletResponse)asyncContext.getResponse();

    User user = null;
    String action = request.getParameter("Action");
    long ts1 = System.currentTimeMillis();

    try {

      valueAccumulator.initializeAllCounters();
     
      valueAccumulator.addToCounter(AccumulatorName.AsyncQueueTime, System.currentTimeMillis()-request.getRequestReceivedTimestamp());

      initRequest();
      response.setContentType("text/xml");

      if (!isValidAction(action)) {
        logger.warn("event=invalid_action action=" + action + " url=" + request.getRequestURL());
        String errXml = createErrorResponse(CMBErrorCodes.InvalidAction.getCMBCode(), action + " is not a valid action");
        response.setStatus(CMBErrorCodes.InvalidAction.getHttpCode());
        response.getWriter().println(errXml);
        return;
      }

      if (isAuthenticationRequired(action)) {
        user = authModule.authenticateByRequest(request);
      } else {
        user = null;
      }

      long ts3 = System.currentTimeMillis();

      valueAccumulator.addToCounter(AccumulatorName.CMBControllerPreHandleAction, (ts3 - ts1));
      handleAction(action, user, asyncContext);
      response.setStatus(200);
      long ts2 = System.currentTimeMillis();
      //if it is waiting for long poll receive, do not log now. Wait till asyn request finished.
      if(request.getAttribute("lp")==null){
        String logLine = getLogLine(asyncContext, request, user, ts2-ts1, "ok");
        logger.info(logLine);
      }
      //String rawRequest = this.getFullRequestUrl(asyncContext);
      //logger.info(rawRequest);

      if (CMBProperties.getInstance().isCMBStatsEnabled()) {
        logStats(action, ts2-ts1, valueAccumulator.getCounter(AccumulatorName.RedisTime), valueAccumulator.getCounter(AccumulatorName.CassandraTime));
      }

    } catch (Exception ex) {

      long ts2 = System.currentTimeMillis();
      String logLine = getLogLine(asyncContext, request, user, ts2-ts1, "failed");
      logger.error(logLine, ex);

      int httpCode = CMBErrorCodes.InternalError.getHttpCode();
      String code = CMBErrorCodes.InternalError.getCMBCode();
      String message = "There is an internal problem with CMB";

      if (ex instanceof CMBException) {
        httpCode = ((CMBException)ex).getHttpCode();
        code = ((CMBException) ex).getCMBCode();
        message = ex.getMessage();
      }

      String errXml = createErrorResponse(code, message);
      response.setStatus(httpCode);
      Action.writeResponse(errXml, response);

      if (CMBProperties.getInstance().isCMBStatsEnabled() && action != null && !action.equals("")) {

        callFailureStats.putIfAbsent(action, new AtomicLong());

        if (callFailureStats.get(action).incrementAndGet() == Long.MAX_VALUE - 100) {
          callFailureStats = new ConcurrentHashMap<String, AtomicLong>();
        }

        StringBuffer errorDetail = new StringBuffer(new Date() + "|" + logLine + "|" + ex.getMessage() + "\n");

        for (StackTraceElement element : ex.getStackTrace()) {
          errorDetail.append(element).append("\n");
        }

        // not thread safe but that's ok here

        recentErrorIdx = (recentErrorIdx+1)%recentErrors.length;
        recentErrors[recentErrorIdx] = errorDetail.toString();
      }

    } finally {
      valueAccumulator.deleteAllCounters();
    }
  }

  @Override
  public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    doPost(request, response);
  }

  @Override
  public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    if (!request.isAsyncSupported()) {
      throw new ServletException("Servlet container does not support asynchronous calls");
    }
   
    HttpServletRequest wrappedRequest = new CQSHttpServletRequest(request);
    final AsyncContext asyncContext = request.startAsync(wrappedRequest, response);

    // jetty appears to require calling setTimeout on http handler thread so we are setting
    // wait time seconds for long polling receive message here
    String actionParam = wrappedRequest.getParameter("Action");
    String waitTimeSecondsParam = wrappedRequest.getParameter(CQSConstants.WAIT_TIME_SECONDS);

    if (actionParam != null && actionParam.equals("ReceiveMessage") && waitTimeSecondsParam != null) {

      try {

        int waitTimeSeconds = Integer.parseInt(waitTimeSecondsParam);

        if (waitTimeSeconds >= 1 && waitTimeSeconds <= CMBProperties.getInstance().getCMBRequestTimeoutSec()) {
          asyncContext.setTimeout(waitTimeSeconds*1000);
          ((CQSHttpServletRequest)asyncContext.getRequest()).setWaitTime(waitTimeSeconds*1000);
          logger.debug("event=set_message_timeout secs=" + waitTimeSeconds);
        }

      } catch (Exception ex) {
        logger.warn("event=ignoring_suspicious_wait_time_parameter value=" + waitTimeSecondsParam);
      }

    } else if (actionParam != null && actionParam.equals("ReceiveMessage")) {

      String queueUrl = com.comcast.cqs.util.Util.getRelativeForAbsoluteQueueUrl(request.getRequestURL().toString());
      CQSQueue queue;
      int waitTimeSeconds = 0;

      try {

        queue = CQSCache.getCachedQueue(queueUrl);

        if (queue != null && queue.getReceiveMessageWaitTimeSeconds() > 0) {
          waitTimeSeconds = queue.getReceiveMessageWaitTimeSeconds();
        }

      } catch (Exception ex) {
        logger.warn("event=lookup_queue queue_url=" + queueUrl, ex);
      }

      if (waitTimeSeconds > 0) {
        asyncContext.setTimeout(waitTimeSeconds*1000);
        ((CQSHttpServletRequest)asyncContext.getRequest()).setWaitTime(waitTimeSeconds*1000);
        logger.debug("event=set_queue_timeout secs=" + waitTimeSeconds + " queue_url=" + queueUrl);
      } else {
        asyncContext.setTimeout(HARD_TIMEOUT_SEC*1000);
        logger.debug("event=set_default_timeout secs="+CMBProperties.getInstance().getCMBRequestTimeoutSec());
      }

    } else {
      asyncContext.setTimeout(HARD_TIMEOUT_SEC*1000);
      logger.debug("event=set_default_timeout secs=" + CMBProperties.getInstance().getCMBRequestTimeoutSec());
    }

    asyncContext.addListener(new AsyncListener() {

      @Override
      public void onComplete(AsyncEvent asyncEvent) throws IOException {

        AsyncContext asyncContext = asyncEvent.getAsyncContext();
        CQSHttpServletRequest request = (CQSHttpServletRequest)asyncContext.getRequest();
        String action = request.getParameter("Action");
        if(action.equals("ReceiveMessage")){
          String lpValue = (String)request.getAttribute("lp");

          if(lpValue!=null){

            User user = authModule.getUserByRequest(request);

            Object lp_ms = request.getAttribute("lp_ms");
            String logLine = null;
            if(lp_ms!=null){
              logLine = getLogLine(asyncContext, request, user, (Long)request.getAttribute("lp_ms"), "ok");
            }else{
              logLine = getLogLine(asyncContext, request, user, 0, "ok");
            }
            logger.info(logLine);
          }
        }
       
      }

      @Override
      public void onError(AsyncEvent asyncEvent) throws IOException {

        int httpCode = CMBErrorCodes.InternalError.getHttpCode();
        String code = CMBErrorCodes.InternalError.getCMBCode();
        String message = "There is an internal problem with CMB";

        if (asyncEvent.getThrowable() instanceof CMBException) {
          httpCode = ((CMBException)asyncEvent.getThrowable()).getHttpCode();
          code = ((CMBException)asyncEvent.getThrowable()).getCMBCode();
          message = asyncEvent.getThrowable().getMessage();
        }

        String errXml = CMBControllerServlet.createErrorResponse(code, message);
        HttpServletResponse response = ((HttpServletResponse)asyncEvent.getSuppliedResponse());
        response.setStatus(httpCode);
        Action.writeResponse(errXml, response);

        if (!(asyncEvent.getSuppliedRequest() instanceof CQSHttpServletRequest)) {
          logger.error("event=invalid_request stage=on_error");
          return;
        }         

        CQSQueue queue = ((CQSHttpServletRequest)asyncEvent.getSuppliedRequest()).getQueue();
        AsyncContext asyncContext = asyncEvent.getAsyncContext();

        if (queue != null) {

          logger.info("event=on_error queue_url=" + queue.getAbsoluteUrl());

          ConcurrentLinkedQueue<AsyncContext> queueContextsList = CQSLongPollReceiver.contextQueues.get(queue.getArn());

          if (queueContextsList != null && asyncContext != null) {
            queueContextsList.remove(asyncContext);
          }

        } else {
          logger.info("event=on_error");
        }

        asyncContext.complete();
      }

      @Override
      public void onTimeout(AsyncEvent asyncEvent) throws IOException {
       
        if (!(asyncEvent.getSuppliedRequest() instanceof CQSHttpServletRequest)) {
          logger.error("event=invalid_request stage=on_timeout");
          return;
        }
       
        //first check if it is long poll receive, if so, complete and return.
        AsyncContext asyncContext = asyncEvent.getAsyncContext();
        CQSHttpServletRequest request = (CQSHttpServletRequest)asyncContext.getRequest();
        String action = request.getParameter("Action");
        if(action.equals("ReceiveMessage")){
          String lpValue = (String)request.getAttribute("lp");
          if(lpValue!=null){
            String out = CQSMessagePopulator.getReceiveMessageResponseAfterSerializing(new ArrayList<CQSMessage>(), new ArrayList<String>());
            asyncEvent.getSuppliedResponse().getWriter().println(out);
           
            CQSQueue queue = ((CQSHttpServletRequest)asyncEvent.getSuppliedRequest()).getQueue();

            asyncContext = asyncEvent.getAsyncContext();

            if (queue != null) {

              logger.debug("event=on_timeout queue_url=" + queue.getAbsoluteUrl());

              ConcurrentLinkedQueue<AsyncContext> queueContextsList = CQSLongPollReceiver.contextQueues.get(queue.getArn());

              if (queueContextsList != null && asyncContext != null) {
                queueContextsList.remove(asyncContext);
              }

            } else {
              logger.debug("event=on_timeout");
            }
            asyncContext.complete();
            return;           
          }
        }       
        //for other Time out show log error and return error response.
        int httpCode = CMBErrorCodes.InternalError.getHttpCode();
        String code = CMBErrorCodes.InternalError.getCMBCode();
        String message = "CMB timeout after "+CMBProperties.getInstance().getCMBRequestTimeoutSec()+" seconds";

        if (asyncEvent.getThrowable() instanceof CMBException) {
          httpCode = ((CMBException)asyncEvent.getThrowable()).getHttpCode();
          code = ((CMBException)asyncEvent.getThrowable()).getCMBCode();
          message = asyncEvent.getThrowable().getMessage();
        }

        String errXml = CMBControllerServlet.createErrorResponse(code, message);
        HttpServletResponse response = ((HttpServletResponse)asyncEvent.getSuppliedResponse());
        response.setStatus(httpCode);
        Action.writeResponse(errXml, response);

        if (!(asyncEvent.getSuppliedRequest() instanceof CQSHttpServletRequest)) {
          logger.error("event=invalid_request stage=on_timeout");
          return;
        }         

        CQSQueue queue = ((CQSHttpServletRequest)asyncEvent.getSuppliedRequest()).getQueue();
        asyncContext = asyncEvent.getAsyncContext();

        if (queue != null) {

          logger.error("event=on_timeout queue_url=" + queue.getAbsoluteUrl());

          ConcurrentLinkedQueue<AsyncContext> queueContextsList = CQSLongPollReceiver.contextQueues.get(queue.getArn());

          if (queueContextsList != null && asyncContext != null) {
            queueContextsList.remove(asyncContext);
          }

        } else {
          logger.error("event=on_timeout "+getLogLine(asyncContext, request, authModule.getUserByRequest(request), 0, "timeout"));
        }
        asyncContext.complete();
      }

      @Override
      public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
      }
    });

    workerPool.submit(new Runnable() {

      public void run() {

        try {
          ReceiptModule.init();
          handleRequest(asyncContext);
        } catch (Exception ex) {
          logger.error("event=async_api_call_failure receipt_id=" + ReceiptModule.getReceiptId(), ex);
        }

        if (!((CQSHttpServletRequest)asyncContext.getRequest()).isQueuedForProcessing()) {
          asyncContext.complete();
        }
      }
    });
  }
 
  public static String createErrorResponse(String code, String errorMsg) {
    StringBuffer message = new StringBuffer("<ErrorResponse>\n")
    .append("\t<Error>\n")
    .append("\t\t<Type>Sender</Type>\n")
    .append("\t\t<Code>").append(code).append("</Code>\n")
    .append("\t\t<Message>").append(errorMsg).append("</Message>\n")
    .append("\t\t<Detail/>\n")
    .append("\t</Error>\n")
    .append("\t<RequestId>").append(ReceiptModule.getReceiptId()).append("</RequestId>\n")
    .append("</ErrorResponse>\n");
    return message.toString();
  }
}
TOP

Related Classes of com.comcast.cmb.common.controller.CMBControllerServlet

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.