This is an automated email from the ASF dual-hosted git repository.

zhaijia pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/bookkeeper.git


The following commit(s) were added to refs/heads/master by this push:
     new 67f8362  Migrate command `readledger`
67f8362 is described below

commit 67f83620eb8ed93bf73322293deb1a1bde8d09c7
Author: Yong Zhang <[email protected]>
AuthorDate: Wed Apr 3 21:30:16 2019 +0800

    Migrate command `readledger`
    
    Descriptions of the changes in this PR:
    
    #2040
    
    - Using `bkctl` run `readledger`
    
    ```
    Read a range of entries from a ledger.
    
    Usage:  bkctl bookie readledger [flags]
    
    Flags:
    
        -b, --bookie
            Only read from a specific bookie
    
        -ef, --entryformatter
            Set entry formatter
    
        -fe, --firstentryid
            First Entry ID
    
        -r, --force-recovery
            Ensure the ledger is properly closed before reading
    
        -le, --lastentryid
            Last Entry ID
    
        -l, --ledgerid
            Ledger ID
    
        -lf, --ledgeridformatter
            Set ledger id formatter
    
        -m, --msg
            Print message body
    
    
        -h, --help
            Display help information
    ```
    
    
    Reviewers: Jia Zhai <[email protected]>, Sijie Guo <[email protected]>
    
    This closes #2041 from zymap/command-readledger and squashes the following 
commits:
    
    56b0a4581 [Yong Zhang] Remove unused import
    30dafa85f [Yong Zhang] Merge branch 'master' into command-readledger
    bfbd6b023 [Yong Zhang] Migrate command `decommission`
    d40b8b69f [Yong Zhang] Migrate command `readlog`
    95d145a15 [Yong Zhang] Migrate command `nukeexistingcluster`
    e2b1dc7f3 [Yong Zhang] Migrate command `listunderreplicated`
    c465c4761 [Yong Zhang] Remove unused import
    0988e12c7 [bd2019us] ISSUE #2023: change cached thread pool to fixed thread 
pool
    6a6d7bbd9 [Yong Zhang] Migrate command `initnewcluster`
    931df8c2c [Sijie Guo] Merge branch 'master' into command-readledger
    c391fe58d [Yong Zhang] Migrate command `readlogmetadata`
    120d67737 [Yong Zhang] Migrate command `lostbookierecoverydelay`
    bf66235e5 [Yong Zhang] Migrate command `deleteledger`
    87e6644f2 [Yong Zhang] Fix some conflict
    5ae05f0d2 [Yong Zhang] Migrate command `readledger`
    751e55fa4 [Arvin] ISSUE #2020: close db properly to avoid open RocksDB 
failure at the second time
    138a7ae85 [Yong Zhang] Migrate command `metadataformat`
    b043d1694 [Yong Zhang] Migrate command `listledgers`
    4573285db [Ivan Kelly] Docker autobuild hook
    e3d807a32 [Like] Fix IDE complain as there are multi choices for error code
    9524a9f4a [Yong Zhang] Migrate command `readjournal`
    6c3f33f55 [Yong Zhang] Fix when met unexpect entry id crashed
    e35a108c7 [Like] Fix error message for unrecognized number-of-bookies
    5902ee27b [Boyang Jerry Peng] fix potential NPE when releasing entry that 
is null
    6aa73ce05 [Ivan Kelly] [RELEASE] Update website to include documentation 
for 4.8.2
    1448d12aa [Yong Zhang] Migrate command `listfilesondisk`
    4de598379 [Yong Zhang] Issue #1987: Migrate command 
`convert-to-interleaved-storage`
    468743e7e [Matteo Merli] In DbLedgerStorage use default values when config 
key is present but empty
    f26a4cae0 [Ivan Kelly] Release notes for v4.8.2
    ec2636cd2 [Yong Zhang] Issue #1985: Migrate command `convert-to-db-storage`
    8cc7239ac [Yong Zhang] Issue #1982: Migrate command `bookiesanity`
    fa90f0185 [Yong Zhang] Issue #1980: Migrate command `ledger` from shell to 
bkctl
---
 .../org/apache/bookkeeper/bookie/BookieShell.java  | 104 ++-------
 .../cli/commands/bookie/ReadLedgerCommand.java     | 244 +++++++++++++++++++++
 .../tools/cli/commands/BookieCommandGroup.java     |   2 +
 .../cli/commands/bookie/ReadLedgerCommandTest.java | 169 ++++++++++++++
 4 files changed, 428 insertions(+), 91 deletions(-)

diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/BookieShell.java 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/BookieShell.java
index 870d31b..98f276c 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/BookieShell.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/BookieShell.java
@@ -27,11 +27,6 @@ import static 
org.apache.bookkeeper.tools.cli.helpers.CommandHelpers.getBookieSo
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.UncheckedExecutionException;
-import io.netty.buffer.ByteBufUtil;
-import io.netty.buffer.UnpooledByteBufAllocator;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.util.concurrent.DefaultThreadFactory;
 import java.io.File;
 import java.io.IOException;
 import java.io.Serializable;
@@ -50,7 +45,6 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -58,13 +52,8 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
-import java.util.stream.LongStream;
-
 import org.apache.bookkeeper.bookie.BookieException.CookieNotFoundException;
 import org.apache.bookkeeper.bookie.BookieException.InvalidCookieException;
 import org.apache.bookkeeper.bookie.storage.ldb.LocationsIndexRebuildOp;
@@ -73,11 +62,9 @@ import 
org.apache.bookkeeper.client.BKException.MetaStoreException;
 import org.apache.bookkeeper.client.BookKeeper;
 import org.apache.bookkeeper.client.BookKeeperAdmin;
 import org.apache.bookkeeper.client.LedgerEntry;
-import org.apache.bookkeeper.client.LedgerHandle;
 import org.apache.bookkeeper.client.UpdateLedgerOp;
 import org.apache.bookkeeper.client.api.LedgerMetadata;
 import org.apache.bookkeeper.common.annotation.InterfaceAudience.Private;
-import org.apache.bookkeeper.common.util.OrderedExecutor;
 import org.apache.bookkeeper.conf.ClientConfiguration;
 import org.apache.bookkeeper.conf.ServerConfiguration;
 import org.apache.bookkeeper.discover.RegistrationManager;
@@ -86,12 +73,8 @@ import org.apache.bookkeeper.meta.LedgerMetadataSerDe;
 import org.apache.bookkeeper.meta.LedgerUnderreplicationManager;
 import org.apache.bookkeeper.meta.zk.ZKMetadataDriverBase;
 import org.apache.bookkeeper.net.BookieSocketAddress;
-import org.apache.bookkeeper.proto.BookieClient;
-import org.apache.bookkeeper.proto.BookieClientImpl;
-import org.apache.bookkeeper.proto.BookieProtocol;
 import org.apache.bookkeeper.replication.AuditorElector;
 import org.apache.bookkeeper.replication.ReplicationException;
-import org.apache.bookkeeper.stats.NullStatsLogger;
 import 
org.apache.bookkeeper.tools.cli.commands.autorecovery.ListUnderReplicatedCommand;
 import 
org.apache.bookkeeper.tools.cli.commands.autorecovery.LostBookieRecoveryDelayCommand;
 import 
