private void initializeBookKeeper(final ManagedLedgerCallback<Void> callback) {
    log.debug("[{}] initializing bookkeeper; ledgers {}", name, ledgers);

    final MetaStoreCallback<Void> storeLedgersCb =
        new MetaStoreCallback<Void>() {
          public void operationComplete(Void v, Version version) {
            ledgersVersion = version;
            initializeCursors(callback);
          }

          public void operationFailed(MetaStoreException e) {
            callback.operationFailed(new ManagedLedgerException(e));
          }
        };
    // Create a new ledger to start writing
    bookKeeper.asyncCreateLedger(
        config.getEnsembleSize(),
        config.getQuorumSize(),
        config.getDigestType(),
        config.getPassword(),
        new CreateCallback() {
          public void createComplete(int rc, LedgerHandle lh, Object ctx) {
            if (rc == BKException.Code.OK) {
              state = State.LedgerOpened;
              currentLedger = lh;
              ledgers.put(currentLedger.getId(), new LedgerStat(currentLedger.getId(), 0, 0));
              // Save it back to ensure all nodes exist
              store.asyncUpdateLedgerIds(name, ledgers.values(), ledgersVersion, storeLedgersCb);
            } else {
              callback.operationFailed(new ManagedLedgerException(BKException.create(rc)));
            }
          }
        },
        null);
  }
  @Override
  public synchronized void asyncAddEntry(
      final byte[] data, final AddEntryCallback callback, final Object ctx) {
    checkArgument(state != State.None);
    log.debug("[{}] asyncAddEntry size={} state={}", va(name, data.length, state));
    if (state == State.Fenced) {
      callback.addComplete(new ManagedLedgerFencedException(), null, ctx);
      return;
    }

    OpAddEntry addOperation = new OpAddEntry(this, data, callback, ctx);

    if (state == State.ClosingLedger || state == State.CreatingLedger) {
      // We don't have a ready ledger to write into
      // We are waiting for a new ledger to be created
      log.debug("[{}] Queue addEntry request", name);
      pendingAddEntries.add(addOperation);
    } else if (state == State.ClosedLedger) {
      // No ledger and no pending operations. Create a new one
      pendingAddEntries.add(addOperation);
      log.debug("[{}] Creating a new ledger", name);
      state = State.CreatingLedger;
      bookKeeper.asyncCreateLedger(
          config.getEnsembleSize(),
          config.getQuorumSize(),
          config.getDigestType(),
          config.getPassword(),
          this,
          ctx);
    } else {
      checkArgument(state == State.LedgerOpened);
      checkArgument(!currentLedgerIsFull());

      // Write into lastLedger
      log.debug("[{}] Write into current ledger lh={}", name, currentLedger.getId());
      addOperation.setLedger(currentLedger);

      ++currentLedgerEntries;
      currentLedgerSize += data.length;
      if (currentLedgerIsFull()) {
        // This entry will be the last added to current ledger
        addOperation.setCloseWhenDone(true);
        state = State.ClosingLedger;
      }

      addOperation.initiate();
    }
  }
  protected synchronized void ledgerClosed(LedgerHandle lh) {
    checkArgument(lh.getId() == currentLedger.getId());
    state = State.ClosedLedger;

    log.debug(
        "[{}] Ledger has been closed id={} entries={}",
        va(name, lh.getId(), lh.getLastAddConfirmed() + 1));
    ledgers.put(lh.getId(), new LedgerStat(lh));

    trimConsumedLedgersInBackground();

    if (!pendingAddEntries.isEmpty()) {
      // Need to create a new ledger to write pending entries
      log.debug("[{}] Creating a new ledger", name);
      state = State.CreatingLedger;
      bookKeeper.asyncCreateLedger(
          config.getEnsembleSize(),
          config.getQuorumSize(),
          config.getDigestType(),
          config.getPassword(),
          this,
          null);
    }
  }