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

sijie 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 9524a9f  Migrate command `readjournal`
9524a9f is described below

commit 9524a9f4a82dd990aa437a2931bf2e5541ea0a37
Author: Yong Zhang <[email protected]>
AuthorDate: Fri Mar 29 12:44:24 2019 +0800

    Migrate command `readjournal`
    
    Descriptions of the changes in this PR:
    
    - Use `bkctl` execute `readjournal` command
    
    ### Motivation
    
    - Migrate from bookieshelll
    
    
    
    
    Reviewers: Jia Zhai <[email protected]>, Sijie Guo <[email protected]>
    
    This closes #2000 from zymap/command-readjournal
---
 .../java/org/apache/bookkeeper/bookie/Bookie.java  |   4 +-
 .../org/apache/bookkeeper/bookie/BookieShell.java  | 196 ++-----------------
 .../org/apache/bookkeeper/bookie/LedgerCache.java  |   2 +-
 .../tools/cli/commands/bookie/FormatUtil.java      |  98 ++++++++++
 .../cli/commands/bookie/ReadJournalCommand.java    | 213 +++++++++++++++++++++
 .../tools/cli/commands/BookieCommandGroup.java     |   5 +-
 .../commands/bookie/ReadJournalCommandTest.java    | 128 +++++++++++++
 7 files changed, 466 insertions(+), 180 deletions(-)

diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/Bookie.java 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/Bookie.java
index 44c9509..06ccd58 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/Bookie.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/Bookie.java
@@ -116,8 +116,8 @@ public class Bookie extends BookieCriticalThread {
     final HandleFactory handles;
     final boolean entryLogPerLedgerEnabled;
 
-    static final long METAENTRY_ID_LEDGER_KEY = -0x1000;
-    static final long METAENTRY_ID_FENCE_KEY  = -0x2000;
+    public static final long METAENTRY_ID_LEDGER_KEY = -0x1000;
+    public static final long METAENTRY_ID_FENCE_KEY  = -0x2000;
     public static final long METAENTRY_ID_FORCE_LEDGER  = -0x4000;
     static final long METAENTRY_ID_LEDGER_EXPLICITLAC  = -0x8000;
 
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 fa52bc7..8c448a1 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
@@ -29,7 +29,6 @@ import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.UncheckedExecutionException;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufUtil;
-import io.netty.buffer.Unpooled;
 import io.netty.buffer.UnpooledByteBufAllocator;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
@@ -38,7 +37,6 @@ import java.io.File;
 import java.io.IOException;
 import java.io.Serializable;
 import java.net.URI;
-import java.nio.ByteBuffer;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -51,7 +49,6 @@ import java.util.Base64;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.Formatter;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -71,10 +68,10 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Predicate;
 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.EntryLogger.EntryLogScanner;
-import org.apache.bookkeeper.bookie.Journal.JournalScanner;
 import org.apache.bookkeeper.bookie.storage.ldb.LocationsIndexRebuildOp;
 import org.apache.bookkeeper.client.BKException;
 import org.apache.bookkeeper.client.BKException.MetaStoreException;
@@ -107,10 +104,12 @@ import org.apache.bookkeeper.stats.NullStatsLogger;
 import 
org.apache.bookkeeper.tools.cli.commands.bookie.ConvertToDBStorageCommand;
 import 
org.apache.bookkeeper.tools.cli.commands.bookie.ConvertToInterleavedStorageCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.FormatCommand;
+import org.apache.bookkeeper.tools.cli.commands.bookie.FormatUtil;
 import org.apache.bookkeeper.tools.cli.commands.bookie.InitCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.LastMarkCommand;
 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.ReadJournalCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.SanityTestCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookies.InfoCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookies.ListBookiesCommand;
@@ -122,7 +121,6 @@ import 
org.apache.bookkeeper.tools.cli.commands.cookie.GetCookieCommand;
 import org.apache.bookkeeper.tools.cli.commands.cookie.UpdateCookieCommand;
 import org.apache.bookkeeper.tools.framework.CliFlags;
 import org.apache.bookkeeper.util.BookKeeperConstants;
-import org.apache.bookkeeper.util.DiskChecker;
 import org.apache.bookkeeper.util.EntryFormatter;
 import org.apache.bookkeeper.util.IOUtils;
 import org.apache.bookkeeper.util.LedgerIdFormatter;
@@ -1437,55 +1435,25 @@ public class BookieShell implements Tool {
                 return -1;
             }
 
+            long journalId = -1L;
+            String filename = "";
+            try {
+                journalId = Long.parseLong(leftArgs[0]);
+            } catch (NumberFormatException nfe) {
+                filename = leftArgs[0];
+            }
+
             boolean printMsg = false;
             if (cmdLine.hasOption("m")) {
                 printMsg = true;
             }
 
-            Journal journal = null;
-            if (getJournals().size() > 1) {
-                if (!cmdLine.hasOption("dir")) {
-                    System.err.println("ERROR: invalid or missing journal 
directory");
-                    printUsage();
-                    return -1;
-                }
-
-                File journalDirectory = new 
File(cmdLine.getOptionValue("dir"));
-                for (Journal j : getJournals()) {
-                    if (j.getJournalDirectory().equals(journalDirectory)) {
-                        journal = j;
-                        break;
-                    }
-                }
-
-                if (journal == null) {
-                    System.err.println("ERROR: journal directory not found");
-                    printUsage();
-                    return -1;
-                }
-            } else {
-                journal = getJournals().get(0);
-            }
-
-            long journalId;
-            try {
-                journalId = Long.parseLong(leftArgs[0]);
-            } catch (NumberFormatException nfe) {
-                // not a journal id
-                File f = new File(leftArgs[0]);
-                String name = f.getName();
-                if (!name.endsWith(".txn")) {
-                    // not a journal file
-                    System.err.println("ERROR: invalid journal file name " + 
leftArgs[0]);
-                    printUsage();
-                    return -1;
-                }
-                String idString = name.split("\\.")[0];
-                journalId = Long.parseLong(idString, 16);
-            }
-            // scan journal
-            scanJournal(journal, journalId, printMsg);
-            return 0;
+            ReadJournalCommand.ReadJournalFlags flags = new 
ReadJournalCommand.ReadJournalFlags().msg(printMsg)
+                                                            
.fileName(filename).journalId(journalId)
+                                                            
.dir(cmdLine.getOptionValue("dir"));
+            ReadJournalCommand cmd = new ReadJournalCommand(ledgerIdFormatter, 
entryFormatter);
+            boolean result = cmd.apply(bkConf, flags);
+            return result ? 0 : -1;
         }
 
         @Override
@@ -1626,7 +1594,6 @@ public class BookieShell implements Tool {
         }
     }
 
-
     /**
      * Command to print help message.
      */
@@ -1803,7 +1770,6 @@ public class BookieShell implements Tool {
         }
     }
 
-
     /**
      * Print which node has the auditor lock.
      */
@@ -2493,7 +2459,6 @@ public class BookieShell implements Tool {
         void progress(long updated, long issued);
     }
 
-
     /**
      * Convert bookie indexes from InterleavedStorage to DbLedgerStorage 
format.
      */
@@ -2898,29 +2863,6 @@ public class BookieShell implements Tool {
         entryLogger.scanEntryLog(logId, scanner);
     }
 
