@Test(timeout = 60000)
  public void testAllocation() throws Exception {
    String allocationPath = "/allocation1";
    SimpleLedgerAllocator allocator = createAllocator(allocationPath);
    allocator.allocate();
    ZKTransaction txn = newTxn();
    LedgerHandle lh = FutureUtils.result(allocator.tryObtain(txn, NULL_LISTENER));
    logger.info("Try obtaining ledger handle {}", lh.getId());
    byte[] data = zkc.get().getData(allocationPath, false, null);
    assertEquals((Long) lh.getId(), Long.valueOf(new String(data, UTF_8)));
    txn.addOp(DefaultZKOp.of(Op.setData("/unexistedpath", "data".getBytes(UTF_8), -1)));
    try {
      FutureUtils.result(txn.execute());
      fail("Should fail the transaction when setting unexisted path");
    } catch (ZKException ke) {
      // expected
      logger.info("Should fail on executing transaction when setting unexisted path", ke);
    }
    data = zkc.get().getData(allocationPath, false, null);
    assertEquals((Long) lh.getId(), Long.valueOf(new String(data, UTF_8)));

    // Create new transaction to obtain the ledger again.
    txn = newTxn();
    // we could obtain the ledger if it was obtained
    LedgerHandle newLh = FutureUtils.result(allocator.tryObtain(txn, NULL_LISTENER));
    assertEquals(lh.getId(), newLh.getId());
    FutureUtils.result(txn.execute());
    data = zkc.get().getData(allocationPath, false, null);
    assertEquals(0, data.length);
    Utils.close(allocator);
  }
 @Test(timeout = 60000)
 public void testObtainMultipleLedgers() throws Exception {
   String allocationPath = "/" + runtime.getMethodName();
   SimpleLedgerAllocator allocator = createAllocator(allocationPath);
   int numLedgers = 10;
   Set<LedgerHandle> allocatedLedgers = new HashSet<LedgerHandle>();
   for (int i = 0; i < numLedgers; i++) {
     allocator.allocate();
     ZKTransaction txn = newTxn();
     LedgerHandle lh = FutureUtils.result(allocator.tryObtain(txn, NULL_LISTENER));
     FutureUtils.result(txn.execute());
     allocatedLedgers.add(lh);
   }
   assertEquals(numLedgers, allocatedLedgers.size());
 }
  @Test(timeout = 60000)
  public void testBadVersionOnTwoAllocators() throws Exception {
    String allocationPath = "/allocation-bad-version";
    zkc.get()
        .create(allocationPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    Stat stat = new Stat();
    byte[] data = zkc.get().getData(allocationPath, false, stat);
    Versioned<byte[]> allocationData =
        new Versioned<byte[]>(data, new ZkVersion(stat.getVersion()));

    SimpleLedgerAllocator allocator1 =
        new SimpleLedgerAllocator(
            allocationPath, allocationData, newQuorumConfigProvider(dlConf), zkc, bkc);
    SimpleLedgerAllocator allocator2 =
        new SimpleLedgerAllocator(
            allocationPath, allocationData, newQuorumConfigProvider(dlConf), zkc, bkc);
    allocator1.allocate();
    // wait until allocated
    ZKTransaction txn1 = newTxn();
    LedgerHandle lh = FutureUtils.result(allocator1.tryObtain(txn1, NULL_LISTENER));
    allocator2.allocate();
    ZKTransaction txn2 = newTxn();
    try {
      FutureUtils.result(allocator2.tryObtain(txn2, NULL_LISTENER));
      fail(
          "Should fail allocating on second allocator as allocator1 is starting allocating something.");
    } catch (ZKException zke) {
      assertEquals(KeeperException.Code.BADVERSION, zke.getKeeperExceptionCode());
    }
    FutureUtils.result(txn1.execute());
    Utils.close(allocator1);
    Utils.close(allocator2);

    long eid = lh.addEntry("hello world".getBytes());
    lh.close();
    LedgerHandle readLh =
        bkc.get()
            .openLedger(lh.getId(), BookKeeper.DigestType.CRC32, dlConf.getBKDigestPW().getBytes());
    Enumeration<LedgerEntry> entries = readLh.readEntries(eid, eid);
    int i = 0;
    while (entries.hasMoreElements()) {
      LedgerEntry entry = entries.nextElement();
      assertEquals("hello world", new String(entry.getEntry(), UTF_8));
      ++i;
    }
    assertEquals(1, i);
  }
 @Test(timeout = 60000)
 public void testCloseAllocatorAfterConfirm() throws Exception {
   String allocationPath = "/allocation2";
   SimpleLedgerAllocator allocator = createAllocator(allocationPath);
   allocator.allocate();
   ZKTransaction txn = newTxn();
   // close during obtaining ledger.
   LedgerHandle lh = FutureUtils.result(allocator.tryObtain(txn, NULL_LISTENER));
   FutureUtils.result(txn.execute());
   Utils.close(allocator);
   byte[] data = zkc.get().getData(allocationPath, false, null);
   assertEquals(0, data.length);
   // the ledger is not deleted.
   bkc.get()
       .openLedger(
           lh.getId(), BookKeeper.DigestType.CRC32, dlConf.getBKDigestPW().getBytes(UTF_8));
 }
 @Test(timeout = 60000)
 public void testCloseAllocatorAfterAbort() throws Exception {
   String allocationPath = "/allocation3";
   SimpleLedgerAllocator allocator = createAllocator(allocationPath);
   allocator.allocate();
   ZKTransaction txn = newTxn();
   // close during obtaining ledger.
   LedgerHandle lh = FutureUtils.result(allocator.tryObtain(txn, NULL_LISTENER));
   txn.addOp(DefaultZKOp.of(Op.setData("/unexistedpath", "data".getBytes(UTF_8), -1)));
   try {
     FutureUtils.result(txn.execute());
     fail("Should fail the transaction when setting unexisted path");
   } catch (ZKException ke) {
     // expected
   }
   Utils.close(allocator);
   byte[] data = zkc.get().getData(allocationPath, false, null);
   assertEquals((Long) lh.getId(), Long.valueOf(new String(data, UTF_8)));
   // the ledger is not deleted.
   bkc.get()
       .openLedger(
           lh.getId(), BookKeeper.DigestType.CRC32, dlConf.getBKDigestPW().getBytes(UTF_8));
 }
 @Test(timeout = 60000)
 public void testConcurrentAllocation() throws Exception {
   String allcationPath = "/" + runtime.getMethodName();
   SimpleLedgerAllocator allocator = createAllocator(allcationPath);
   allocator.allocate();
   ZKTransaction txn1 = newTxn();
   Future<LedgerHandle> obtainFuture1 = allocator.tryObtain(txn1, NULL_LISTENER);
   ZKTransaction txn2 = newTxn();
   Future<LedgerHandle> obtainFuture2 = allocator.tryObtain(txn2, NULL_LISTENER);
   assertTrue(obtainFuture2.isDefined());
   assertTrue(obtainFuture2.isThrow());
   try {
     FutureUtils.result(obtainFuture2);
     fail(
         "Should fail the concurrent obtain since there is already a transaction obtaining the ledger handle");
   } catch (SimpleLedgerAllocator.ConcurrentObtainException cbe) {
     // expected
   }
 }
  @Test(timeout = 60000)
  public void testAllocatorWithoutEnoughBookies() throws Exception {
    String allocationPath = "/allocator-without-enough-bookies";

    DistributedLogConfiguration confLocal = new DistributedLogConfiguration();
    confLocal.addConfiguration(conf);
    confLocal.setEnsembleSize(numBookies * 2);
    confLocal.setWriteQuorumSize(numBookies * 2);

    SimpleLedgerAllocator allocator1 = createAllocator(allocationPath, confLocal);
    allocator1.allocate();
    ZKTransaction txn1 = newTxn();

    try {
      FutureUtils.result(allocator1.tryObtain(txn1, NULL_LISTENER));
      fail("Should fail allocating ledger if there aren't enough bookies");
    } catch (AllocationException ioe) {
      // expected
      assertEquals(Phase.ERROR, ioe.getPhase());
    }
    byte[] data = zkc.get().getData(allocationPath, false, null);
    assertEquals(0, data.length);
  }
  @Test(timeout = 60000)
  public void testSuccessAllocatorShouldDeleteUnusedledger() throws Exception {
    String allocationPath = "/allocation-delete-unused-ledger";
    zkc.get()
        .create(allocationPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    Stat stat = new Stat();
    byte[] data = zkc.get().getData(allocationPath, false, stat);

    Versioned<byte[]> allocationData =
        new Versioned<byte[]>(data, new ZkVersion(stat.getVersion()));

    SimpleLedgerAllocator allocator1 =
        new SimpleLedgerAllocator(
            allocationPath, allocationData, newQuorumConfigProvider(dlConf), zkc, bkc);
    allocator1.allocate();
    // wait until allocated
    ZKTransaction txn1 = newTxn();
    LedgerHandle lh1 = FutureUtils.result(allocator1.tryObtain(txn1, NULL_LISTENER));

    // Second allocator kicks in
    stat = new Stat();
    data = zkc.get().getData(allocationPath, false, stat);
    allocationData = new Versioned<byte[]>(data, new ZkVersion(stat.getVersion()));
    SimpleLedgerAllocator allocator2 =
        new SimpleLedgerAllocator(
            allocationPath, allocationData, newQuorumConfigProvider(dlConf), zkc, bkc);
    allocator2.allocate();
    // wait until allocated
    ZKTransaction txn2 = newTxn();
    LedgerHandle lh2 = FutureUtils.result(allocator2.tryObtain(txn2, NULL_LISTENER));

    // should fail to commit txn1 as version is changed by second allocator
    try {
      FutureUtils.result(txn1.execute());
      fail(
          "Should fail commit obtaining ledger handle from first allocator as allocator is modified by second allocator.");
    } catch (ZKException ke) {
      // as expected
    }
    FutureUtils.result(txn2.execute());
    Utils.close(allocator1);
    Utils.close(allocator2);

    // ledger handle should be deleted
    try {
      lh1.close();
      fail("LedgerHandle allocated by allocator1 should be deleted.");
    } catch (BKException bke) {
      // as expected
    }
    try {
      bkc.get()
          .openLedger(lh1.getId(), BookKeeper.DigestType.CRC32, dlConf.getBKDigestPW().getBytes());
      fail("LedgerHandle allocated by allocator1 should be deleted.");
    } catch (BKException.BKNoSuchLedgerExistsException nslee) {
      // as expected
    }
    long eid = lh2.addEntry("hello world".getBytes());
    lh2.close();
    LedgerHandle readLh =
        bkc.get()
            .openLedger(
                lh2.getId(), BookKeeper.DigestType.CRC32, dlConf.getBKDigestPW().getBytes());
    Enumeration<LedgerEntry> entries = readLh.readEntries(eid, eid);
    int i = 0;
    while (entries.hasMoreElements()) {
      LedgerEntry entry = entries.nextElement();
      assertEquals("hello world", new String(entry.getEntry(), UTF_8));
      ++i;
    }
    assertEquals(1, i);
  }
 private SimpleLedgerAllocator createAllocator(
     String allocationPath, DistributedLogConfiguration conf) throws IOException {
   return FutureUtils.result(
       SimpleLedgerAllocator.of(allocationPath, null, newQuorumConfigProvider(conf), zkc, bkc));
 }