/* (non-Javadoc)
   * @see org.springframework.web.servlet.DispatcherServlet#doDispatch(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
   */
  protected void doDispatch(final HttpServletRequest request, HttpServletResponse response)
      throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    int interceptorIndex = -1;
    final LocaleResolver localeResolver =
        (LocaleResolver) request.getAttribute(LOCALE_RESOLVER_ATTRIBUTE);

    // Expose current LocaleResolver and request as LocaleContext.
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContextHolder.setLocaleContext(
        new LocaleContext() {
          public Locale getLocale() {

            return localeResolver.resolveLocale(request);
          }
        });

    // If the request is an include we need to try to use the original wrapped sitemesh
    // response, otherwise layouts won't work properly
    if (WebUtils.isIncludeRequest(request)) {
      response = useWrappedOrOriginalResponse(response);
    }

    GrailsWebRequest requestAttributes = null;
    GrailsWebRequest previousRequestAttributes = null;
    Exception handlerException = null;
    try {
      ModelAndView mv = null;
      try {
        Object exceptionAttribute = request.getAttribute(EXCEPTION_ATTRIBUTE);
        // only process multipart requests if an exception hasn't occured
        if (exceptionAttribute == null) processedRequest = checkMultipart(request);
        // Expose current RequestAttributes to current thread.
        previousRequestAttributes =
            (GrailsWebRequest) RequestContextHolder.currentRequestAttributes();
        requestAttributes = new GrailsWebRequest(processedRequest, response, getServletContext());
        copyParamsFromPreviousRequest(previousRequestAttributes, requestAttributes);

        // Update the current web request.
        WebUtils.storeGrailsWebRequest(requestAttributes);

        if (logger.isDebugEnabled()) {
          logger.debug("Bound request context to thread: " + request);
          logger.debug("Using response object: " + response.getClass());
        }

        // Determine handler for the current request.
        mappedHandler = getHandler(processedRequest, false);
        if (mappedHandler == null || mappedHandler.getHandler() == null) {
          noHandlerFound(processedRequest, response);
          return;
        }

        // Apply preHandle methods of registered interceptors.
        if (mappedHandler.getInterceptors() != null) {
          for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {
            HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
            if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
              triggerAfterCompletion(
                  mappedHandler, interceptorIndex, processedRequest, response, null);
              return;
            }
            interceptorIndex = i;
          }
        }

        // Actually invoke the handler.
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        // Apply postHandle methods of registered interceptors.
        if (mappedHandler.getInterceptors() != null) {
          for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
            interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
          }
        }
      } catch (ModelAndViewDefiningException ex) {
        GrailsUtil.deepSanitize(ex);
        handlerException = ex;
        if (logger.isDebugEnabled()) logger.debug("ModelAndViewDefiningException encountered", ex);
        mv = ex.getModelAndView();
      } catch (Exception ex) {
        GrailsUtil.deepSanitize(ex);
        handlerException = ex;
        Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
        mv = processHandlerException(request, response, handler, ex);
      }

      // Did the handler return a view to render?
      if (mv != null && !mv.wasCleared()) {
        // If an exception occurs in here, like a bad closing tag,
        // we have nothing to render.

        try {
          render(mv, processedRequest, response);
        } catch (Exception e) {
          mv = super.processHandlerException(processedRequest, response, mappedHandler, e);
          handlerException = e;
          render(mv, processedRequest, response);
        }
      } else {
        if (logger.isDebugEnabled()) {
          logger.debug(
              "Null ModelAndView returned to DispatcherServlet with name '"
                  + getServletName()
                  + "': assuming HandlerAdapter completed request handling");
        }
      }

      // Trigger after-completion for successful outcome.
      triggerAfterCompletion(
          mappedHandler, interceptorIndex, processedRequest, response, handlerException);
    } catch (Exception ex) {
      // Trigger after-completion for thrown exception.
      triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
      throw ex;
    } catch (Error err) {
      ServletException ex = new NestedServletException("Handler processing failed", err);
      // Trigger after-completion for thrown exception.
      triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
      throw ex;
    } finally {
      // Clean up any resources used by a multipart request.
      if (processedRequest instanceof MultipartHttpServletRequest && processedRequest != request) {
        if (multipartResolver != null)
          this.multipartResolver.cleanupMultipart((MultipartHttpServletRequest) processedRequest);
      }
      request.removeAttribute(MultipartHttpServletRequest.class.getName());

      // Reset thread-bound holders
      if (requestAttributes != null) {
        requestAttributes.requestCompleted();
        WebUtils.storeGrailsWebRequest(previousRequestAttributes);
      }

      LocaleContextHolder.setLocaleContext(previousLocaleContext);

      if (logger.isDebugEnabled()) {
        logger.debug("Cleared thread-bound request context: " + request);
      }
    }
  }
  /* (non-Javadoc)
   * @see org.springframework.web.servlet.DispatcherServlet#doDispatch(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
   */
  @Override
  protected void doDispatch(final HttpServletRequest request, HttpServletResponse response)
      throws Exception {

    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, localeResolver);

    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    int interceptorIndex = -1;

    // Expose current LocaleResolver and request as LocaleContext.
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContextHolder.setLocaleContext(
        new LocaleContext() {
          public Locale getLocale() {
            return localeResolver.resolveLocale(request);
          }
        });

    // If the request is an include we need to try to use the original wrapped sitemesh
    // response, otherwise layouts won't work properly
    if (WebUtils.isIncludeRequest(request)) {
      response = useWrappedOrOriginalResponse(response);
    }

    GrailsWebRequest requestAttributes = null;
    RequestAttributes previousRequestAttributes = null;
    Exception handlerException = null;
    boolean isAsyncRequest =
        processedRequest.getAttribute("javax.servlet.async.request_uri") != null;
    try {
      ModelAndView mv;
      boolean errorView = false;
      try {
        Object exceptionAttribute = request.getAttribute(EXCEPTION_ATTRIBUTE);
        // only process multipart requests if an exception hasn't occured
        if (exceptionAttribute == null) {
          processedRequest = checkMultipart(request);
        }
        // Expose current RequestAttributes to current thread.
        previousRequestAttributes = RequestContextHolder.currentRequestAttributes();
        requestAttributes = new GrailsWebRequest(processedRequest, response, getServletContext());
        copyParamsFromPreviousRequest(previousRequestAttributes, requestAttributes);

        // Update the current web request.
        WebUtils.storeGrailsWebRequest(requestAttributes);

        if (logger.isDebugEnabled()) {
          logger.debug("Bound request context to thread: " + request);
          logger.debug("Using response object: " + response.getClass());
        }

        // Determine handler for the current request.
        mappedHandler = getHandler(processedRequest);
        if (mappedHandler == null || mappedHandler.getHandler() == null) {
          noHandlerFound(processedRequest, response);
          return;
        }

        // Apply preHandle methods of registered interceptors.
        if (mappedHandler.getInterceptors() != null) {
          for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {
            HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
            if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
              triggerAfterCompletion(
                  mappedHandler, interceptorIndex, processedRequest, response, null);
              return;
            }
            interceptorIndex = i;
          }
        }

        // if this is an async request that has been resumed, then don't execute the action again
        // instead try get the model and view and continue

        if (isAsyncRequest) {
          Object modelAndViewO =
              processedRequest.getAttribute(GrailsApplicationAttributes.MODEL_AND_VIEW);
          if (modelAndViewO != null) {
            mv = (ModelAndView) modelAndViewO;
          } else {
            mv = null;
          }

        } else {
          // Actually invoke the handler.
          HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
          mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
          // if an async request was started simply return
          if (processedRequest.getAttribute(GrailsApplicationAttributes.ASYNC_STARTED) != null) {
            processedRequest.setAttribute(GrailsApplicationAttributes.MODEL_AND_VIEW, mv);
            return;
          }

          // Do we need view name translation?
          if ((ha instanceof AnnotationMethodHandlerAdapter
                  || ha instanceof RequestMappingHandlerAdapter)
              && mv != null
              && !mv.hasView()) {
            mv.setViewName(getDefaultViewName(request));
          }
        }

        // Apply postHandle methods of registered interceptors.
        if (mappedHandler.getInterceptors() != null) {
          for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
            interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
          }
        }
      } catch (ModelAndViewDefiningException ex) {
        handlerException = ex;
        if (logger.isDebugEnabled()) {
          logger.debug("ModelAndViewDefiningException encountered", ex);
        }
        mv = ex.getModelAndView();
      } catch (Exception ex) {
        handlerException = ex;
        Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
        mv = processHandlerException(request, response, handler, ex);
        errorView = (mv != null);
      }

      // Did the handler return a view to render?
      if (mv != null && !mv.wasCleared()) {
        // If an exception occurs in here, like a bad closing tag,
        // we have nothing to render.

        try {
          render(mv, processedRequest, response);
          if (isAsyncRequest && (response instanceof GrailsContentBufferingResponse)) {
            GroovyPageLayoutFinder groovyPageLayoutFinder =
                getWebApplicationContext()
                    .getBean("groovyPageLayoutFinder", GroovyPageLayoutFinder.class);
            GrailsContentBufferingResponse bufferingResponse =
                (GrailsContentBufferingResponse) response;
            HttpServletResponse targetResponse = bufferingResponse.getTargetResponse();
            Content content = bufferingResponse.getContent();
            if (content != null) {
              Decorator decorator = groovyPageLayoutFinder.findLayout(request, content);
              SiteMeshWebAppContext webAppContext =
                  new SiteMeshWebAppContext(request, targetResponse, getServletContext());
              if (decorator != null) {
                if (decorator instanceof com.opensymphony.sitemesh.Decorator) {
                  ((com.opensymphony.sitemesh.Decorator) decorator).render(content, webAppContext);
                } else {
                  new OldDecorator2NewDecorator(decorator).render(content, webAppContext);
                }
              } else {
                content.writeOriginal(targetResponse.getWriter());
              }
            }
          }
          if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
          }
        } catch (Exception e) {
          // Only render the error view if we're not already trying to render it.
          // This prevents a recursion if the error page itself has errors.
          if (request.getAttribute(GrailsApplicationAttributes.RENDERING_ERROR_ATTRIBUTE) == null) {
            request.setAttribute(
                GrailsApplicationAttributes.RENDERING_ERROR_ATTRIBUTE, Boolean.TRUE);

            mv = super.processHandlerException(processedRequest, response, mappedHandler, e);
            handlerException = e;
            if (mv != null) render(mv, processedRequest, response);
          } else {
            request.removeAttribute(GrailsApplicationAttributes.RENDERING_ERROR_ATTRIBUTE);
            logger.warn("Recursive rendering of error view detected.", e);

            try {
              response.setContentType("text/plain");
              response.getWriter().write("Internal server error");
              response.flushBuffer();
            } catch (Exception e2) {
              logger.error("Internal server error - problem rendering error view", e2);
            }

            requestAttributes.setRenderView(false);
            return;
          }
        }
      } else {
        if (logger.isDebugEnabled()) {
          logger.debug(
              "Null ModelAndView returned to DispatcherServlet with name '"
                  + getServletName()
                  + "': assuming HandlerAdapter completed request handling");
        }
      }

      // Trigger after-completion for successful outcome.
      triggerAfterCompletion(
          mappedHandler, interceptorIndex, processedRequest, response, handlerException);
    } catch (Exception ex) {
      // Trigger after-completion for thrown exception.
      triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
      throw ex;
    } catch (Error err) {
      ServletException ex = new NestedServletException("Handler processing failed", err);
      // Trigger after-completion for thrown exception.
      triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
      throw ex;
    } finally {
      // Clean up any resources used by a multipart request.
      if (processedRequest instanceof MultipartHttpServletRequest) {
        if (multipartResolver != null) {
          multipartResolver.cleanupMultipart((MultipartHttpServletRequest) processedRequest);
        }
      }
      request.removeAttribute(MultipartHttpServletRequest.class.getName());

      // Reset thread-bound holders
      if (requestAttributes != null) {
        requestAttributes.requestCompleted();
        if (previousRequestAttributes instanceof GrailsWebRequest) {
          WebUtils.storeGrailsWebRequest((GrailsWebRequest) previousRequestAttributes);
        } else {
          RequestContextHolder.setRequestAttributes(previousRequestAttributes);
        }
      }

      LocaleContextHolder.setLocaleContext(previousLocaleContext);

      if (logger.isDebugEnabled()) {
        logger.debug("Cleared thread-bound request context: " + request);
      }
    }
  }