org.apache.bookkeeper.tools.cli.commands.bookie.ConvertToDBStorageCommand;
@@ -103,6 +86,7 @@ import 
org.apache.bookkeeper.tools.cli.commands.bookie.LedgerCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.ListFilesOnDiscCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.ListLedgersCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.ReadJournalCommand;
+import org.apache.bookkeeper.tools.cli.commands.bookie.ReadLedgerCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.ReadLogCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.ReadLogMetadataCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.SanityTestCommand;
@@ -772,91 +756,29 @@ public class BookieShell implements Tool {
         @Override
         int runCmd(CommandLine cmdLine) throws Exception {
             final long ledgerId = getOptionLedgerIdValue(cmdLine, "ledgerid", 
-1);
-            if (ledgerId == -1) {
-                System.err.println("Must specify a ledger id");
-                return -1;
-            }
-
             final long firstEntry = getOptionLongValue(cmdLine, 
"firstentryid", 0);
             long lastEntry = getOptionLongValue(cmdLine, "lastentryid", -1);
 
             boolean printMsg = cmdLine.hasOption("m");
             boolean forceRecovery = cmdLine.hasOption("r");
             final BookieSocketAddress bookie;
+            String bookieAddress;
             if (cmdLine.hasOption("b")) {
                 // A particular bookie was specified
-                bookie = new BookieSocketAddress(cmdLine.getOptionValue("b"));
+                bookieAddress = cmdLine.getOptionValue("b");
             } else {
-                bookie = null;
-            }
-
-            ClientConfiguration conf = new ClientConfiguration();
-            conf.addConfiguration(bkConf);
-
-            try (BookKeeperAdmin bk = new BookKeeperAdmin(conf)) {
-                if (forceRecovery) {
-                    // Force the opening of the ledger to trigger recovery
-                    try (LedgerHandle lh = bk.openLedger(ledgerId)) {
-                        if (lastEntry == -1 || lastEntry > 
lh.getLastAddConfirmed()) {
-                            lastEntry = lh.getLastAddConfirmed();
-                        }
-                    }
-                }
-
-                if (bookie == null) {
-                    // No bookie was specified, use normal bk client
-                    Iterator<LedgerEntry> entries = bk.readEntries(ledgerId, 
firstEntry, lastEntry).iterator();
-                    while (entries.hasNext()) {
-                        LedgerEntry entry = entries.next();
-                        formatEntry(entry, printMsg);
-                    }
-                } else {
-                    // Use BookieClient to target a specific bookie
-                    EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
-                    OrderedExecutor executor = OrderedExecutor.newBuilder()
-                        .numThreads(1)
-                        .name("BookieClientScheduler")
-                        .build();
-
-                    ScheduledExecutorService scheduler = 
Executors.newSingleThreadScheduledExecutor(
-                        new 
DefaultThreadFactory("BookKeeperClientSchedulerPool"));
-
-                    BookieClient bookieClient = new BookieClientImpl(conf, 
eventLoopGroup,
-                            UnpooledByteBufAllocator.DEFAULT, executor, 
scheduler, NullStatsLogger.INSTANCE);
-
-                    LongStream.range(firstEntry, lastEntry).forEach(entryId -> 
{
-                        CompletableFuture<Void> future = new 
CompletableFuture<>();
-
-                        bookieClient.readEntry(bookie, ledgerId, entryId,
-                            (rc, ledgerId1, entryId1, buffer, ctx) -> {
-                                if (rc != BKException.Code.OK) {
-                                    LOG.error("Failed to read entry {} -- {}", 
entryId1, BKException.getMessage(rc));
-                                    
future.completeExceptionally(BKException.create(rc));
-                                    return;
-                                }
-
-                                System.out.println("--------- Lid=" + 
ledgerIdFormatter.formatLedgerId(ledgerId)
-                                    + ", Eid=" + entryId + " ---------");
-                                if (printMsg) {
-                                    System.out.println("Data: " + 
ByteBufUtil.prettyHexDump(buffer));
-                                }
-
-                                future.complete(null);
-                                }, null, BookieProtocol.FLAG_NONE);
-
-                        try {
-                            future.get();
-                        } catch (Exception e) {
-                            LOG.error("Error future.get while reading entries 
from ledger {}", ledgerId, e);
-                        }
-                    });
-
-                    eventLoopGroup.shutdownGracefully();
-                    executor.shutdown();
-                    bookieClient.close();
-                }
+                bookieAddress = null;
             }
 
+            ReadLedgerCommand cmd = new ReadLedgerCommand(entryFormatter, 
ledgerIdFormatter);
+            ReadLedgerCommand.ReadLedgerFlags flags = new 
ReadLedgerCommand.ReadLedgerFlags();
+            flags.bookieAddresss(bookieAddress);
+            flags.firstEntryId(firstEntry);
+            flags.forceRecovery(forceRecovery);
+            flags.lastEntryId(lastEntry);
+            flags.ledgerId(ledgerId);
+            flags.msg(printMsg);
+            cmd.apply(bkConf, flags);
             return 0;
         }
 
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/bookie/ReadLedgerCommand.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/bookie/ReadLedgerCommand.java
new file mode 100644
index 0000000..98c1b11
--- /dev/null
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/bookie/ReadLedgerCommand.java
@@ -0,0 +1,244 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bookkeeper.tools.cli.commands.bookie;
+
+import com.beust.jcommander.Parameter;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.UnpooledByteBufAllocator;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.util.concurrent.DefaultThreadFactory;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.stream.LongStream;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+import org.apache.bookkeeper.client.BKException;
+import org.apache.bookkeeper.client.BookKeeperAdmin;
+import org.apache.bookkeeper.client.LedgerEntry;
+import org.apache.bookkeeper.client.LedgerHandle;
+import org.apache.bookkeeper.common.util.OrderedExecutor;
+import org.apache.bookkeeper.conf.ClientConfiguration;
+import org.apache.bookkeeper.conf.ServerConfiguration;
+import org.apache.bookkeeper.net.BookieSocketAddress;
+import org.apache.bookkeeper.proto.BookieClient;
+import org.apache.bookkeeper.proto.BookieClientImpl;
+import org.apache.bookkeeper.proto.BookieProtocol;
+import org.apache.bookkeeper.stats.NullStatsLogger;
+import org.apache.bookkeeper.tools.cli.helpers.BookieCommand;
+import org.apache.bookkeeper.tools.framework.CliFlags;
+import org.apache.bookkeeper.tools.framework.CliSpec;
+import org.apache.bookkeeper.util.EntryFormatter;
+import org.apache.bookkeeper.util.LedgerIdFormatter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Command to read ledger entries.
+ */
+public class ReadLedgerCommand extends 
BookieCommand<ReadLedgerCommand.ReadLedgerFlags> {
+
+    static final Logger LOG = LoggerFactory.getLogger(ReadLedgerCommand.class);
+
+    private static final String NAME = "readledger";
+    private static final String DESC = "Read a range of entries from a 
ledger.";
+
+    EntryFormatter entryFormatter;
+    LedgerIdFormatter ledgerIdFormatter;
+
+    public ReadLedgerCommand() {
+        this(new ReadLedgerFlags());
+    }
+
+    public ReadLedgerCommand(EntryFormatter entryFormatter, LedgerIdFormatter 
ledgerIdFormatter) {
+        this(new ReadLedgerFlags());
+        this.ledgerIdFormatter = ledgerIdFormatter;
+        this.entryFormatter = entryFormatter;
+    }
+
+    private ReadLedgerCommand(ReadLedgerFlags flags) {
+        super(CliSpec.<ReadLedgerFlags>newBuilder()
+                  .withName(NAME)
+                  .withDescription(DESC)
+                  .withFlags(flags)
+                  .build());
+    }
+
+    /**
+     * Flags for read ledger command.
+     */
+    @Accessors(fluent = true)
+    @Setter
+    public static class ReadLedgerFlags extends CliFlags {
+
+        @Parameter(names = { "-m", "--msg" }, description = "Print message 
body")
+        private boolean msg;
+
+        @Parameter(names = { "-l", "--ledgerid" }, description = "Ledger ID")
+        private long ledgerId = -1;
+
+        @Parameter(names = { "-fe", "--firstentryid" }, description = "First 
Entry ID")
+        private long firstEntryId = -1;
+
+        @Parameter(names = { "-le", "--lastentryid" }, description = "Last 
Entry ID")
+        private long lastEntryId = -1;
+
+        @Parameter(names = { "-r", "--force-recovery" },
+            description = "Ensure the ledger is properly closed before 
reading")
+        private boolean forceRecovery;
+
+        @Parameter(names = { "-b", "--bookie" }, description = "Only read from 
a specific bookie")
+        private String bookieAddresss;
+
+        @Parameter(names = { "-lf", "--ledgeridformatter" }, description = 
"Set ledger id formatter")
+        private String ledgerIdFormatter;
+
+        @Parameter(names = { "-ef", "--entryformatter" }, description = "Set 
entry formatter")
+        private String entryFormatter;
+    }
+
+    @Override
+    public boolean apply(ServerConfiguration conf, ReadLedgerFlags cmdFlags) {
+        if (cmdFlags.ledgerIdFormatter != null && ledgerIdFormatter == null) {
+            this.ledgerIdFormatter = 
LedgerIdFormatter.newLedgerIdFormatter(cmdFlags.ledgerIdFormatter, conf);
+        } else if (ledgerIdFormatter == null) {
+            this.ledgerIdFormatter = 
LedgerIdFormatter.newLedgerIdFormatter(conf);
+        }
+
+        if (cmdFlags.entryFormatter != null && entryFormatter == null) {
+            this.entryFormatter = 
EntryFormatter.newEntryFormatter(cmdFlags.entryFormatter, conf);
+        } else if (entryFormatter == null) {
+            this.entryFormatter = EntryFormatter.newEntryFormatter(conf);
+        }
+
+        try {
+            return readledger(conf, cmdFlags);
+        } catch (Exception e) {
+            throw new UncheckedExecutionException(e.getMessage(), e);
+        }
+    }
+
+    private boolean readledger(ServerConfiguration serverConf, ReadLedgerFlags 
flags)
+        throws InterruptedException, BKException, IOException {
+
+        long lastEntry = flags.lastEntryId;
+
+        final BookieSocketAddress bookie;
+        if (flags.bookieAddresss != null) {
+            // A particular bookie was specified
+            bookie = new BookieSocketAddress(flags.bookieAddresss);
+        } else {
+            bookie = null;
+        }
+
+        ClientConfiguration conf = new ClientConfiguration();
+        conf.addConfiguration(serverConf);
+
+        try (BookKeeperAdmin bk = new BookKeeperAdmin(conf)) {
+            if (flags.forceRecovery) {
+                // Force the opening of the ledger to trigger recovery
+                try (LedgerHandle lh = bk.openLedger(flags.ledgerId)) {
+                    if (lastEntry == -1 || lastEntry > 
lh.getLastAddConfirmed()) {
+                        lastEntry = lh.getLastAddConfirmed();
+                    }
+                }
+            }
+
+            if (bookie == null) {
+                // No bookie was specified, use normal bk client
+                Iterator<LedgerEntry> entries = bk.readEntries(flags.ledgerId, 
flags.firstEntryId, lastEntry)
+                                                  .iterator();
+                while (entries.hasNext()) {
+                    LedgerEntry entry = entries.next();
+                    formatEntry(entry, flags.msg);
+                }
+            } else {
+                // Use BookieClient to target a specific bookie
+                EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
+                OrderedExecutor executor = OrderedExecutor.newBuilder()
+                                                          .numThreads(1)
+                                                          
.name("BookieClientScheduler")
+                                                          .build();
+
+                ScheduledExecutorService scheduler = 
Executors.newSingleThreadScheduledExecutor(
+                    new DefaultThreadFactory("BookKeeperClientSchedulerPool"));
+
+                BookieClient bookieClient = new BookieClientImpl(conf, 
eventLoopGroup, UnpooledByteBufAllocator.DEFAULT,
+                                                                 executor, 
scheduler, NullStatsLogger.INSTANCE);
+
+                LongStream.range(flags.firstEntryId, 
lastEntry).forEach(entryId -> {
+                    CompletableFuture<Void> future = new CompletableFuture<>();
+
+                    bookieClient.readEntry(bookie, flags.ledgerId, entryId,
+                                           (rc, ledgerId1, entryId1, buffer, 
ctx) -> {
+                                               if (rc != BKException.Code.OK) {
+                                                   LOG.error("Failed to read 
entry {} -- {}", entryId1,
+                                                             
BKException.getMessage(rc));
+                                                   
future.completeExceptionally(BKException.create(rc));
+                                                   return;
+                                               }
+
+                                               System.out.println(
+                                                   "--------- Lid=" + 
ledgerIdFormatter.formatLedgerId(flags.ledgerId)
+                                                   + ", Eid=" + entryId + " 
---------");
+                                               if (flags.msg) {
+                                                   System.out.println("Data: " 
+ ByteBufUtil.prettyHexDump(buffer));
+                                               }
+
+                                               future.complete(null);
+                                           }, null, BookieProtocol.FLAG_NONE);
+
+                    try {
+                        future.get();
+                    } catch (Exception e) {
+                        LOG.error("Error future.get while reading entries from 
ledger {}", flags.ledgerId, e);
+                    }
+                });
+
+                eventLoopGroup.shutdownGracefully();
+                executor.shutdown();
+                bookieClient.close();
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Format the entry into a readable format.
+     *
+     * @param entry
+     *          ledgerentry to print
+     * @param printMsg
+     *          Whether printing the message body
+     */
+    private void formatEntry(LedgerEntry entry, boolean printMsg) {
+        long ledgerId = entry.getLedgerId();
+        long entryId = entry.getEntryId();
+        long entrySize = entry.getLength();
+        System.out.println("--------- Lid=" + 
ledgerIdFormatter.formatLedgerId(ledgerId) + ", Eid=" + entryId
+                           + ", EntrySize=" + entrySize + " ---------");
+        if (printMsg) {
+            entryFormatter.formatEntry(entry.getEntry());
+        }
+    }
+}
diff --git 
a/tools/ledger/src/main/java/org/apache/bookkeeper/tools/cli/commands/BookieCommandGroup.java
 
b/tools/ledger/src/main/java/org/apache/bookkeeper/tools/cli/commands/BookieCommandGroup.java
index 2970c8c..51e579f 100644
--- 
a/tools/ledger/src/main/java/org/apache/bookkeeper/tools/cli/commands/BookieCommandGroup.java
+++ 
b/tools/ledger/src/main/java/org/apache/bookkeeper/tools/cli/commands/BookieCommandGroup.java
@@ -30,6 +30,7 @@ import 
org.apache.bookkeeper.tools.cli.commands.bookie.LedgerCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.ListFilesOnDiscCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.ListLedgersCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.ReadJournalCommand;
+import org.apache.bookkeeper.tools.cli.commands.bookie.ReadLedgerCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.ReadLogCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.ReadLogMetadataCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.SanityTestCommand;
@@ -60,6 +61,7 @@ public class BookieCommandGroup extends 
CliCommandGroup<BKFlags> {
         .addCommand(new ListLedgersCommand())
         .addCommand(new ConvertToInterleavedStorageCommand())
         .addCommand(new ReadJournalCommand())
+        .addCommand(new ReadLedgerCommand())
         .addCommand(new ReadLogCommand())
         .addCommand(new ReadLogMetadataCommand())
         .build();
diff --git 
a/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/bookie/ReadLedgerCommandTest.java
 
b/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/bookie/ReadLedgerCommandTest.java
new file mode 100644
index 0000000..7eb651d
--- /dev/null
+++ 
b/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/bookie/ReadLedgerCommandTest.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bookkeeper.tools.cli.commands.bookie;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.powermock.api.mockito.PowerMockito.mock;
+import static org.powermock.api.mockito.PowerMockito.verifyNew;
+import static org.powermock.api.mockito.PowerMockito.when;
+
+import io.netty.buffer.UnpooledByteBufAllocator;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.util.concurrent.DefaultThreadFactory;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import org.apache.bookkeeper.client.BookKeeperAdmin;
+import org.apache.bookkeeper.client.LedgerEntry;
+import org.apache.bookkeeper.client.LedgerHandle;
+import org.apache.bookkeeper.common.util.OrderedExecutor;
+import org.apache.bookkeeper.conf.ClientConfiguration;
+import org.apache.bookkeeper.conf.ServerConfiguration;
+import org.apache.bookkeeper.net.BookieSocketAddress;
+import org.apache.bookkeeper.proto.BookieClientImpl;
+import org.apache.bookkeeper.stats.NullStatsLogger;
+import org.apache.bookkeeper.tools.cli.helpers.BookieCommandTestBase;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+/**
+ * Unit test for {@link ReadLedgerCommand}.
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ ReadLedgerCommand.class, BookKeeperAdmin.class, 
BookieSocketAddress.class, ClientConfiguration.class,
+    LedgerHandle.class, LedgerEntry.class, OrderedExecutor.class })
+public class ReadLedgerCommandTest extends BookieCommandTestBase {
+
+    @Mock
+    private BookieSocketAddress bookieSocketAddress;
+
+    @Mock
+    private ClientConfiguration clientConfiguration;
+
+    @Mock
+    private BookKeeperAdmin bookKeeperAdmin;
+
+    @Mock
+    private LedgerHandle ledgerHandle;
+
+    @Mock
+    private LedgerEntry entry;
+
+    @Mock
+    private NioEventLoopGroup nioEventLoopGroup;
+
+    @Mock
+    private OrderedExecutor orderedExecutor;
+
+    @Mock
+    private ScheduledExecutorService scheduledExecutorService;
+
+    @Mock
+    private DefaultThreadFactory defaultThreadFactory;
+
+    @Mock
+    private BookieClientImpl bookieClient;
+
+    public ReadLedgerCommandTest() {
+        super(3, 0);
+    }
+
+    @Override
+    public void setup() throws Exception {
+        super.setup();
+
+        
PowerMockito.whenNew(ServerConfiguration.class).withNoArguments().thenReturn(conf);
+        
PowerMockito.whenNew(BookieSocketAddress.class).withArguments(anyString()).thenReturn(bookieSocketAddress);
+        
PowerMockito.whenNew(ClientConfiguration.class).withNoArguments().thenReturn(clientConfiguration);
+        
PowerMockito.whenNew(BookKeeperAdmin.class).withParameterTypes(ClientConfiguration.class)
+                    
.withArguments(eq(clientConfiguration)).thenReturn(bookKeeperAdmin);
+
+        when(bookKeeperAdmin.openLedger(anyLong())).thenReturn(ledgerHandle);
+        when(ledgerHandle.getLastAddConfirmed()).thenReturn(1L);
+
+        List<LedgerEntry> entries = new LinkedList<>();
+        entries.add(entry);
+        when(entry.getLedgerId()).thenReturn(1L);
+        when(entry.getEntryId()).thenReturn(1L);
+        when(entry.getLength()).thenReturn(1L);
+
+        when(bookKeeperAdmin.readEntries(anyLong(), anyLong(), 
anyLong())).thenReturn(entries);
+
+        
PowerMockito.whenNew(NioEventLoopGroup.class).withNoArguments().thenReturn(nioEventLoopGroup);
+
+        PowerMockito.mockStatic(OrderedExecutor.class);
+        OrderedExecutor.Builder builder = mock(OrderedExecutor.Builder.class);
+        when(OrderedExecutor.newBuilder()).thenReturn(builder);
+        when(builder.numThreads(anyInt())).thenCallRealMethod();
+        when(builder.name(anyString())).thenCallRealMethod();
+        when(builder.build()).thenReturn(orderedExecutor);
+
+        PowerMockito.mockStatic(Executors.class);
+        
PowerMockito.whenNew(DefaultThreadFactory.class).withArguments(anyString()).thenReturn(defaultThreadFactory);
+        
when(Executors.newSingleThreadScheduledExecutor(eq(defaultThreadFactory))).thenReturn(scheduledExecutorService);
+
+        PowerMockito.whenNew(BookieClientImpl.class)
+                    .withArguments(eq(clientConfiguration), 
eq(nioEventLoopGroup), eq(UnpooledByteBufAllocator.DEFAULT),
+                                   eq(orderedExecutor), 
eq(scheduledExecutorService), eq(NullStatsLogger.INSTANCE))
+                    .thenReturn(bookieClient);
+
+
+    }
+
+    @Test
+    public void testWithoutBookieAddress() throws Exception {
+        ReadLedgerCommand cmd = new ReadLedgerCommand();
+        Assert.assertTrue(cmd.apply(bkFlags, new String[] { "-r" }));
+        verifyNew(ClientConfiguration.class, times(1)).withNoArguments();
+        verify(clientConfiguration, times(1)).addConfiguration(eq(conf));
+        verifyNew(BookKeeperAdmin.class, 
times(1)).withArguments(eq(clientConfiguration));
+        verify(bookKeeperAdmin, times(1)).openLedger(anyLong());
+        verify(ledgerHandle, times(1)).getLastAddConfirmed();
+        verify(bookKeeperAdmin, times(1)).readEntries(anyLong(), anyLong(), 
anyLong());
+        verify(entry, times(1)).getLedgerId();
+        verify(entry, times(1)).getEntryId();
+        verify(entry, times(1)).getLength();
+    }
+
+    @Test
+    public void testWithBookieAddress() throws Exception {
+        ReadLedgerCommand cmd = new ReadLedgerCommand();
+        Assert.assertTrue(cmd.apply(bkFlags, new String[] { "-b", 
"localhost:9000" }));
+        verifyNew(NioEventLoopGroup.class, times(1)).withNoArguments();
+        verifyNew(DefaultThreadFactory.class, 
times(1)).withArguments(anyString());
+        verifyNew(BookieClientImpl.class, times(1))
+            .withArguments(eq(clientConfiguration), eq(nioEventLoopGroup), 
eq(UnpooledByteBufAllocator.DEFAULT),
+                           eq(orderedExecutor), eq(scheduledExecutorService), 
eq(NullStatsLogger.INSTANCE));
+        verify(nioEventLoopGroup, times(1)).shutdownGracefully();
+        verify(orderedExecutor, times(1)).shutdown();
+        verify(bookieClient, times(1)).close();
+    }
+
+}

Reply via email to