@Override
  public void validateSpecificParameters(final Map<String, String> params) {
    super.validateSpecificParameters(params);

    final Object pageSizeObj = params.get(ApiConstants.PAGE_SIZE);
    Long pageSize = null;
    if (pageSizeObj != null) {
      pageSize = Long.valueOf((String) pageSizeObj);
    }

    if (params.get(ApiConstants.PAGE) == null
        && pageSize != null
        && !pageSize.equals(BaseListCmd.s_pageSizeUnlimited)) {
      final ServerApiException ex =
          new ServerApiException(
              ApiErrorCode.PARAM_ERROR,
              "\"page\" parameter is required when \"pagesize\" is specified");
      ex.setCSErrorCode(CSExceptionErrorCode.getCSErrCode(ex.getClass().getName()));
      throw ex;
    } else if (pageSize == null && (params.get(ApiConstants.PAGE) != null)) {
      throw new ServerApiException(
          ApiErrorCode.PARAM_ERROR,
          "\"pagesize\" parameter is required when \"page\" is specified");
    }
  }
  public String getSerializedApiError(
      ServerApiException ex, Map<String, Object[]> apiCommandParams, String responseType) {
    String responseName = null;
    Class<?> cmdClass = null;
    String responseText = null;

    if (ex == null) {
      // this call should not be invoked with null exception
      return getSerializedApiError(
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          "Some internal error happened",
          apiCommandParams,
          responseType);
    }
    try {
      if (ex.getErrorCode() == ApiErrorCode.UNSUPPORTED_ACTION_ERROR
          || apiCommandParams == null
          || apiCommandParams.isEmpty()) {
        responseName = "errorresponse";
      } else {
        Object cmdObj = apiCommandParams.get("command");
        // cmd name can be null when "command" parameter is missing in
        // the request
        if (cmdObj != null) {
          String cmdName = ((String[]) cmdObj)[0];
          cmdClass = getCmdClass(cmdName);
          if (cmdClass != null) {
            responseName = ((BaseCmd) cmdClass.newInstance()).getCommandName();
          } else {
            responseName = "errorresponse";
          }
        }
      }
      ExceptionResponse apiResponse = new ExceptionResponse();
      apiResponse.setErrorCode(ex.getErrorCode().getHttpCode());
      apiResponse.setErrorText(ex.getDescription());
      apiResponse.setResponseName(responseName);
      ArrayList<String> idList = ex.getIdProxyList();
      if (idList != null) {
        for (int i = 0; i < idList.size(); i++) {
          apiResponse.addProxyObject(idList.get(i));
        }
      }
      // Also copy over the cserror code and the function/layer in which
      // it was thrown.
      apiResponse.setCSErrorCode(ex.getCSErrorCode());

      SerializationContext.current().setUuidTranslation(true);
      responseText = ApiResponseSerializer.toSerializedString(apiResponse, responseType);

    } catch (Exception e) {
      s_logger.error("Exception responding to http request", e);
    }
    return responseText;
  }
Beispiel #3
0
  @Override
  public void execute() {
    try {
      if ((getRamOvercommitRaito().compareTo(1f) <= 0)
          | (getCpuOvercommitRatio().compareTo(1f) <= 0)) {
        throw new InvalidParameterValueException(
            "Cpu and ram overcommit ratios  should not be less than 1");
      }
      List<? extends Cluster> result = _resourceService.discoverCluster(this);
      ListResponse<ClusterResponse> response = new ListResponse<ClusterResponse>();
      List<ClusterResponse> clusterResponses = new ArrayList<ClusterResponse>();
      if (result != null && result.size() > 0) {
        for (Cluster cluster : result) {
          ClusterResponse clusterResponse =
              _responseGenerator.createClusterResponse(cluster, false);
          clusterResponses.add(clusterResponse);
        }
      } else {
        throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add cluster");
      }

      response.setResponses(clusterResponses);
      response.setResponseName(getCommandName());

      this.setResponseObject(response);
    } catch (DiscoveryException ex) {
      s_logger.warn("Exception: ", ex);
      throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
    } catch (ResourceInUseException ex) {
      s_logger.warn("Exception: ", ex);
      ServerApiException e = new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
      for (String proxyObj : ex.getIdProxyList()) {
        e.addProxyObject(proxyObj);
      }
      throw e;
    }
  }