-    private synchronized List<Journal> getJournals() throws IOException {
-        if (null == journals) {
-            journals = 
Lists.newArrayListWithCapacity(bkConf.getJournalDirs().length);
-            int idx = 0;
-            for (File journalDir : bkConf.getJournalDirs()) {
-                journals.add(new Journal(idx++, new File(journalDir, 
BookKeeperConstants.CURRENT_DIR), bkConf,
-                    new LedgerDirsManager(bkConf, bkConf.getLedgerDirs(),
-                        new DiskChecker(bkConf.getDiskUsageThreshold(), 
bkConf.getDiskUsageWarnThreshold()))));
-            }
-        }
-        return journals;
-    }
-
-    /**
-     * Scan journal file.
-     *
-     * @param journalId Journal File Id
-     * @param scanner Journal File Scanner
-     */
-    protected void scanJournal(Journal journal, long journalId, JournalScanner 
scanner) throws IOException {
-        journal.scanJournal(journalId, 0L, scanner);
-    }
-
     ///
     /// Bookie Shell Commands
     ///
@@ -2953,7 +2895,7 @@ public class BookieShell implements Tool {
 
             @Override
             public void process(long ledgerId, long startPos, ByteBuf entry) {
-                formatEntry(startPos, entry, printMsg);
+                FormatUtil.formatEntry(startPos, entry, printMsg, 
ledgerIdFormatter, entryFormatter);
             }
         });
     }
@@ -2985,7 +2927,7 @@ public class BookieShell implements Tool {
                 if ((candidateLedgerId == entrysLedgerId) && 
(candidateLedgerId == ledgerId)
                         && ((entrysEntryId == entryId) || (entryId == -1))) {
                     entryFound.setValue(true);
-                    formatEntry(startPos, entry, printMsg);
+                    FormatUtil.formatEntry(startPos, entry, printMsg, 
ledgerIdFormatter, entryFormatter);
                 }
             }
         });
@@ -3034,7 +2976,7 @@ public class BookieShell implements Tool {
                          */
                         long entryEndPos = entryStartPos + entrySize + 4 - 1;
                         if (((rangeEndPos == -1) || (entryStartPos <= 
rangeEndPos)) && (rangeStartPos <= entryEndPos)) {
-                            formatEntry(entryStartPos, entry, printMsg);
+                            FormatUtil.formatEntry(entryStartPos, entry, 
printMsg, ledgerIdFormatter, entryFormatter);
                             entryFound.setValue(true);
                         }
                     }
