protected PageInfo buildCachedPageInfo(
      HttpServletRequest request, HttpServletResponse response, CacheStatus cacheStatus)
      throws Exception {

    Timer timer = new Timer(getCachedUri(request));
    timer.start();

    String key = calculateKey(request);
    PageInfo pageInfo;
    ValueWrapper element = cacheStatus.valueWrapper;
    log.debug("Serving cached content for {}", key);
    pageInfo = (PageInfo) element.get();

    for (Map.Entry<String, ? extends Serializable> entry :
        pageInfo.getRequestAttributes().entrySet()) {
      request.setAttribute(entry.getKey(), entry.getValue());
    }

    // As the page is cached, we need to add an instance of the associated
    // controller to the request. This is required by GrailsLayoutDecoratorMapper
    // to pick the appropriate layout.
    if (StringUtils.hasLength(getContext().getControllerName())) {
      Object controller = lookupController(getContext().getControllerClass());
      request.setAttribute(GrailsApplicationAttributes.CONTROLLER, controller);
    }
    timer.stop(true);
    response.addHeader(X_CACHED, String.valueOf(true));
    return pageInfo;
  }
  protected PageInfo buildNewPageInfo(
      HttpServletRequest request,
      HttpServletResponse response,
      FilterChain chain,
      CacheStatus cacheStatus,
      Map<String, Collection<CacheOperationContext>> operationsByType)
      throws Exception {

    Timer timer = new Timer(getCachedUri(request));
    timer.start();

    String key = calculateKey(request);
    PageInfo pageInfo;
    try {
      // Page is not cached - build the response, cache it, and send to client
      pageInfo = buildPage(request, response, chain);
      if (pageInfo.isOk()) {
        Object noCache = pageInfo.getCacheDirectives().get("no-cache");
        if (noCache instanceof Boolean && ((Boolean) noCache)) {
          log.debug("Response ok but Cache-Control: no-cache is present, not caching");
          releaseCacheLocks(operationsByType, key);
        } else {
          Collection<Cache> caches = new ArrayList<Cache>();
          for (CacheOperationContext operationContext : operationsByType.get(UPDATE)) {
            for (Cache cache : operationContext.getCaches()) {
              caches.add(cache);
            }
          }
          update(caches, pageInfo, cacheStatus, key);
        }
      } else {
        for (CacheOperationContext operationContext : operationsByType.get(UPDATE)) {
          for (Cache cache : operationContext.getCaches()) {
            log.debug(
                "Response not ok ({}). Putting null into cache {} with key {}",
                pageInfo.getStatusCode(),
                cache.getName(),
                key);
          }
        }
        releaseCacheLocks(operationsByType, key);
      }
    } catch (Exception e) {
      if ("net.sf.ehcache.constructs.blocking.LockTimeoutException"
          .equals(e.getClass().getName())) {
        // do not release the lock, because you never acquired it
        throw e;
      }
      // Must unlock the cache if the above fails. Will be logged at Filter
      releaseCacheLocks(operationsByType, key);
      throw e;
    }

    timer.stop(false);
    response.addHeader(X_CACHED, String.valueOf(false));
    return pageInfo;
  }