Beispiel #4
0
  @SuppressWarnings("unchecked")
  private void processRequest(HttpServletRequest req, HttpServletResponse resp) {
    StringBuffer auditTrailSb = new StringBuffer();
    auditTrailSb.append(" " + req.getRemoteAddr());
    auditTrailSb.append(" -- " + req.getMethod() + " ");
    // get the response format since we'll need it in a couple of places
    String responseType = BaseCmd.RESPONSE_TYPE_XML;
    Map<String, Object[]> params = new HashMap<String, Object[]>();
    params.putAll(req.getParameterMap());

    //
    // For HTTP GET requests, it seems that HttpServletRequest.getParameterMap() actually tries
    // to unwrap URL encoded content from ISO-9959-1.
    //
    // After failed in using setCharacterEncoding() to control it, end up with following hacking :
    // for all GET requests,
    // we will override it with our-own way of UTF-8 based URL decoding.
    //
    utf8Fixup(req, params);

    try {
      HttpSession session = req.getSession(false);
      Object[] responseTypeParam = params.get("response");
      if (responseTypeParam != null) {
        responseType = (String) responseTypeParam[0];
      }

      Object[] commandObj = params.get("command");
      if (commandObj != null) {
        String command = (String) commandObj[0];
        if ("logout".equalsIgnoreCase(command)) {
          // if this is just a logout, invalidate the session and return
          if (session != null) {
            Long userId = (Long) session.getAttribute("userid");
            Account account = (Account) session.getAttribute("accountobj");
            Long accountId = null;
            if (account != null) {
              accountId = account.getId();
            }
            auditTrailSb.insert(
                0,
                "(userId="
                    + userId
                    + " accountId="
                    + accountId
                    + " sessionId="
                    + session.getId()
                    + ")");
            if (userId != null) {
              _apiServer.logoutUser(userId);
            }
            try {
              session.invalidate();
            } catch (IllegalStateException ise) {
            }
          }
          auditTrailSb.append("command=logout");
          auditTrailSb.append(" " + HttpServletResponse.SC_OK);
          writeResponse(
              resp,
              getLogoutSuccessResponse(responseType),
              HttpServletResponse.SC_OK,
              responseType);
          return;
        } else if ("login".equalsIgnoreCase(command)) {
          auditTrailSb.append("command=login");
          // if this is a login, authenticate the user and return
          if (session != null) {
            try {
              session.invalidate();
            } catch (IllegalStateException ise) {
            }
          }
          session = req.getSession(true);
          String[] username = (String[]) params.get("username");
          String[] password = (String[]) params.get("password");
          String[] domainIdArr = (String[]) params.get("domainid");

          if (domainIdArr == null) {
            domainIdArr = (String[]) params.get("domainId");
          }
          String[] domainName = (String[]) params.get("domain");
          Long domainId = null;
          if ((domainIdArr != null) && (domainIdArr.length > 0)) {
            try {
              domainId = new Long(Long.parseLong(domainIdArr[0]));
              auditTrailSb.append(" domainid=" + domainId); // building the params for POST call
            } catch (NumberFormatException e) {
              s_logger.warn("Invalid domain id entered by user");
              auditTrailSb.append(
                  " "
                      + HttpServletResponse.SC_UNAUTHORIZED
                      + " "
                      + "Invalid domain id entered, please enter a valid one");
              String serializedResponse =
                  _apiServer.getSerializedApiError(
                      HttpServletResponse.SC_UNAUTHORIZED,
                      "Invalid domain id entered, please enter a valid one",
                      params,
                      responseType,
                      null);
              writeResponse(
                  resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType);
            }
          }
          String domain = null;
          if (domainName != null) {
            domain = domainName[0];
            auditTrailSb.append(" domain=" + domain);
            if (domain != null) {
              // ensure domain starts with '/' and ends with '/'
              if (!domain.endsWith("/")) {
                domain += '/';
              }
              if (!domain.startsWith("/")) {
                domain = "/" + domain;
              }
            }
          }

          if (username != null) {
            String pwd = ((password == null) ? null : password[0]);
            try {
              _apiServer.loginUser(session, username[0], pwd, domainId, domain, params);
              auditTrailSb.insert(
                  0,
                  "(userId="
                      + session.getAttribute("userid")
                      + " accountId="
                      + ((Account) session.getAttribute("accountobj")).getId()
                      + " sessionId="
                      + session.getId()
                      + ")");
              String loginResponse = getLoginSuccessResponse(session, responseType);
              writeResponse(resp, loginResponse, HttpServletResponse.SC_OK, responseType);
              return;
            } catch (CloudAuthenticationException ex) {
              // TODO: fall through to API key, or just fail here w/ auth error? (HTTP 401)
              try {
                session.invalidate();
              } catch (IllegalStateException ise) {
              }

              auditTrailSb.append(
                  " " + BaseCmd.ACCOUNT_ERROR + " " + ex.getMessage() != null
                      ? ex.getMessage()
                      : "failed to authenticate user, check if username/password are correct");
              String serializedResponse =
                  _apiServer.getSerializedApiError(
                      BaseCmd.ACCOUNT_ERROR,
                      ex.getMessage() != null
                          ? ex.getMessage()
                          : "failed to authenticate user, check if username/password are correct",
                      params,
                      responseType,
                      null);
              writeResponse(resp, serializedResponse, BaseCmd.ACCOUNT_ERROR, responseType);
              return;
            }
          }
        }
      }
      auditTrailSb.append(req.getQueryString());
      boolean isNew = ((session == null) ? true : session.isNew());

      // Initialize an empty context and we will update it after we have verified the request below,
      // we no longer rely on web-session here, verifyRequest will populate user/account information
      // if a API key exists
      UserContext.registerContext(
          _accountMgr.getSystemUser().getId(), _accountMgr.getSystemAccount(), null, false);
      Long userId = null;

      if (!isNew) {
        userId = (Long) session.getAttribute("userid");
        String account = (String) session.getAttribute("account");
        Object accountObj = session.getAttribute("accountobj");
        String sessionKey = (String) session.getAttribute("sessionkey");
        String[] sessionKeyParam = (String[]) params.get("sessionkey");
        if ((sessionKeyParam == null)
            || (sessionKey == null)
            || !sessionKey.equals(sessionKeyParam[0])) {
          try {
            session.invalidate();
          } catch (IllegalStateException ise) {
          }
          auditTrailSb.append(
              " "
                  + HttpServletResponse.SC_UNAUTHORIZED
                  + " "
                  + "unable to verify user credentials");
          String serializedResponse =
              _apiServer.getSerializedApiError(
                  HttpServletResponse.SC_UNAUTHORIZED,
                  "unable to verify user credentials",
                  params,
                  responseType,
                  null);
          writeResponse(
              resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType);
          return;
        }

        // Do a sanity check here to make sure the user hasn't already been deleted
        if ((userId != null)
            && (account != null)
            && (accountObj != null)
            && _apiServer.verifyUser(userId)) {
          String[] command = (String[]) params.get("command");
          if (command == null) {
            s_logger.info("missing command, ignoring request...");
            auditTrailSb.append(
                " " + HttpServletResponse.SC_BAD_REQUEST + " " + "no command specified");
            String serializedResponse =
                _apiServer.getSerializedApiError(
                    HttpServletResponse.SC_BAD_REQUEST,
                    "no command specified",
                    params,
                    responseType,
                    null);
            writeResponse(
                resp, serializedResponse, HttpServletResponse.SC_BAD_REQUEST, responseType);
            return;
          }
          UserContext.updateContext(userId, (Account) accountObj, session.getId());
        } else {
          // Invalidate the session to ensure we won't allow a request across management server
          // restarts if the userId
          // was serialized to the
          // stored session
          try {
            session.invalidate();
          } catch (IllegalStateException ise) {
          }

          auditTrailSb.append(
              " "
                  + HttpServletResponse.SC_UNAUTHORIZED
                  + " "
                  + "unable to verify user credentials");
          String serializedResponse =
              _apiServer.getSerializedApiError(
                  HttpServletResponse.SC_UNAUTHORIZED,
                  "unable to verify user credentials",
                  params,
                  responseType,
                  null);
          writeResponse(
              resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType);
          return;
        }
      }

      if (_apiServer.verifyRequest(params, userId)) {
        /*
         * if (accountObj != null) { Account userAccount = (Account)accountObj; if (userAccount.getType() ==
         * Account.ACCOUNT_TYPE_NORMAL) { params.put(BaseCmd.Properties.USER_ID.getName(), new String[] { userId });
         * params.put(BaseCmd.Properties.ACCOUNT.getName(), new String[] { account });
         * params.put(BaseCmd.Properties.DOMAIN_ID.getName(), new String[] { domainId });
         * params.put(BaseCmd.Properties.ACCOUNT_OBJ.getName(), new Object[] { accountObj }); } else {
         * params.put(BaseCmd.Properties.USER_ID.getName(), new String[] { userId });
         * params.put(BaseCmd.Properties.ACCOUNT_OBJ.getName(), new Object[] { accountObj }); } }
         *
         * // update user context info here so that we can take information if the request is authenticated // via api
         * key mechanism updateUserContext(params, session != null ? session.getId() : null);
         */

        auditTrailSb.insert(
            0,
            "(userId="
                + UserContext.current().getCallerUserId()
                + " accountId="
                + UserContext.current().getCaller().getId()
                + " sessionId="
                + (session != null ? session.getId() : null)
                + ")");

        try {
          String response = _apiServer.handleRequest(params, false, responseType, auditTrailSb);
          writeResponse(
              resp, response != null ? response : "", HttpServletResponse.SC_OK, responseType);
        } catch (ServerApiException se) {
          String serializedResponseText =
              _apiServer.getSerializedApiError(
                  se.getErrorCode(), se.getDescription(), params, responseType, null);
          resp.setHeader("X-Description", se.getDescription());
          writeResponse(resp, serializedResponseText, se.getErrorCode(), responseType);
          auditTrailSb.append(" " + se.getErrorCode() + " " + se.getDescription());
        }
      } else {
        if (session != null) {
          try {
            session.invalidate();
          } catch (IllegalStateException ise) {
          }
        }

        auditTrailSb.append(
            " "
                + HttpServletResponse.SC_UNAUTHORIZED
                + " "
                + "unable to verify user credentials and/or request signature");
        String serializedResponse =
            _apiServer.getSerializedApiError(
                HttpServletResponse.SC_UNAUTHORIZED,
                "unable to verify user credentials and/or request signature",
                params,
                responseType,
                null);
        writeResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType);
      }
    } catch (Exception ex) {
      if (ex instanceof ServerApiException
          && ((ServerApiException) ex).getErrorCode() == BaseCmd.UNSUPPORTED_ACTION_ERROR) {
        ServerApiException se = (ServerApiException) ex;
        String serializedResponseText =
            _apiServer.getSerializedApiError(
                se.getErrorCode(), se.getDescription(), params, responseType, null);
        resp.setHeader("X-Description", se.getDescription());
        writeResponse(resp, serializedResponseText, se.getErrorCode(), responseType);
        auditTrailSb.append(" " + se.getErrorCode() + " " + se.getDescription());
      } else {
        s_logger.error("unknown exception writing api response", ex);
        auditTrailSb.append(" unknown exception writing api response");
      }
    } finally {
      s_accessLogger.info(auditTrailSb.toString());
      // cleanup user context to prevent from being peeked in other request context
      UserContext.unregisterContext();
    }
  }
  @SuppressWarnings("rawtypes")
  public String handleRequest(
      Map params, boolean decode, String responseType, StringBuffer auditTrailSb)
      throws ServerApiException {
    String response = null;
    String[] command = null;
    try {
      command = (String[]) params.get("command");
      if (command == null) {
        s_logger.error("invalid request, no command sent");
        if (s_logger.isTraceEnabled()) {
          s_logger.trace("dumping request parameters");
          for (Object key : params.keySet()) {
            String keyStr = (String) key;
            String[] value = (String[]) params.get(key);
            s_logger.trace(
                "   key: " + keyStr + ", value: " + ((value == null) ? "'null'" : value[0]));
          }
        }
        throw new ServerApiException(
            ApiErrorCode.UNSUPPORTED_ACTION_ERROR, "Invalid request, no command sent");
      } else {
        Map<String, String> paramMap = new HashMap<String, String>();
        Set keys = params.keySet();
        Iterator keysIter = keys.iterator();
        while (keysIter.hasNext()) {
          String key = (String) keysIter.next();
          if ("command".equalsIgnoreCase(key)) {
            continue;
          }
          String[] value = (String[]) params.get(key);

          String decodedValue = null;
          if (decode) {
            try {
              decodedValue = URLDecoder.decode(value[0], "UTF-8");
            } catch (UnsupportedEncodingException usex) {
              s_logger.warn(key + " could not be decoded, value = " + value[0]);
              throw new ServerApiException(
                  ApiErrorCode.PARAM_ERROR,
                  key + " could not be decoded, received value " + value[0]);
            } catch (IllegalArgumentException iae) {
              s_logger.warn(key + " could not be decoded, value = " + value[0]);
              throw new ServerApiException(
                  ApiErrorCode.PARAM_ERROR,
                  key
                      + " could not be decoded, received value "
                      + value[0]
                      + " which contains illegal characters eg.%");
            }
          } else {
            decodedValue = value[0];
          }
          paramMap.put(key, decodedValue);
        }

        Class<?> cmdClass = getCmdClass(command[0]);
        if (cmdClass != null) {
          BaseCmd cmdObj = (BaseCmd) cmdClass.newInstance();
          cmdObj.setFullUrlParams(paramMap);
          cmdObj.setResponseType(responseType);
          // This is where the command is either serialized, or directly dispatched
          response = queueCommand(cmdObj, paramMap);
          buildAuditTrail(auditTrailSb, command[0], response);
        } else {
          if (!command[0].equalsIgnoreCase("login") && !command[0].equalsIgnoreCase("logout")) {
            String errorString =
                "Unknown API command: " + ((command == null) ? "null" : command[0]);
            s_logger.warn(errorString);
            auditTrailSb.append(" " + errorString);
            throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, errorString);
          }
        }
      }
    } catch (InvalidParameterValueException ex) {
      s_logger.info(ex.getMessage());
      throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage(), ex);
    } catch (IllegalArgumentException ex) {
      s_logger.info(ex.getMessage());
      throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage(), ex);
    } catch (PermissionDeniedException ex) {
      ArrayList<String> idList = ex.getIdProxyList();
      if (idList != null) {
        s_logger.info(
            "PermissionDenied: "
                + ex.getMessage()
                + " on uuids: ["
                + StringUtils.listToCsvTags(idList)
                + "]");
      } else {
        s_logger.info("PermissionDenied: " + ex.getMessage());
      }
      throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, ex.getMessage(), ex);
    } catch (AccountLimitException ex) {
      s_logger.info(ex.getMessage());
      throw new ServerApiException(ApiErrorCode.ACCOUNT_RESOURCE_LIMIT_ERROR, ex.getMessage(), ex);
    } catch (InsufficientCapacityException ex) {
      s_logger.info(ex.getMessage());
      String errorMsg = ex.getMessage();
      if (UserContext.current().getCaller().getType() != Account.ACCOUNT_TYPE_ADMIN) {
        // hide internal details to non-admin user for security reason
        errorMsg = BaseCmd.USER_ERROR_MESSAGE;
      }
      throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, errorMsg, ex);
    } catch (ResourceAllocationException ex) {
      s_logger.info(ex.getMessage());
      String errorMsg = ex.getMessage();
      if (UserContext.current().getCaller().getType() != Account.ACCOUNT_TYPE_ADMIN) {
        // hide internal details to non-admin user for security reason
        errorMsg = BaseCmd.USER_ERROR_MESSAGE;
      }
      throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, errorMsg, ex);
    } catch (ResourceUnavailableException ex) {
      s_logger.info(ex.getMessage());
      String errorMsg = ex.getMessage();
      if (UserContext.current().getCaller().getType() != Account.ACCOUNT_TYPE_ADMIN) {
        // hide internal details to non-admin user for security reason
        errorMsg = BaseCmd.USER_ERROR_MESSAGE;
      }
      throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, errorMsg, ex);
    } catch (AsyncCommandQueued ex) {
      s_logger.error(
          "unhandled exception executing api command: " + ((command == null) ? "null" : command[0]),
          ex);
      throw new ServerApiException(
          ApiErrorCode.INTERNAL_ERROR, "Internal server error, unable to execute request.");
    } catch (ServerApiException ex) {
      s_logger.info(ex.getDescription());
      throw ex;
    } catch (Exception ex) {
      s_logger.error(
          "unhandled exception executing api command: " + ((command == null) ? "null" : command[0]),
          ex);
      String errorMsg = ex.getMessage();
      if (UserContext.current().getCaller().getType() != Account.ACCOUNT_TYPE_ADMIN) {
        // hide internal details to non-admin user for security reason
        errorMsg = BaseCmd.USER_ERROR_MESSAGE;
      }
      throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMsg, ex);
    }

    return response;
  }
  // NOTE: handle() only handles over the wire (OTW) requests from integration.api.port 8096
  // If integration api port is not configured, actual OTW requests will be received by ApiServlet
  @SuppressWarnings({"unchecked", "rawtypes"})
  @Override
  public void handle(HttpRequest request, HttpResponse response, HttpContext context)
      throws HttpException, IOException {

    // Create StringBuffer to log information in access log
    StringBuffer sb = new StringBuffer();
    HttpServerConnection connObj = (HttpServerConnection) context.getAttribute("http.connection");
    if (connObj instanceof SocketHttpServerConnection) {
      InetAddress remoteAddr = ((SocketHttpServerConnection) connObj).getRemoteAddress();
      sb.append(remoteAddr.toString() + " -- ");
    }
    sb.append(StringUtils.cleanString(request.getRequestLine().toString()));

    try {
      List<NameValuePair> paramList = null;
      try {
        paramList = URLEncodedUtils.parse(new URI(request.getRequestLine().getUri()), "UTF-8");
      } catch (URISyntaxException e) {
        s_logger.error("Error parsing url request", e);
      }

      // Use Multimap as the parameter map should be in the form (name=String, value=String[])
      // So parameter values are stored in a list for the same name key
      // APITODO: Use Guava's (import com.google.common.collect.Multimap;)
      // (Immutable)Multimap<String, String> paramMultiMap = HashMultimap.create();
      // Map<String, Collection<String>> parameterMap = paramMultiMap.asMap();
      Map parameterMap = new HashMap<String, String[]>();
      String responseType = BaseCmd.RESPONSE_TYPE_XML;
      for (NameValuePair param : paramList) {
        if (param.getName().equalsIgnoreCase("response")) {
          responseType = param.getValue();
          continue;
        }
        parameterMap.put(param.getName(), new String[] {param.getValue()});
      }

      // Check responseType, if not among valid types, fallback to JSON
      if (!(responseType.equals(BaseCmd.RESPONSE_TYPE_JSON)
          || responseType.equals(BaseCmd.RESPONSE_TYPE_XML)))
        responseType = BaseCmd.RESPONSE_TYPE_XML;

      try {
        // always trust commands from API port, user context will always be
        // UID_SYSTEM/ACCOUNT_ID_SYSTEM
        UserContext.registerContext(_systemUser.getId(), _systemAccount, null, true);
        sb.insert(
            0,
            "(userId="
                + User.UID_SYSTEM
                + " accountId="
                + Account.ACCOUNT_ID_SYSTEM
                + " sessionId="
                + null
                + ") ");
        String responseText = handleRequest(parameterMap, true, responseType, sb);
        sb.append(" 200 " + ((responseText == null) ? 0 : responseText.length()));

        writeResponse(response, responseText, HttpStatus.SC_OK, responseType, null);
      } catch (ServerApiException se) {
        String responseText = getSerializedApiError(se, parameterMap, responseType);
        writeResponse(
            response,
            responseText,
            se.getErrorCode().getHttpCode(),
            responseType,
            se.getDescription());
        sb.append(" " + se.getErrorCode() + " " + se.getDescription());
      } catch (RuntimeException e) {
        // log runtime exception like NullPointerException to help identify the source easier
        s_logger.error("Unhandled exception, ", e);
        throw e;
      }
    } finally {
      s_accessLogger.info(sb.toString());
      UserContext.unregisterContext();
    }
  }