@Test
  public void testFilters() throws Exception {

    int index = 1;
    for (String json : jsonList) {
      MvcResult result = ControllerUtil.performRouterRequest(this.mockMvc, json);
      List<ExtDirectResponse> responses =
          ControllerUtil.readDirectResponses(result.getResponse().getContentAsByteArray());

      assertThat(responses).hasSize(1);
      ExtDirectResponse resp = responses.get(0);
      assertThat(resp.getAction()).isEqualTo("remoteProviderStoreRead");
      assertThat(resp.getMethod()).isEqualTo("methodFilter");
      assertThat(resp.getType()).isEqualTo("rpc");
      assertThat(resp.getTid()).isEqualTo(index);
      assertThat(resp.getMessage()).isNull();
      assertThat(resp.getWhere()).isNull();
      assertThat(resp.getResult()).isNotNull();

      List<Row> rows =
          ControllerUtil.convertValue(
              resp.getResult(),
              new TypeReference<List<Row>>() {
                // nothing here
              });

      assertThat(rows).hasSize(1);
      assertThat(rows.get(0).getId()).isEqualTo(index);

      index++;
    }

    assertThat(index).isEqualTo(34);
  }
  private void handleMethodCallsSequential(
      List<ExtDirectRequest> directRequests,
      HttpServletRequest request,
      HttpServletResponse response,
      Locale locale)
      throws JsonGenerationException, JsonMappingException, IOException {
    List<ExtDirectResponse> directResponses =
        new ArrayList<ExtDirectResponse>(directRequests.size());
    boolean streamResponse = configurationService.getConfiguration().isStreamResponse();
    Class<?> jsonView = null;

    for (ExtDirectRequest directRequest : directRequests) {
      ExtDirectResponse directResponse = handleMethodCall(directRequest, request, response, locale);
      streamResponse = streamResponse || directResponse.isStreamResponse();
      jsonView = directResponse.getJsonView();
      directResponses.add(directResponse);
    }

    writeJsonResponse(response, directResponses, jsonView, streamResponse);
  }
  private void handleMethodCallsConcurrent(
      List<ExtDirectRequest> directRequests,
      HttpServletRequest request,
      HttpServletResponse response,
      Locale locale)
      throws JsonGenerationException, JsonMappingException, IOException {

    Class<?> jsonView = null;

    List<Future<ExtDirectResponse>> futures =
        new ArrayList<Future<ExtDirectResponse>>(directRequests.size());
    for (ExtDirectRequest directRequest : directRequests) {
      Callable<ExtDirectResponse> callable =
          createMethodCallCallable(directRequest, request, response, locale);
      futures.add(
          configurationService
              .getConfiguration()
              .getBatchedMethodsExecutorService()
              .submit(callable));
    }

    List<ExtDirectResponse> directResponses =
        new ArrayList<ExtDirectResponse>(directRequests.size());
    boolean streamResponse = configurationService.getConfiguration().isStreamResponse();
    for (Future<ExtDirectResponse> future : futures) {
      try {
        ExtDirectResponse directResponse = future.get();
        streamResponse = streamResponse || directResponse.isStreamResponse();
        jsonView = directResponse.getJsonView();
        directResponses.add(directResponse);
      } catch (InterruptedException e) {
        log.error("Error invoking method", e);
      } catch (ExecutionException e) {
        log.error("Error invoking method", e);
      }
    }
    writeJsonResponse(response, directResponses, jsonView, streamResponse);
  }
  @SuppressWarnings({"rawtypes", "unchecked"})
  ExtDirectResponse handleMethodCall(
      ExtDirectRequest directRequest,
      HttpServletRequest request,
      HttpServletResponse response,
      Locale locale) {
    ExtDirectResponse directResponse = new ExtDirectResponse(directRequest);

    MethodInfo methodInfo =
        MethodInfoCache.INSTANCE.get(directRequest.getAction(), directRequest.getMethod());

    if (methodInfo != null) {

      try {
        directResponse.setStreamResponse(methodInfo.isStreamResponse());
        Object result =
            processRemotingRequest(request, response, locale, directRequest, methodInfo);

        if (result != null) {

          ModelAndJsonView modelAndJsonView = null;
          if (result instanceof ModelAndJsonView) {
            modelAndJsonView = (ModelAndJsonView) result;
            result = modelAndJsonView.getModel();
          }

          if (methodInfo.isType(ExtDirectMethodType.FORM_LOAD)
              && !ExtDirectFormLoadResult.class.isAssignableFrom(result.getClass())) {
            ExtDirectFormLoadResult formLoadResult = new ExtDirectFormLoadResult(result);
            if (result instanceof JsonViewHint) {
              formLoadResult.setJsonView(((JsonViewHint) result).getJsonView());
            }
            result = formLoadResult;
          } else if ((methodInfo.isType(ExtDirectMethodType.STORE_MODIFY)
                  || methodInfo.isType(ExtDirectMethodType.STORE_READ))
              && !ExtDirectStoreReadResult.class.isAssignableFrom(result.getClass())
              && !ExtDirectStoreResult.class.isAssignableFrom(result.getClass())
              && configurationService.getConfiguration().isAlwaysWrapStoreResponse()) {
            if (result instanceof Collection) {
              result = new ExtDirectStoreResult((Collection) result);
            } else {
              result = new ExtDirectStoreResult(result);
            }
          }

          directResponse.setResult(result);
          if (modelAndJsonView != null) {
            directResponse.setJsonView(getJsonView(modelAndJsonView, methodInfo.getJsonView()));
          } else {
            directResponse.setJsonView(getJsonView(result, methodInfo.getJsonView()));
          }

        } else {
          if (methodInfo.isType(ExtDirectMethodType.STORE_MODIFY)
              || methodInfo.isType(ExtDirectMethodType.STORE_READ)) {
            directResponse.setResult(Collections.emptyList());
          }
        }

      } catch (Exception e) {
        log.error(
            "Error calling method: " + directRequest.getMethod(),
            e.getCause() != null ? e.getCause() : e);
        directResponse.setResult(handleException(methodInfo, directResponse, e, request));
      }
    } else {
      log.error(
          "Error invoking method '"
              + directRequest.getAction()
              + "."
              + directRequest.getMethod()
              + "'. Method or Bean not found");
      handleMethodNotFoundError(
          directResponse, directRequest.getAction(), directRequest.getMethod());
    }

    return directResponse;
  }
  @RequestMapping(value = "/router", method = RequestMethod.POST, params = "extAction")
  public String router(
      HttpServletRequest request,
      HttpServletResponse response,
      @RequestParam("extAction") String extAction,
      @RequestParam("extMethod") String extMethod)
      throws IOException {

    ExtDirectResponse directResponse = new ExtDirectResponse(request);
    MethodInfo methodInfo = MethodInfoCache.INSTANCE.get(extAction, extMethod);
    Class<?> jsonView = null;
    boolean streamResponse;

    if (methodInfo != null && methodInfo.getForwardPath() != null) {
      return methodInfo.getForwardPath();
    } else if (methodInfo != null && methodInfo.getHandlerMethod() != null) {
      streamResponse =
          configurationService.getConfiguration().isStreamResponse()
              || methodInfo.isStreamResponse();

      HandlerMethod handlerMethod = methodInfo.getHandlerMethod();
      try {

        ModelAndView modelAndView = null;

        if (configurationService.getConfiguration().isSynchronizeOnSession()
            || methodInfo.isSynchronizeOnSession()) {
          HttpSession session = request.getSession(false);
          if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
              modelAndView = handlerAdapter.handle(request, response, handlerMethod);
            }
          } else {
            modelAndView = handlerAdapter.handle(request, response, handlerMethod);
          }
        } else {
          modelAndView = handlerAdapter.handle(request, response, handlerMethod);
        }

        ExtDirectFormPostResult formPostResult =
            (ExtDirectFormPostResult) modelAndView.getModel().get("extDirectFormPostResult");
        directResponse.setResult(formPostResult.getResult());
        directResponse.setJsonView(getJsonView(formPostResult, methodInfo.getJsonView()));
      } catch (Exception e) {
        log.error("Error calling method: " + extMethod, e.getCause() != null ? e.getCause() : e);
        directResponse.setResult(handleException(methodInfo, directResponse, e, request));
      }
    } else {
      streamResponse = configurationService.getConfiguration().isStreamResponse();
      log.error(
          "Error invoking method '" + extAction + "." + extMethod + "'. Method  or Bean not found");
      handleMethodNotFoundError(directResponse, extAction, extMethod);
    }
    writeJsonResponse(
        response,
        directResponse,
        jsonView,
        streamResponse,
        ExtDirectSpringUtil.isMultipart(request));

    return null;
  }
  public static Object sendAndReceive(
      RouterController controller,
      MockHttpServletRequest request,
      final String action,
      String method,
      boolean namedParameter,
      Object data,
      final Object result) {

    MockHttpServletResponse response = new MockHttpServletResponse();

    int tid = (int) (Math.random() * 1000);
    Map<String, Object> edRequest = createRequestJson(action, method, namedParameter, tid, data);

    request.setContent(ControllerUtil.writeAsByte(edRequest));
    try {
      controller.router(request, response, Locale.ENGLISH);
    } catch (IOException e) {
      fail("call controller.router: " + e.getMessage());
    }
    List<ExtDirectResponse> responses = readDirectResponses(response.getContentAsByteArray());
    assertThat(responses).hasSize(1);

    ExtDirectResponse edResponse = responses.get(0);

    assertThat(edResponse.getAction()).isEqualTo(action);
    assertThat(edResponse.getMethod()).isEqualTo(method);
    assertThat(edResponse.getTid()).isEqualTo(tid);
    assertThat(edResponse.getWhere()).isNull();

    if (result == null) {
      assertThat(edResponse.getType()).isEqualTo("exception");
      assertThat(edResponse.getResult()).isNull();
      assertThat(edResponse.getMessage()).isEqualTo("Server Error");
    } else {
      assertThat(edResponse.getType()).isEqualTo("rpc");
      assertThat(edResponse.getMessage()).isNull();
      if (result == Void.TYPE) {
        assertThat(edResponse.getResult()).isNull();
      } else if (result instanceof Class<?>) {
        Object r = ControllerUtil.convertValue(edResponse.getResult(), (Class<?>) result);
        return r;
      } else if (result instanceof TypeReference) {
        Object r = ControllerUtil.convertValue(edResponse.getResult(), (TypeReference<?>) result);
        return r;
      } else {
        assertThat(edResponse.getResult()).isEqualTo(result);
      }
    }

    return edResponse.getResult();
  }