public PageCache getPageCache(final long pageId) {
    try {
      boolean needToRead = false;
      PageCache cache = null;
      synchronized (softCache) {
        if (pageId > pagingStore.getCurrentWritingPage()) {
          return null;
        }

        cache = softCache.get(pageId);
        if (cache == null) {
          if (!pagingStore.checkPageFileExists((int) pageId)) {
            return null;
          }

          cache = createPageCache(pageId);
          needToRead = true;
          // anyone reading from this cache will have to wait reading to finish first
          // we also want only one thread reading this cache
          cache.lock();
          if (isTrace) {
            log.trace("adding " + pageId + " into cursor = " + this.pagingStore.getAddress());
          }
          softCache.put(pageId, cache);
        }
      }

      // Reading is done outside of the synchronized block, however
      // the page stays locked until the entire reading is finished
      if (needToRead) {
        Page page = null;
        try {
          page = pagingStore.createPage((int) pageId);

          storageManager.beforePageRead();
          page.open();

          List<PagedMessage> pgdMessages = page.read(storageManager);
          cache.setMessages(pgdMessages.toArray(new PagedMessage[pgdMessages.size()]));
        } finally {
          try {
            if (page != null) {
              page.close();
            }
          } catch (Throwable ignored) {
          }
          storageManager.afterPageRead();
          cache.unlock();
        }
      }

      return cache;
    } catch (Exception e) {
      throw new RuntimeException(
          "Couldn't complete paging due to an IO Exception on Paging - " + e.getMessage(), e);
    }
  }
  /**
   * @param cursorList
   * @param currentPage
   * @throws Exception
   */
  private void storePositions(ArrayList<PageSubscription> cursorList, Page currentPage)
      throws Exception {
    try {
      // First step: Move every cursor to the next bookmarked page (that was just created)
      for (PageSubscription cursor : cursorList) {
        cursor.confirmPosition(new PagePositionImpl(currentPage.getPageId(), -1));
      }

      while (!storageManager.waitOnOperations(5000)) {
        log.warn("Couldn't complete operations on IO context " + storageManager.getContext());
      }
    } finally {
      for (PageSubscription cursor : cursorList) {
        cursor.enableAutoCleanup();
      }
    }
  }
  public void cleanup() {
    ArrayList<Page> depagedPages = new ArrayList<Page>();

    while (true) {
      if (pagingStore.lock(100)) {
        break;
      }
      if (!pagingStore.isStarted()) return;
    }

    synchronized (this) {
      try {
        if (!pagingStore.isStarted()) {
          return;
        }

        if (pagingStore.getNumberOfPages() == 0) {
          return;
        }

        if (log.isDebugEnabled()) {
          log.debug("Asserting cleanup for address " + this.pagingStore.getAddress());
        }

        ArrayList<PageSubscription> cursorList = new ArrayList<PageSubscription>();
        cursorList.addAll(activeCursors.values());

        long minPage = checkMinPage(cursorList);

        if (minPage == pagingStore.getCurrentWritingPage()
            && pagingStore.getCurrentPage().getNumberOfMessages() > 0) {
          boolean complete = true;

          for (PageSubscription cursor : cursorList) {
            if (!cursor.isComplete(minPage)) {
              if (log.isDebugEnabled()) {
                log.debug("Cursor " + cursor + " was considered incomplete at page " + minPage);
              }

              complete = false;
              break;
            } else {
              if (log.isDebugEnabled()) {
                log.debug("Cursor " + cursor + "was considered **complete** at page " + minPage);
              }
            }
          }

          if (!pagingStore.isStarted()) {
            return;
          }

          if (complete) {

            if (log.isDebugEnabled()) {
              log.debug(
                  "Address "
                      + pagingStore.getAddress()
                      + " is leaving page mode as all messages are consumed and acknowledged from the page store");
            }

            pagingStore.forceAnotherPage();

            Page currentPage = pagingStore.getCurrentPage();

            storePositions(cursorList, currentPage);

            pagingStore.stopPaging();

            // This has to be called after we stopped paging
            for (PageSubscription cursor : cursorList) {
              cursor.scheduleCleanupCheck();
            }
          }
        }

        for (long i = pagingStore.getFirstPage(); i < minPage; i++) {
          Page page = pagingStore.depage();
          if (page == null) {
            break;
          }
          depagedPages.add(page);
        }

        if (pagingStore.getNumberOfPages() == 0
            || pagingStore.getNumberOfPages() == 1
                && pagingStore.getCurrentPage().getNumberOfMessages() == 0) {
          pagingStore.stopPaging();
        } else {
          if (log.isTraceEnabled()) {
            log.trace(
                "Couldn't cleanup page on address "
                    + this.pagingStore.getAddress()
                    + " as numberOfPages == "
                    + pagingStore.getNumberOfPages()
                    + " and currentPage.numberOfMessages = "
                    + pagingStore.getCurrentPage().getNumberOfMessages());
          }
        }
      } catch (Exception ex) {
        log.warn("Couldn't complete cleanup on paging", ex);
        return;
      } finally {
        pagingStore.unlock();
      }
    }

    try {
      for (Page depagedPage : depagedPages) {
        PageCache cache;
        PagedMessage[] pgdMessages;
        synchronized (softCache) {
          cache = softCache.get((long) depagedPage.getPageId());
        }

        if (isTrace) {
          log.trace("Removing page " + depagedPage.getPageId() + " from page-cache");
        }

        if (cache == null) {
          // The page is not on cache any more
          // We need to read the page-file before deleting it
          // to make sure we remove any large-messages pending
          storageManager.beforePageRead();

          List<PagedMessage> pgdMessagesList = null;
          try {
            depagedPage.open();
            pgdMessagesList = depagedPage.read(storageManager);
          } finally {
            try {
              depagedPage.close();
            } catch (Exception e) {
            }

            storageManager.afterPageRead();
          }
          depagedPage.close();
          pgdMessages = pgdMessagesList.toArray(new PagedMessage[pgdMessagesList.size()]);
        } else {
          pgdMessages = cache.getMessages();
        }

        depagedPage.delete(pgdMessages);

        synchronized (softCache) {
          softCache.remove((long) depagedPage.getPageId());
        }
      }
    } catch (Exception ex) {
      log.warn("Couldn't complete cleanup on paging", ex);
      return;
    }
  }