public ZLDBackendState getZLDBackendState(SwordBookMetaData metadata) throws BookException {
    ensureNotShuttingDown();

    ZLDBackendState state = getInstance(metadata);
    if (state == null) {
      LOGGER.trace("Initializing: {}", metadata.getInitials());
      return new ZLDBackendState(metadata);
    }

    LOGGER.trace("Reusing: {}", metadata.getInitials());
    return state;
  }
  /*
   * (non-Javadoc)
   *
   * @see
   * org.crosswire.jsword.book.install.Installer#isNewer(org.crosswire.jsword
   * .book.BookMetaData)
   */
  public boolean isNewer(Book book) {
    File dldir = SwordBookPath.getSwordDownloadDir();

    SwordBookMetaData sbmd = (SwordBookMetaData) book.getBookMetaData();
    File conf = new File(dldir, sbmd.getConfPath());

    // The conf may not exist in our download dir.
    // In this case we say that it should not be downloaded again.
    if (!conf.exists()) {
      return false;
    }

    URI configURI = NetUtil.getURI(conf);

    URI remote = toRemoteURI(book);
    return NetUtil.isNewer(remote, configURI, proxyHost, proxyPort);
  }
  public void release(OpenFileState fileState) {
    if (fileState == null) {
      // can't release anything. JSword has failed to open a file state,
      // and a finally block is trying to close this
      return;
    }

    fileState.setLastAccess(System.currentTimeMillis());

    // instead of releasing, we add to our queue
    SwordBookMetaData bmd = fileState.getBookMetaData();
    Queue<OpenFileState> queueForMeta = getQueueForMeta(bmd);
    LOGGER.trace("Offering to releasing: {}", bmd.getInitials());
    boolean offered = queueForMeta.offer(fileState);

    // ignore if we couldn't offer to the queue
    if (!offered) {
      LOGGER.trace("Released: {}", bmd.getInitials());
      fileState.releaseResources();
    }
  }
  /** Load the cached index file into memory */
  private void loadCachedIndex() throws InstallException {
    // We need a sword book driver so the installer can use the driver
    // name to use in deciding where to put the index.
    BookDriver fake = SwordBookDriver.instance();

    entries.clear();

    URI cache = getCachedIndexFile();
    if (!NetUtil.isFile(cache)) {
      reloadBookList();
    }

    InputStream in = null;
    GZIPInputStream gin = null;
    TarInputStream tin = null;
    try {
      ConfigEntry.resetStatistics();

      in = NetUtil.getInputStream(cache);
      gin = new GZIPInputStream(in);
      tin = new TarInputStream(gin);
      while (true) {
        TarEntry entry = tin.getNextEntry();
        if (entry == null) {
          break;
        }

        String internal = entry.getName();
        if (!entry.isDirectory()) {
          try {
            int size = (int) entry.getSize();

            // Every now and then an empty entry sneaks in
            if (size == 0) {
              log.error("Empty entry: " + internal);
              continue;
            }

            byte[] buffer = new byte[size];
            if (tin.read(buffer) != size) {
              // This should not happen, but if it does then skip
              // it.
              log.error("Did not read all that was expected " + internal);
              continue;
            }

            if (internal.endsWith(SwordConstants.EXTENSION_CONF)) {
              internal = internal.substring(0, internal.length() - 5);
            } else {
              log.error("Not a SWORD config file: " + internal);
              continue;
            }

            if (internal.startsWith(SwordConstants.DIR_CONF + '/')) {
              internal = internal.substring(7);
            }

            SwordBookMetaData sbmd = new SwordBookMetaData(buffer, internal);
            sbmd.setDriver(fake);
            Book book = new SwordBook(sbmd, null);
            entries.put(book.getName(), book);
          } catch (IOException ex) {
            log.error("Failed to load config for entry: " + internal, ex);
          }
        }
      }

      loaded = true;

      ConfigEntry.dumpStatistics();
    } catch (IOException ex) {
      throw new InstallException(JSOtherMsg.lookupText("Error loading from cache"), ex);
    } finally {
      IOUtil.close(tin);
      IOUtil.close(gin);
      IOUtil.close(in);
    }
  }