/**
   * {@inheritDoc}
   *
   * <p>Processes page caching.
   */
  @Override
  protected void afterRender(final HTTPRequestContext context) throws Exception {
    final HttpServletRequest request = context.getRequest();
    final String pageContent = (String) request.getAttribute(PageCaches.CACHED_CONTENT);

    if (null == pageContent) {
      return;
    }

    if (Latkes.isPageCacheEnabled()) {
      final String cachedPageKey = (String) request.getAttribute(Keys.PAGE_CACHE_KEY);
      if (Strings.isEmptyOrNull(cachedPageKey)) {
        return;
      }

      LOGGER.log(Level.FINEST, "Caching page[cachedPageKey={0}]", cachedPageKey);

      check(request, pageContent);

      final JSONObject cachedValue = new JSONObject();
      cachedValue.put(PageCaches.CACHED_CONTENT, pageContent);
      cachedValue.put(PageCaches.CACHED_TYPE, request.getAttribute(PageCaches.CACHED_TYPE));
      cachedValue.put(PageCaches.CACHED_OID, request.getAttribute(PageCaches.CACHED_OID));
      cachedValue.put(PageCaches.CACHED_TITLE, request.getAttribute(PageCaches.CACHED_TITLE));
      cachedValue.put(PageCaches.CACHED_LINK, request.getAttribute(PageCaches.CACHED_LINK));
      if (null != request.getAttribute(PageCaches.CACHED_PWD)) {
        cachedValue.put(PageCaches.CACHED_PWD, request.getAttribute(PageCaches.CACHED_PWD));
      }

      PageCaches.put(cachedPageKey, cachedValue, request);
      LOGGER.log(Level.FINEST, "Cached page[cachedPageKey={0}]", cachedPageKey);
    }
  }
  /**
   * Try to write response from cache.
   *
   * @param request the specified request
   * @param response the specified response
   * @param chain filter chain
   * @throws IOException io exception
   * @throws ServletException servlet exception
   */
  @Override
  public void doFilter(
      final ServletRequest request, final ServletResponse response, final FilterChain chain)
      throws IOException, ServletException {
    final long startTimeMillis = System.currentTimeMillis();
    request.setAttribute(Keys.HttpRequest.START_TIME_MILLIS, startTimeMillis);

    final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    final String requestURI = httpServletRequest.getRequestURI();
    LOGGER.log(Level.FINER, "Request URI[{0}]", requestURI);

    if (StaticResources.isStatic(httpServletRequest)) {
      final String path = httpServletRequest.getServletPath() + httpServletRequest.getPathInfo();
      LOGGER.log(Level.FINEST, "Requests a static resource, forwards to servlet[path={0}]", path);
      request.getRequestDispatcher(path).forward(request, response);

      return;
    }

    if (!Latkes.isPageCacheEnabled()) {
      LOGGER.log(Level.FINEST, "Page cache is disabled");
      chain.doFilter(request, response);

      return;
    }

    final String skinDirName = (String) httpServletRequest.getAttribute(Keys.TEMAPLTE_DIR_NAME);
    if ("mobile".equals(skinDirName)) {
      // Mobile request, bypasses page caching
      chain.doFilter(request, response);

      return;
    }

    String pageCacheKey;
    final String queryString = httpServletRequest.getQueryString();
    pageCacheKey = (String) request.getAttribute(Keys.PAGE_CACHE_KEY);
    if (Strings.isEmptyOrNull(pageCacheKey)) {
      pageCacheKey = PageCaches.getPageCacheKey(requestURI, queryString);
      request.setAttribute(Keys.PAGE_CACHE_KEY, pageCacheKey);
    }

    final JSONObject cachedPageContentObject =
        PageCaches.get(pageCacheKey, httpServletRequest, (HttpServletResponse) response);

    if (null == cachedPageContentObject) {
      LOGGER.log(Level.FINER, "Page cache miss for request URI[{0}]", requestURI);
      chain.doFilter(request, response);

      return;
    }

    final String cachedType = cachedPageContentObject.optString(PageCaches.CACHED_TYPE);

    try {
      // If cached an article that has view password, dispatches the password form
      if (langPropsService.get(PageTypes.ARTICLE.getLangeLabel()).equals(cachedType)
          && cachedPageContentObject.has(PageCaches.CACHED_PWD)) {
        JSONObject article = new JSONObject();

        final String articleId = cachedPageContentObject.optString(PageCaches.CACHED_OID);

        article.put(Keys.OBJECT_ID, articleId);
        article.put(
            Article.ARTICLE_VIEW_PWD, cachedPageContentObject.optString(PageCaches.CACHED_PWD));

        if (articles.needViewPwd(httpServletRequest, article)) {
          article = articleRepository.get(articleId); // Loads the article entity

          final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
          try {
            httpServletResponse.sendRedirect(
                Latkes.getServePath()
                    + "/console/article-pwd"
                    + articles.buildArticleViewPwdFormParameters(article));
            return;
          } catch (final Exception e) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
          }
        }
      }
    } catch (final Exception e) {
      LOGGER.log(Level.SEVERE, e.getMessage(), e);
      chain.doFilter(request, response);
    }

    try {
      LOGGER.log(
          Level.FINEST, "Writes resposne for page[pageCacheKey={0}] from cache", pageCacheKey);
      response.setContentType("text/html");
      response.setCharacterEncoding("UTF-8");
      final PrintWriter writer = response.getWriter();
      String cachedPageContent = cachedPageContentObject.getString(PageCaches.CACHED_CONTENT);
      final String topBarHTML =
          TopBars.getTopBarHTML((HttpServletRequest) request, (HttpServletResponse) response);
      cachedPageContent = cachedPageContent.replace(Common.TOP_BAR_REPLACEMENT_FLAG, topBarHTML);

      final String cachedTitle = cachedPageContentObject.getString(PageCaches.CACHED_TITLE);
      LOGGER.log(
          Level.FINEST,
          "Cached value[key={0}, type={1}, title={2}]",
          new Object[] {pageCacheKey, cachedType, cachedTitle});

      statistics.incBlogViewCount((HttpServletRequest) request, (HttpServletResponse) response);

      final long endimeMillis = System.currentTimeMillis();
      final String dateString = DateFormatUtils.format(endimeMillis, "yyyy/MM/dd HH:mm:ss");
      final String msg =
          String.format(
              "<!-- Cached by B3log Solo(%1$d ms), %2$s -->",
              endimeMillis - startTimeMillis, dateString);
      LOGGER.finer(msg);
      cachedPageContent += Strings.LINE_SEPARATOR + msg;
      writer.write(cachedPageContent);
      writer.flush();
      writer.close();
    } catch (final JSONException e) {
      LOGGER.log(Level.SEVERE, e.getMessage(), e);
      chain.doFilter(request, response);
    } catch (final RepositoryException e) {
      LOGGER.log(Level.SEVERE, e.getMessage(), e);
      chain.doFilter(request, response);
    } catch (final ServiceException e) {
      LOGGER.log(Level.SEVERE, e.getMessage(), e);
      chain.doFilter(request, response);
    }
  }