/**
   * Convert Iterable of {@link ZKOp} we got into the ZooKeeper.Op instances to actually pass to
   * multi (need to do this in order to appendMetaData).
   */
  private Iterable<Op> prepareZKMulti(Iterable<Op> ops) throws UnsupportedOperationException {
    if (ops == null) return null;

    List<Op> preparedOps = new LinkedList<Op>();
    for (Op op : ops) {
      if (op.getType() == ZooDefs.OpCode.create) {
        CreateRequest create = (CreateRequest) op.toRequestRecord();
        preparedOps.add(
            Op.create(
                create.getPath(),
                appendMetaData(create.getData()),
                create.getAcl(),
                create.getFlags()));
      } else if (op.getType() == ZooDefs.OpCode.delete) {
        // no need to appendMetaData for delete
        preparedOps.add(op);
      } else if (op.getType() == ZooDefs.OpCode.setData) {
        SetDataRequest setData = (SetDataRequest) op.toRequestRecord();
        preparedOps.add(
            Op.setData(setData.getPath(), appendMetaData(setData.getData()), setData.getVersion()));
      } else {
        throw new UnsupportedOperationException("Unexpected ZKOp type: " + op.getClass().getName());
      }
    }
    return preparedOps;
  }
  @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 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));
 }