@@ -3050,40 +2992,6 @@ public class BookieShell implements Tool {
     }
 
     /**
-     * Scan a journal file.
-     *
-     * @param journalId Journal File Id
-     * @param printMsg Whether printing the entry data.
-     */
-    protected void scanJournal(Journal journal, long journalId, final boolean 
printMsg) throws Exception {
-        System.out.println("Scan journal " + journalId + " (" + 
Long.toHexString(journalId) + ".txn)");
-        scanJournal(journal, journalId, new JournalScanner() {
-            boolean printJournalVersion = false;
-
-            @Override
-            public void process(int journalVersion, long offset, ByteBuffer 
entry) throws IOException {
-                if (!printJournalVersion) {
-                    System.out.println("Journal Version : " + journalVersion);
-                    printJournalVersion = true;
-                }
-                formatEntry(offset, Unpooled.wrappedBuffer(entry), printMsg);
-            }
-        });
-    }
-
-    /**
-     * Print last log mark.
-     */
-    protected void printLastLogMark() throws IOException {
-        for (Journal journal : getJournals()) {
-            LogMark lastLogMark = journal.getLastLogMark().getCurMark();
-            System.out.println("LastLogMark: Journal Id - " + 
lastLogMark.getLogFileId() + "("
-                    + Long.toHexString(lastLogMark.getLogFileId()) + ".txn), 
Pos - "
-                    + lastLogMark.getLogFileOffset());
-        }
-    }
-
-    /**
      * Format the entry into a readable format.
      *
      * @param entry
@@ -3102,70 +3010,6 @@ public class BookieShell implements Tool {
         }
     }
 
-    /**
-     * Format the message into a readable format.
-     *
-     * @param pos
-     *          File offset of the message stored in entry log file
-     * @param recBuff
-     *          Entry Data
-     * @param printMsg
-     *          Whether printing the message body
-     */
-    private void formatEntry(long pos, ByteBuf recBuff, boolean printMsg) {
-        int entrySize = recBuff.readableBytes();
-        long ledgerId = recBuff.readLong();
-        long entryId = recBuff.readLong();
-
-        System.out.println("--------- Lid=" + 
ledgerIdFormatter.formatLedgerId(ledgerId) + ", Eid=" + entryId
-                + ", ByteOffset=" + pos + ", EntrySize=" + entrySize + " 
---------");
-        if (entryId == Bookie.METAENTRY_ID_LEDGER_KEY) {
-            int masterKeyLen = recBuff.readInt();
-            byte[] masterKey = new byte[masterKeyLen];
-            recBuff.readBytes(masterKey);
-            System.out.println("Type:           META");
-            System.out.println("MasterKey:      " + bytes2Hex(masterKey));
-            System.out.println();
-            return;
-        }
-        if (entryId == Bookie.METAENTRY_ID_FENCE_KEY) {
-            System.out.println("Type:           META");
-            System.out.println("Fenced");
-            System.out.println();
-            return;
-        }
-        // process a data entry
-        long lastAddConfirmed = recBuff.readLong();
-        System.out.println("Type:           DATA");
-        System.out.println("LastConfirmed:  " + lastAddConfirmed);
-        if (!printMsg) {
-            System.out.println();
-            return;
-        }
-        // skip digest checking
-        recBuff.skipBytes(8);
-        System.out.println("Data:");
-        System.out.println();
-        try {
-            byte[] ret = new byte[recBuff.readableBytes()];
-            recBuff.readBytes(ret);
-            entryFormatter.formatEntry(ret);
-        } catch (Exception e) {
-            System.out.println("N/A. Corrupted.");
-        }
-        System.out.println();
-    }
-
-    static String bytes2Hex(byte[] data) {
-        StringBuilder sb = new StringBuilder(data.length * 2);
-        Formatter formatter = new Formatter(sb);
-        for (byte b : data) {
-            formatter.format("%02x", b);
-        }
-        formatter.close();
-        return sb.toString();
-    }
-
     private static int getOptionIntValue(CommandLine cmdLine, String option, 
int defaultVal) {
         if (cmdLine.hasOption(option)) {
             String val = cmdLine.getOptionValue(option);
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerCache.java 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerCache.java
index 86984b7..993bd0c 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerCache.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/LedgerCache.java
@@ -21,7 +21,7 @@
 
 package org.apache.bookkeeper.bookie;
 
-import static org.apache.bookkeeper.bookie.BookieShell.bytes2Hex;
+import static 
org.apache.bookkeeper.tools.cli.commands.bookie.FormatUtil.bytes2Hex;
 
 import io.netty.buffer.ByteBuf;
 import java.io.Closeable;
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/bookie/FormatUtil.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/bookie/FormatUtil.java
new file mode 100644
index 0000000..d786c7e
--- /dev/null
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/bookie/FormatUtil.java
@@ -0,0 +1,98 @@
+/*
+ * 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 io.netty.buffer.ByteBuf;
+import java.util.Formatter;
+import org.apache.bookkeeper.bookie.Bookie;
+import org.apache.bookkeeper.util.EntryFormatter;
+import org.apache.bookkeeper.util.LedgerIdFormatter;
+
+/**
+ * .Provide to format message.
+ */
+public class FormatUtil {
+
+    /**
+     * Format the message into a readable format.
+     * @param pos
+     *          File offset of the message stored in entry log file
+     * @param recBuff
+     *          Entry Data
+     * @param printMsg
+     *          Whether printing the message body
+     * @param ledgerIdFormatter
+     * @param entryFormatter
+     */
+    public static void formatEntry(long pos, ByteBuf recBuff, boolean 
printMsg, LedgerIdFormatter ledgerIdFormatter,
+                                   EntryFormatter entryFormatter) {
+        int entrySize = recBuff.readableBytes();
+        long ledgerId = recBuff.readLong();
+        long entryId = recBuff.readLong();
+
+        System.out.println(
+            "--------- Lid=" + ledgerIdFormatter.formatLedgerId(ledgerId) + ", 
Eid=" + entryId + ", ByteOffset=" + pos
+                + ", EntrySize=" + entrySize + " ---------");
+        if (entryId == Bookie.METAENTRY_ID_LEDGER_KEY) {
+            int masterKeyLen = recBuff.readInt();
+            byte[] masterKey = new byte[masterKeyLen];
+            recBuff.readBytes(masterKey);
+            System.out.println("Type:           META");
+            System.out.println("MasterKey:      " + bytes2Hex(masterKey));
+            System.out.println();
+            return;
+        }
+        if (entryId == Bookie.METAENTRY_ID_FENCE_KEY) {
+            System.out.println("Type:           META");
+            System.out.println("Fenced");
+            System.out.println();
+            return;
+        }
+        // process a data entry
+        long lastAddConfirmed = recBuff.readLong();
+        System.out.println("Type:           DATA");
+        System.out.println("LastConfirmed:  " + lastAddConfirmed);
+        if (!printMsg) {
+            System.out.println();
+            return;
+        }
+        // skip digest checking
+        recBuff.skipBytes(8);
+        System.out.println("Data:");
+        System.out.println();
+        try {
+            byte[] ret = new byte[recBuff.readableBytes()];
+            recBuff.readBytes(ret);
+            entryFormatter.formatEntry(ret);
+        } catch (Exception e) {
+            System.out.println("N/A. Corrupted.");
+        }
+        System.out.println();
+    }
+
+    public static String bytes2Hex(byte[] data) {
+        StringBuilder sb = new StringBuilder(data.length * 2);
+        Formatter formatter = new Formatter(sb);
+        for (byte b : data) {
+            formatter.format("%02x", b);
+        }
+        formatter.close();
+        return sb.toString();
+    }
+}
diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/bookie/ReadJournalCommand.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/bookie/ReadJournalCommand.java
new file mode 100644
index 0000000..6729257
--- /dev/null
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tools/cli/commands/bookie/ReadJournalCommand.java
@@ -0,0 +1,213 @@
+/*
+ * 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.collect.Lists;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+import io.netty.buffer.Unpooled;
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+import org.apache.bookkeeper.bookie.Journal;
+import org.apache.bookkeeper.bookie.LedgerDirsManager;
+import org.apache.bookkeeper.conf.ServerConfiguration;
+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.BookKeeperConstants;
+import org.apache.bookkeeper.util.DiskChecker;
+import org.apache.bookkeeper.util.EntryFormatter;
+import org.apache.bookkeeper.util.LedgerIdFormatter;
+
+/**
+ * Command to scan a journal file and format the entries into readable format.
+ */
+public class ReadJournalCommand extends 
BookieCommand<ReadJournalCommand.ReadJournalFlags> {
+
+    private static final String NAME = "readjournal";
+    private static final String DESC = "Scan a journal file and format the 
entries into readable format.";
+    private static final long DEFAULT_JOURNALID = -1L;
+    private static final String DEFAULT = "";
+    private LedgerIdFormatter ledgerIdFormatter;
+    private EntryFormatter entryFormatter;
+
+    List<Journal> journals = null;
+
+    public ReadJournalCommand() {
+        this(new ReadJournalFlags());
+    }
+
+    public ReadJournalCommand(LedgerIdFormatter idFormatter, EntryFormatter 
entryFormatter) {
+        this(new ReadJournalFlags());
+        this.ledgerIdFormatter = idFormatter;
+        this.entryFormatter = entryFormatter;
+    }
+
+    ReadJournalCommand(ReadJournalFlags flags) {
+        
super(CliSpec.<ReadJournalFlags>newBuilder().withName(NAME).withDescription(DESC).withFlags(flags).build());
+    }
+
+    /**
+     * Flag for read journal command.
+     */
+    @Accessors(fluent = true)
+    @Setter
+    public static class ReadJournalFlags extends CliFlags {
+
+        @Parameter(names = {"-m", "--msg"}, description = "Print message body")
+        private boolean msg;
+
+        @Parameter(names = { "-d", "--dir" }, description = "Journal directory 
(needed if more than one journal "
+                                                                + 
"configured)")
+        private String dir = DEFAULT;
+
+        @Parameter(names = {"-id", "--journalid"}, description = "Journal Id")
+        private long journalId = DEFAULT_JOURNALID;
+
+        @Parameter(names = {"-f", "--filename"}, description = "Journal file 
name")
+        private String fileName = DEFAULT;
+
+        @Parameter(names = {"-l", "--ledgerIdFormatter"}, description = "Set 
ledger id formatter")
+        private String ledgerIdFormatter = DEFAULT;
+
+        @Parameter(names = {"-e", "--entryformatter"}, description = "set 
entry formatter")
+        private String entryFormatter = DEFAULT;
+
+    }
+
+    @Override
+    public boolean apply(ServerConfiguration conf, ReadJournalFlags cmdFlags) {
+        initTools(conf, cmdFlags);
+        if (!checkArgs(cmdFlags)) {
+            return false;
+        }
+        try {
+            return handler(conf, cmdFlags);
+        } catch (IOException e) {
+            throw new UncheckedExecutionException(e.getMessage(), e);
+        }
+    }
+
+    private void initTools(ServerConfiguration conf, ReadJournalFlags flags) {
+        if (!flags.ledgerIdFormatter.equals(DEFAULT)) {
+            ledgerIdFormatter = 
LedgerIdFormatter.newLedgerIdFormatter(flags.ledgerIdFormatter, conf);
+        } else {
+            ledgerIdFormatter = LedgerIdFormatter.newLedgerIdFormatter(conf);
+        }
+
+        if (!flags.entryFormatter.equals(DEFAULT)) {
+            entryFormatter = 
EntryFormatter.newEntryFormatter(flags.entryFormatter, conf);
+        } else {
+            entryFormatter = EntryFormatter.newEntryFormatter(conf);
+        }
+    }
+    private boolean handler(ServerConfiguration conf, ReadJournalFlags cmd) 
throws IOException {
+        Journal journal = null;
+        if (getJournals(conf).size() > 1) {
+            if (cmd.dir.equals(DEFAULT)) {
+                System.err.println("ERROR: invalid or missing journal 
directory");
+                usage();
+                return false;
+            }
+            File journalDirectory = new File(cmd.dir);
+            for (Journal j : getJournals(conf)) {
+                if (j.getJournalDirectory().equals(journalDirectory)) {
+                    journal = j;
+                    break;
+                }
+            }
+
+            if (journal == null) {
+                System.err.println("ERROR: journal directory not found");
+                usage();
+                return false;
+            }
+        } else {
+            journal = getJournals(conf).get(0);
+        }
+
+        long journalId = cmd.journalId;
+        if (cmd.journalId == DEFAULT_JOURNALID && 
!cmd.fileName.equals(DEFAULT)) {
+            File f = new File(cmd.fileName);
+            String name = f.getName();
+            if (!name.endsWith(".txn")) {
+                System.err.println("ERROR: invalid journal file name " + 
cmd.fileName);
+                usage();
+                return false;
+            }
+            String idString = name.split("\\.")[0];
+            journalId = Long.parseLong(idString);
+        }
+        scanJournal(journal, journalId, cmd.msg);
+        return true;
+    }
+
+    private boolean checkArgs(ReadJournalFlags flags) {
+        if ((flags.fileName.equals(DEFAULT) && flags.journalId == 
DEFAULT_JOURNALID)) {
+            System.out.println("ERROR: You should figure jounalId or journal 
filename");
+            return false;
+        }
+
+        return true;
+    }
+
+    private synchronized List<Journal> getJournals(ServerConfiguration conf) {
+        if (null == journals) {
+            journals = 
Lists.newArrayListWithCapacity(conf.getJournalDirs().length);
+            int idx = 0;
+            for (File journalDir : conf.getJournalDirs()) {
+                journals.add(new Journal(idx++, new File(journalDir, 
BookKeeperConstants.CURRENT_DIR), conf,
+                         new LedgerDirsManager(conf, conf.getLedgerDirs(),
+                               new DiskChecker(conf.getDiskUsageThreshold(), 
conf.getDiskUsageWarnThreshold()))));
+            }
+        }
+        return journals;
+    }
+
+    /**
+      * Scan a journal file.
+      *
+      * @param journalId Journal File Id
+      * @param printMsg Whether printing the entry data.
+      */
+    private void scanJournal(Journal journal, long journalId, final boolean 
printMsg) throws IOException {
+        System.out.println("Scan journal " + journalId + " (" + 
Long.toHexString(journalId) + ".txn)");
+        scanJournal(journal, journalId, new Journal.JournalScanner() {
+            boolean printJournalVersion = false;
+
+            @Override
+            public void process(int journalVersion, long offset, ByteBuffer 
entry) throws IOException {
+                if (!printJournalVersion) {
+                    System.out.println("Journal Version : " + journalVersion);
+                    printJournalVersion = true;
+                }
+                FormatUtil
+                    .formatEntry(offset, Unpooled.wrappedBuffer(entry), 
printMsg, ledgerIdFormatter, entryFormatter);
+            }
+        });
+    }
+
+    private void scanJournal(Journal journal, long journalId, 
Journal.JournalScanner scanner) throws IOException {
+        journal.scanJournal(journalId, 0L, scanner);
+    }
+}
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 9390d2e..c8c7817 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
@@ -28,6 +28,7 @@ import 
org.apache.bookkeeper.tools.cli.commands.bookie.InitCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.LastMarkCommand;
 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.ReadJournalCommand;
 import org.apache.bookkeeper.tools.cli.commands.bookie.SanityTestCommand;
 import org.apache.bookkeeper.tools.common.BKFlags;
 import org.apache.bookkeeper.tools.framework.CliCommandGroup;
@@ -53,7 +54,9 @@ public class BookieCommandGroup extends 
CliCommandGroup<BKFlags> {
         .addCommand(new LedgerCommand())
         .addCommand(new ListFilesOnDiscCommand())
         .addCommand(new ConvertToDBStorageCommand())
-        .addCommand(new ConvertToInterleavedStorageCommand()).build();
+        .addCommand(new ConvertToInterleavedStorageCommand())
+        .addCommand(new ReadJournalCommand())
+        .build();
 
     public BookieCommandGroup() {
         super(spec);
diff --git 
a/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/bookie/ReadJournalCommandTest.java
 
b/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/bookie/ReadJournalCommandTest.java
new file mode 100644
index 0000000..d78519a
--- /dev/null
+++ 
b/tools/ledger/src/test/java/org/apache/bookkeeper/tools/cli/commands/bookie/ReadJournalCommandTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+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 static org.powermock.api.mockito.PowerMockito.whenNew;
+
+import java.io.File;
+import org.apache.bookkeeper.bookie.Journal;
+import org.apache.bookkeeper.bookie.LedgerDirsManager;
+import org.apache.bookkeeper.conf.ServerConfiguration;
+import org.apache.bookkeeper.tools.cli.helpers.BookieCommandTestBase;
+import org.apache.bookkeeper.util.DiskChecker;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+
+/**
+ * Unit test for read journal command.
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ ReadJournalCommand.class, Journal.class, 
LedgerDirsManager.class,
+    DiskChecker.class })
+public class ReadJournalCommandTest extends BookieCommandTestBase {
+
+    private Journal journal;
+
+    @Rule
+    private TemporaryFolder folder = new TemporaryFolder();
+
+    public ReadJournalCommandTest() {
+        super(3, 0);
+    }
+
+    @Override
+    public void setup() throws Exception {
+        super.setup();
+        whenNew(ServerConfiguration.class).withNoArguments().thenReturn(conf);
+
+        DiskChecker checker = mock(DiskChecker.class);
+        
whenNew(DiskChecker.class).withArguments(eq(conf.getDiskUsageThreshold()), 
eq(conf.getDiskUsageWarnThreshold()))
+            .thenReturn(checker);
+        LedgerDirsManager ledgerDirsManager = mock(LedgerDirsManager.class);
+        whenNew(LedgerDirsManager.class).withArguments(eq(conf), 
eq(conf.getLedgerDirs()), eq(checker))
+            .thenReturn(ledgerDirsManager);
+        journal = mock(Journal.class);
+        whenNew(Journal.class).withArguments(anyInt(), any(File.class), 
eq(conf), eq(ledgerDirsManager))
+            .thenReturn(journal);
+        
when(journal.getJournalDirectory()).thenReturn(conf.getJournalDirs()[0]);
+    }
+
+    @Test
+    public void testWithJournalId() throws Exception {
+        conf.setJournalDirsName(new String[] { 
folder.getRoot().getAbsolutePath() });
+        when(journal.getJournalDirectory()).thenReturn(new File(""));
+        testCommand("-id", "1");
+        verifyNew(Journal.class, times(1))
+            .withArguments(anyInt(), any(File.class), eq(conf), 
any(LedgerDirsManager.class));
+        verifyNew(LedgerDirsManager.class, times(1))
+            .withArguments(eq(conf), eq(conf.getLedgerDirs()), 
any(DiskChecker.class));
+        verifyNew(DiskChecker.class, times(1))
+            .withArguments(eq(conf.getDiskUsageThreshold()), 
eq(conf.getDiskUsageWarnThreshold()));
+    }
+
+    @Test
+    public void testWithFilename() throws Exception {
+        conf.setJournalDirsName(new String[] { 
folder.getRoot().getAbsolutePath() });
+        when(journal.getJournalDirectory()).thenReturn(new File(""));
+        File file = folder.newFile("1.txn");
+        testCommand("-f", file.getAbsolutePath(), "-m");
+        verifyNew(Journal.class, times(1))
+            .withArguments(anyInt(), any(File.class), eq(conf), 
any(LedgerDirsManager.class));
+        verifyNew(LedgerDirsManager.class, times(1))
+            .withArguments(eq(conf), eq(conf.getLedgerDirs()), 
any(DiskChecker.class));
+        verifyNew(DiskChecker.class, times(1))
+            .withArguments(eq(conf.getDiskUsageThreshold()), 
eq(conf.getDiskUsageWarnThreshold()));
+    }
+
+    @Test
+    public void testWithMsg() throws Exception {
+        testCommand("-id", "1", "-d", 
conf.getJournalDirs()[0].getAbsolutePath());
+        verifyNew(Journal.class, times(3))
+            .withArguments(anyInt(), any(File.class), eq(conf), 
any(LedgerDirsManager.class));
+        verifyNew(LedgerDirsManager.class, times(3))
+            .withArguments(eq(conf), eq(conf.getLedgerDirs()), 
any(DiskChecker.class));
+        verifyNew(DiskChecker.class, times(3))
+            .withArguments(eq(conf.getDiskUsageThreshold()), 
eq(conf.getDiskUsageWarnThreshold()));
+        verify(journal, times(1)).getJournalDirectory();
+    }
+
+    public void testCommand(String... args) throws Exception {
+        ReadJournalCommand command = new ReadJournalCommand();
+        Assert.assertTrue(command.apply(bkFlags, args));
+    }
+
+    @Test
+    public void testWithoutArgs() {
+        ReadJournalCommand command = new ReadJournalCommand();
+        Assert.assertFalse(command.apply(bkFlags, new String[] { "" }));
+    }
+}

Reply via email to