public static void process(
      ActionRequest actionRequest,
      ActionResponse actionResponse,
      Map<String, BaseAlloyControllerImpl> alloyControllers)
      throws Exception {

    String jsonString = null;

    String controller = ParamUtil.getString(actionRequest, "controller");

    BaseAlloyControllerImpl baseAlloyControllerImpl = alloyControllers.get(controller);

    if (baseAlloyControllerImpl == null) {
      throw new Exception("Unable to find controller " + controller);
    }

    String action = ParamUtil.getString(actionRequest, "action");

    try {
      if (action.equals("custom")) {
        Class<?> clazz = BaseAlloyControllerImpl.class;

        Method method =
            clazz.getDeclaredMethod("processDataRequest", new Class<?>[] {ActionRequest.class});

        jsonString =
            (String)
                ServiceBeanMethodInvocationFactoryUtil.proceed(
                    baseAlloyControllerImpl,
                    clazz,
                    method,
                    new Object[] {actionRequest},
                    new String[] {"transactionAdvice"});
      } else if (action.equals("dynamicQuery")) {
        jsonString = executeDynamicQuery(baseAlloyControllerImpl, actionRequest);
      } else if (action.equals("search")) {
        jsonString = executeSearch(baseAlloyControllerImpl, actionRequest);
      }
    } catch (Exception e) {
      JSONObject jsonObject = JSONFactoryUtil.createJSONObject();

      String message = e.getMessage();

      if (Validator.isNull(message)) {
        message = baseAlloyControllerImpl.translate("an-unexpected-error-occurred");
      }

      jsonObject.put("message", message);

      jsonObject.put("stacktrace", StackTraceUtil.getStackTrace(e));
      jsonObject.put("success", false);

      jsonString = jsonObject.toString();
    }

    if (jsonString != null) {
      writeJSON(actionRequest, actionResponse, jsonString);
    }
  }
  protected static String executeSearch(
      BaseAlloyControllerImpl baseAlloyControllerImpl, ActionRequest actionRequest)
      throws Exception {

    if (baseAlloyControllerImpl.permissioned) {
      AlloyPermission.check(
          baseAlloyControllerImpl.themeDisplay, baseAlloyControllerImpl.controllerPath, "index");
    }

    Map<String, Serializable> attributes = null;

    String attributesString = ParamUtil.getString(actionRequest, "attributes");

    if (Validator.isNotNull(attributesString)) {
      attributes = JSONFactoryUtil.looseDeserialize(attributesString, HashMap.class);
    }

    String keywords = ParamUtil.getString(actionRequest, "keywords");

    Sort[] sorts = null;

    String sortsString = ParamUtil.getString(actionRequest, "sorts");

    if (Validator.isNotNull(sortsString)) {
      Map<String, Boolean> sortsMap =
          JSONFactoryUtil.looseDeserialize(sortsString, LinkedHashMap.class);

      sorts = new Sort[sortsMap.size()];

      int i = 0;

      for (Map.Entry<String, Boolean> entry : sortsMap.entrySet()) {
        sorts[i++] = new Sort(entry.getKey(), entry.getValue());
      }
    }

    AlloySearchResult alloySearchResult =
        baseAlloyControllerImpl.search(
            PortalUtil.getHttpServletRequest(actionRequest),
            actionRequest,
            attributes,
            keywords,
            sorts);

    List<BaseModel<?>> baseModels = alloySearchResult.getBaseModels();

    return JSONFactoryUtil.looseSerialize(baseModels);
  }