You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by br...@apache.org on 2021/03/20 02:16:55 UTC

[hbase] branch branch-1 updated: HBASE-25258 Backport HBASE-24776 "[hbtop] Support Batch mode" to branch-1 (#3065)

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

brfrn169 pushed a commit to branch branch-1
in repository https://gitbox.apache.org/repos/asf/hbase.git


The following commit(s) were added to refs/heads/branch-1 by this push:
     new f807800  HBASE-25258 Backport HBASE-24776 "[hbtop] Support Batch mode" to branch-1 (#3065)
f807800 is described below

commit f8078009e9e8cadee76fd38d1e35ea6812b866de
Author: Toshihiro Suzuki <br...@gmail.com>
AuthorDate: Sat Mar 20 11:16:26 2021 +0900

    HBASE-25258 Backport HBASE-24776 "[hbtop] Support Batch mode" to branch-1 (#3065)
    
    Signed-off-by: stack stack@apache.org
---
 .../java/org/apache/hadoop/hbase/hbtop/HBTop.java  | 169 ++++++++++++++++++---
 .../hbase/hbtop/screen/AbstractScreenView.java     |   1 +
 .../apache/hadoop/hbase/hbtop/screen/Screen.java   |  26 +++-
 .../hbase/hbtop/screen/top/TopScreenModel.java     |  44 ++++--
 .../hbase/hbtop/screen/top/TopScreenPresenter.java |  46 ++++--
 .../hbase/hbtop/screen/top/TopScreenView.java      |  48 ++++--
 .../hadoop/hbase/hbtop/terminal/Terminal.java      |   2 +-
 .../hbtop/terminal/impl/batch/BatchTerminal.java   |  80 ++++++++++
 .../batch/BatchTerminalPrinter.java}               |  52 ++++---
 .../hbase/hbtop/screen/top/TestTopScreenModel.java |  16 +-
 .../hbtop/screen/top/TestTopScreenPresenter.java   |   3 +-
 11 files changed, 391 insertions(+), 96 deletions(-)

diff --git a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/HBTop.java b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/HBTop.java
index ac05bb2..b0f4710 100644
--- a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/HBTop.java
+++ b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/HBTop.java
@@ -17,6 +17,8 @@
  */
 package org.apache.hadoop.hbase.hbtop;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 import org.apache.commons.cli.BasicParser;
@@ -30,6 +32,8 @@ import org.apache.hadoop.conf.Configured;
 import org.apache.hadoop.hbase.HBaseConfiguration;
 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
 import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.hbtop.field.Field;
+import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
 import org.apache.hadoop.hbase.hbtop.mode.Mode;
 import org.apache.hadoop.hbase.hbtop.screen.Screen;
 import org.apache.hadoop.util.Tool;
@@ -55,17 +59,14 @@ public class HBTop extends Configured implements Tool {
   public int run(String[] args) throws Exception {
     long initialRefreshDelay = 3 * 1000;
     Mode initialMode = Mode.REGION;
+    List<Field> initialFields = null;
+    Field initialSortField = null;
+    Boolean initialAscendingSort = null;
+    List<RecordFilter> initialFilters = null;
+    long numberOfIterations = Long.MAX_VALUE;
+    boolean batchMode = false;
     try {
-      // Command line options
-      Options opts = new Options();
-      opts.addOption("h", "help", false,
-        "Print usage; for help while the tool is running press 'h'");
-      opts.addOption("d", "delay", true,
-        "The refresh delay (in seconds); default is 3 seconds");
-      opts.addOption("m", "mode", true,
-        "The mode; n (Namespace)|t (Table)|r (Region)|s (RegionServer)"
-          + ", default is r (Region)");
-
+      Options opts = getOptions();
       CommandLine commandLine = new BasicParser().parse(opts, args);
 
       if (commandLine.hasOption("help")) {
@@ -73,20 +74,6 @@ public class HBTop extends Configured implements Tool {
         return 0;
       }
 
-      if (commandLine.hasOption("delay")) {
-        int delay = 0;
-        try {
-          delay = Integer.parseInt(commandLine.getOptionValue("delay"));
-        } catch (NumberFormatException ignored) {
-        }
-
-        if (delay < 1) {
-          LOG.warn("Delay set too low or invalid, using default");
-        } else {
-          initialRefreshDelay = delay * 1000L;
-        }
-      }
-
       if (commandLine.hasOption("mode")) {
         String mode = commandLine.getOptionValue("mode");
         switch (mode) {
@@ -111,18 +98,150 @@ public class HBTop extends Configured implements Tool {
             break;
         }
       }
+
+      if (commandLine.hasOption("outputFieldNames")) {
+        for (FieldInfo fieldInfo : initialMode.getFieldInfos()) {
+          System.out.println(fieldInfo.getField().getHeader());
+        }
+        return 0;
+      }
+
+      if (commandLine.hasOption("delay")) {
+        int delay = 0;
+        try {
+          delay = Integer.parseInt(commandLine.getOptionValue("delay"));
+        } catch (NumberFormatException ignored) {
+        }
+
+        if (delay < 1) {
+          LOG.warn("Delay set too low or invalid, using default");
+        } else {
+          initialRefreshDelay = delay * 1000L;
+        }
+      }
+
+      if (commandLine.hasOption("numberOfIterations")) {
+        try {
+          numberOfIterations = Long.parseLong(commandLine.getOptionValue("numberOfIterations"));
+        } catch (NumberFormatException ignored) {
+          LOG.warn("The number of iterations set invalid, ignoring");
+        }
+      }
+
+      if (commandLine.hasOption("sortField")) {
+        String sortField = commandLine.getOptionValue("sortField");
+
+        String field;
+        boolean ascendingSort;
+        if (sortField.startsWith("+")) {
+          field = sortField.substring(1);
+          ascendingSort = false;
+        } else if (sortField.startsWith("-")) {
+          field = sortField.substring(1);
+          ascendingSort = true;
+        } else {
+          field = sortField;
+          ascendingSort = false;
+        }
+
+        FieldInfo fieldInfo = null;
+        for (FieldInfo info : initialMode.getFieldInfos()) {
+          if (info.getField().getHeader().equals(field)) {
+            fieldInfo = info;
+            break;
+          }
+        }
+        if (fieldInfo != null) {
+          initialSortField = fieldInfo.getField();
+          initialAscendingSort = ascendingSort;
+        } else {
+          LOG.warn("The specified sort field " + field + " is not found, using default");
+        }
+      }
+
+      if (commandLine.hasOption("fields")) {
+        String[] fields = commandLine.getOptionValue("fields").split(",");
+        initialFields = new ArrayList<>();
+        for (String field : fields) {
+          FieldInfo fieldInfo = null;
+          for (FieldInfo info : initialMode.getFieldInfos()) {
+            if (info.getField().getHeader().equals(field)) {
+              fieldInfo = info;
+              break;
+            }
+          }
+          if (fieldInfo != null) {
+            initialFields.add(fieldInfo.getField());
+          } else {
+            LOG.warn("The specified field " + field + " is not found, ignoring");
+          }
+        }
+      }
+
+      if (commandLine.hasOption("filters")) {
+        String[] filters = commandLine.getOptionValue("filters").split(",");
+
+        List<Field> fields = new ArrayList<>();
+        for (FieldInfo fieldInfo : initialMode.getFieldInfos()) {
+          fields.add(fieldInfo.getField());
+        }
+
+        for (String filter : filters) {
+          RecordFilter f = RecordFilter.parse(filter, fields, false);
+          if (f != null) {
+            if (initialFilters == null) {
+              initialFilters = new ArrayList<>();
+            }
+            initialFilters.add(f);
+          } else {
+            LOG.warn("The specified filter " + filter + " is invalid, ignoring");
+          }
+        }
+      }
+
+      if (commandLine.hasOption("batchMode")) {
+        batchMode = true;
+      }
     } catch (Exception e) {
       LOG.error("Unable to parse options", e);
       return 1;
     }
 
-    try (Screen screen = new Screen(getConf(), initialRefreshDelay, initialMode)) {
+    try (Screen screen = new Screen(getConf(), initialRefreshDelay, initialMode, initialFields,
+      initialSortField, initialAscendingSort, initialFilters, numberOfIterations, batchMode)) {
       screen.run();
     }
 
     return 0;
   }
 
+  private Options getOptions() {
+    Options opts = new Options();
+    opts.addOption("h", "help", false,
+      "Print usage; for help while the tool is running press 'h'");
+    opts.addOption("d", "delay", true,
+      "The refresh delay (in seconds); default is 3 seconds");
+    opts.addOption("m", "mode", true,
+      "The mode; n (Namespace)|t (Table)|r (Region)|s (RegionServer), default is r");
+    opts.addOption("n", "numberOfIterations", true,
+      "The number of iterations");
+    opts.addOption("s", "sortField", true,
+      "The initial sort field. You can prepend a `+' or `-' to the field name to also override"
+        + " the sort direction. A leading `+' will force sorting high to low, whereas a `-' will"
+        + " ensure a low to high ordering");
+    opts.addOption("O", "outputFieldNames", false,
+      "Print each of the available field names on a separate line, then quit");
+    opts.addOption("f", "fields", true,
+      "Show only the given fields. Specify comma separated fields to show multiple fields");
+    opts.addOption("i", "filters", true,
+      "The initial filters. Specify comma separated filters to set multiple filters");
+    opts.addOption("b", "batchMode", false,
+      "Starts hbtop in Batch mode, which could be useful for sending output from hbtop to other"
+        + " programs or to a file. In this mode, hbtop will not accept input and runs until the"
+        + " iterations limit you've set with the `-n' command-line option or until killed");
+    return opts;
+  }
+
   private void printUsage(Options opts) {
     new HelpFormatter().printHelp("hbase hbtop [opts] [-D<property=value>]*", opts);
     System.out.println("");
diff --git a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/AbstractScreenView.java b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/AbstractScreenView.java
index 32dfb53..8c885a4 100644
--- a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/AbstractScreenView.java
+++ b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/AbstractScreenView.java
@@ -75,6 +75,7 @@ public abstract class AbstractScreenView implements ScreenView {
     return terminal.getTerminalPrinter(startRow);
   }
 
+  @Nullable
   protected TerminalSize getTerminalSize() {
     return terminal.getSize();
   }
diff --git a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/Screen.java b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/Screen.java
index bbcbba2..7629052 100644
--- a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/Screen.java
+++ b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/Screen.java
@@ -17,8 +17,10 @@
  */
 package org.apache.hadoop.hbase.hbtop.screen;
 
+import edu.umd.cs.findbugs.annotations.Nullable;
 import java.io.Closeable;
 import java.io.IOException;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.commons.logging.Log;
@@ -28,11 +30,14 @@ import org.apache.hadoop.hbase.classification.InterfaceAudience;
 import org.apache.hadoop.hbase.client.Admin;
 import org.apache.hadoop.hbase.client.Connection;
 import org.apache.hadoop.hbase.client.ConnectionFactory;
+import org.apache.hadoop.hbase.hbtop.RecordFilter;
+import org.apache.hadoop.hbase.hbtop.field.Field;
 import org.apache.hadoop.hbase.hbtop.mode.Mode;
 import org.apache.hadoop.hbase.hbtop.screen.top.TopScreenView;
 import org.apache.hadoop.hbase.hbtop.terminal.KeyPress;
 import org.apache.hadoop.hbase.hbtop.terminal.Terminal;
 import org.apache.hadoop.hbase.hbtop.terminal.impl.TerminalImpl;
+import org.apache.hadoop.hbase.hbtop.terminal.impl.batch.BatchTerminal;
 
 /**
  * This dispatches key presses and timers to the current {@link ScreenView}.
@@ -51,15 +56,23 @@ public class Screen implements Closeable {
   private ScreenView currentScreenView;
   private Long timerTimestamp;
 
-  public Screen(Configuration conf, long initialRefreshDelay, Mode initialMode)
+  public Screen(Configuration conf, long initialRefreshDelay, Mode initialMode,
+    @Nullable List<Field> initialFields, @Nullable Field initialSortField,
+    @Nullable Boolean initialAscendingSort, @Nullable List<RecordFilter> initialFilters,
+    long numberOfIterations, boolean batchMode)
     throws IOException {
     connection = ConnectionFactory.createConnection(conf);
     admin = connection.getAdmin();
 
     // The first screen is the top screen
-    this.terminal = new TerminalImpl("hbtop");
+    if (batchMode) {
+      terminal = new BatchTerminal();
+    } else {
+      terminal = new TerminalImpl("hbtop");
+    }
     currentScreenView = new TopScreenView(this, terminal, initialRefreshDelay, admin,
-      initialMode);
+      initialMode, initialFields, initialSortField, initialAscendingSort, initialFilters,
+      numberOfIterations);
   }
 
   @Override
@@ -93,11 +106,8 @@ public class Screen implements Closeable {
               timerTimestamp = null;
               nextScreenView = currentScreenView.handleTimer();
             } else {
-              if (timerTimestamp - now < SLEEP_TIMEOUT_MILLISECONDS) {
-                TimeUnit.MILLISECONDS.sleep(timerTimestamp - now);
-              } else {
-                TimeUnit.MILLISECONDS.sleep(SLEEP_TIMEOUT_MILLISECONDS);
-              }
+              TimeUnit.MILLISECONDS
+                .sleep(Math.min(timerTimestamp - now, SLEEP_TIMEOUT_MILLISECONDS));
               continue;
             }
           } else {
diff --git a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/top/TopScreenModel.java b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/top/TopScreenModel.java
index f795c4a..05780da 100644
--- a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/top/TopScreenModel.java
+++ b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/top/TopScreenModel.java
@@ -17,6 +17,7 @@
  */
 package org.apache.hadoop.hbase.hbtop.screen.top;
 
+import edu.umd.cs.findbugs.annotations.Nullable;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -61,20 +62,34 @@ public class TopScreenModel {
 
   private boolean ascendingSort;
 
-  public TopScreenModel(Admin admin, Mode initialMode) {
+  public TopScreenModel(Admin admin, Mode initialMode, @Nullable List<Field> initialFields,
+    @Nullable Field initialSortField, @Nullable Boolean initialAscendingSort,
+    @Nullable List<RecordFilter> initialFilters) {
     this.admin = Objects.requireNonNull(admin);
-    switchMode(Objects.requireNonNull(initialMode), null, false);
+    switchMode(Objects.requireNonNull(initialMode), initialSortField, false, initialFields,
+      initialAscendingSort, initialFilters);
   }
 
-  public void switchMode(Mode nextMode, List<RecordFilter> initialFilters,
-    boolean keepSortFieldAndSortOrderIfPossible) {
+  public void switchMode(Mode nextMode, boolean keepSortFieldAndSortOrderIfPossible,
+    List<RecordFilter> initialFilters) {
+    switchMode(nextMode, null, keepSortFieldAndSortOrderIfPossible, null, null, initialFilters);
+  }
 
+  public void switchMode(Mode nextMode, Field initialSortField,
+    boolean keepSortFieldAndSortOrderIfPossible, @Nullable List<Field> initialFields,
+    @Nullable Boolean initialAscendingSort, @Nullable List<RecordFilter> initialFilters) {
     currentMode = nextMode;
     fieldInfos = Collections.unmodifiableList(new ArrayList<>(currentMode.getFieldInfos()));
 
     fields = new ArrayList<>();
     for (FieldInfo fieldInfo : currentMode.getFieldInfos()) {
-      fields.add(fieldInfo.getField());
+      if (initialFields != null) {
+        if (initialFields.contains(fieldInfo.getField())) {
+          fields.add(fieldInfo.getField());
+        }
+      } else {
+        fields.add(fieldInfo.getField());
+      }
     }
     fields = Collections.unmodifiableList(fields);
 
@@ -88,13 +103,22 @@ public class TopScreenModel {
       }
 
       if (!match) {
+        if (initialSortField != null && initialAscendingSort != null) {
+          currentSortField = initialSortField;
+          ascendingSort = initialAscendingSort;
+        } else {
+          currentSortField = nextMode.getDefaultSortField();
+          ascendingSort = false;
+        }
+      }
+    } else {
+      if (initialSortField != null && initialAscendingSort != null) {
+        currentSortField = initialSortField;
+        ascendingSort = initialAscendingSort;
+      } else {
         currentSortField = nextMode.getDefaultSortField();
         ascendingSort = false;
       }
-
-    } else {
-      currentSortField = nextMode.getDefaultSortField();
-      ascendingSort = false;
     }
 
     clearFilters();
@@ -197,7 +221,7 @@ public class TopScreenModel {
     if (drillDownInfo == null) {
       return false;
     }
-    switchMode(drillDownInfo.getNextMode(), drillDownInfo.getInitialFilters(), true);
+    switchMode(drillDownInfo.getNextMode(), true, drillDownInfo.getInitialFilters());
     return true;
   }
 
diff --git a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/top/TopScreenPresenter.java b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/top/TopScreenPresenter.java
index 02c35b8..266166c 100644
--- a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/top/TopScreenPresenter.java
+++ b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/top/TopScreenPresenter.java
@@ -17,6 +17,7 @@
  */
 package org.apache.hadoop.hbase.hbtop.screen.top;
 
+import edu.umd.cs.findbugs.annotations.Nullable;
 import java.util.ArrayList;
 import java.util.EnumMap;
 import java.util.List;
@@ -56,21 +57,38 @@ public class TopScreenPresenter {
   private final EnumMap<Field, Boolean> fieldDisplayMap = new EnumMap<>(Field.class);
   private final EnumMap<Field, Integer> fieldLengthMap = new EnumMap<>(Field.class);
 
+  private final long numberOfIterations;
+  private long iterations;
+
   public TopScreenPresenter(TopScreenView topScreenView, long initialRefreshDelay,
-    TopScreenModel topScreenModel) {
+    TopScreenModel topScreenModel, @Nullable List<Field> initialFields, long numberOfIterations) {
     this.topScreenView = Objects.requireNonNull(topScreenView);
     this.refreshDelay = new AtomicLong(initialRefreshDelay);
     this.topScreenModel = Objects.requireNonNull(topScreenModel);
+    this.numberOfIterations = numberOfIterations;
 
-    initFieldDisplayMapAndFieldLengthMap();
+    initFieldDisplayMapAndFieldLengthMap(initialFields);
   }
 
   public void init() {
-    terminalLength = topScreenView.getTerminalSize().getColumns();
-    paging.updatePageSize(topScreenView.getPageSize());
+    updateTerminalLengthAndPageSize(topScreenView.getTerminalSize(), topScreenView.getPageSize());
     topScreenView.hideCursor();
   }
 
+  private void updateTerminalLengthAndPageSize(@Nullable TerminalSize terminalSize,
+    @Nullable Integer pageSize) {
+    if (terminalSize != null) {
+      terminalLength = terminalSize.getColumns();
+    } else {
+      terminalLength = Integer.MAX_VALUE;
+    }
+    if (pageSize != null) {
+      paging.updatePageSize(pageSize);
+    } else {
+      paging.updatePageSize(Integer.MAX_VALUE);
+    }
+  }
+
   public long refresh(boolean force) {
     if (!force) {
       long delay = System.currentTimeMillis() - lastRefreshTimestamp;
@@ -81,8 +99,7 @@ public class TopScreenPresenter {
 
     TerminalSize newTerminalSize = topScreenView.doResizeIfNecessary();
     if (newTerminalSize != null) {
-      terminalLength = newTerminalSize.getColumns();
-      paging.updatePageSize(topScreenView.getPageSize());
+      updateTerminalLengthAndPageSize(newTerminalSize, topScreenView.getPageSize());
       topScreenView.clearTerminal();
     }
 
@@ -97,6 +114,7 @@ public class TopScreenPresenter {
     topScreenView.refreshTerminal();
 
     lastRefreshTimestamp = System.currentTimeMillis();
+    iterations++;
     return refreshDelay.get();
   }
 
@@ -260,7 +278,7 @@ public class TopScreenPresenter {
   }
 
   private void switchMode(Mode nextMode) {
-    topScreenModel.switchMode(nextMode, null, false);
+    topScreenModel.switchMode(nextMode, false, null);
     reset();
   }
 
@@ -276,18 +294,22 @@ public class TopScreenPresenter {
   }
 
   private void reset() {
-    initFieldDisplayMapAndFieldLengthMap();
+    initFieldDisplayMapAndFieldLengthMap(null);
     adjustFieldLength.set(true);
     paging.init();
     horizontalScroll = 0;
     topScreenView.clearTerminal();
   }
 
-  private void initFieldDisplayMapAndFieldLengthMap() {
+  private void initFieldDisplayMapAndFieldLengthMap(@Nullable List<Field> initialFields) {
     fieldDisplayMap.clear();
     fieldLengthMap.clear();
     for (FieldInfo fieldInfo : topScreenModel.getFieldInfos()) {
-      fieldDisplayMap.put(fieldInfo.getField(), fieldInfo.isDisplayByDefault());
+      if (initialFields != null) {
+        fieldDisplayMap.put(fieldInfo.getField(), initialFields.contains(fieldInfo.getField()));
+      } else {
+        fieldDisplayMap.put(fieldInfo.getField(), fieldInfo.isDisplayByDefault());
+      }
       fieldLengthMap.put(fieldInfo.getField(), fieldInfo.getDefaultLength());
     }
   }
@@ -353,4 +375,8 @@ public class TopScreenPresenter {
     return new FilterDisplayModeScreenView(screen, terminal, row, topScreenModel.getFilters(),
       topScreenView);
   }
+
+  public boolean isIterationFinished() {
+    return iterations >= numberOfIterations;
+  }
 }
diff --git a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/top/TopScreenView.java b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/top/TopScreenView.java
index 6d9348b..b24b4df 100644
--- a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/top/TopScreenView.java
+++ b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/screen/top/TopScreenView.java
@@ -25,6 +25,8 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.hadoop.hbase.classification.InterfaceAudience;
 import org.apache.hadoop.hbase.client.Admin;
 import org.apache.hadoop.hbase.hbtop.Record;
+import org.apache.hadoop.hbase.hbtop.RecordFilter;
+import org.apache.hadoop.hbase.hbtop.field.Field;
 import org.apache.hadoop.hbase.hbtop.mode.Mode;
 import org.apache.hadoop.hbase.hbtop.screen.AbstractScreenView;
 import org.apache.hadoop.hbase.hbtop.screen.Screen;
@@ -50,13 +52,16 @@ public class TopScreenView extends AbstractScreenView {
   private static final int RECORD_START_ROW = 9;
 
   private final TopScreenPresenter topScreenPresenter;
-  private int pageSize;
+  private Integer pageSize;
 
   public TopScreenView(Screen screen, Terminal terminal, long initialRefreshDelay, Admin admin,
-    Mode initialMode) {
+    Mode initialMode, @Nullable List<Field> initialFields, @Nullable Field initialSortField,
+    @Nullable Boolean initialAscendingSort, @Nullable List<RecordFilter> initialFilters,
+    long numberOfIterations) {
     super(screen, terminal);
     this.topScreenPresenter = new TopScreenPresenter(this, initialRefreshDelay,
-      new TopScreenModel(admin, initialMode));
+      new TopScreenModel(admin, initialMode, initialFields, initialSortField,
+        initialAscendingSort, initialFilters), initialFields, numberOfIterations);
   }
 
   @Override
@@ -66,11 +71,12 @@ public class TopScreenView extends AbstractScreenView {
     setTimer(delay);
   }
 
+  @Nullable
   @Override
   public ScreenView handleTimer() {
     long delay = topScreenPresenter.refresh(false);
     setTimer(delay);
-    return this;
+    return topScreenPresenter.isIterationFinished() ? null : this;
   }
 
   @Nullable
@@ -79,39 +85,39 @@ public class TopScreenView extends AbstractScreenView {
     switch (keyPress.getType()) {
       case Enter:
         topScreenPresenter.refresh(true);
-        return this;
+        return topScreenPresenter.isIterationFinished() ? null : this;
 
       case ArrowUp:
         topScreenPresenter.arrowUp();
-        return this;
+        return topScreenPresenter.isIterationFinished() ? null : this;
 
       case ArrowDown:
         topScreenPresenter.arrowDown();
-        return this;
+        return topScreenPresenter.isIterationFinished() ? null : this;
 
       case ArrowLeft:
         topScreenPresenter.arrowLeft();
-        return this;
+        return topScreenPresenter.isIterationFinished() ? null : this;
 
       case ArrowRight:
         topScreenPresenter.arrowRight();
-        return this;
+        return topScreenPresenter.isIterationFinished() ? null : this;
 
       case PageUp:
         topScreenPresenter.pageUp();
-        return this;
+        return topScreenPresenter.isIterationFinished() ? null : this;
 
       case PageDown:
         topScreenPresenter.pageDown();
-        return this;
+        return topScreenPresenter.isIterationFinished() ? null : this;
 
       case Home:
         topScreenPresenter.home();
-        return this;
+        return topScreenPresenter.isIterationFinished() ? null : this;
 
       case End:
         topScreenPresenter.end();
-        return this;
+        return topScreenPresenter.isIterationFinished() ? null : this;
 
       case Escape:
         return null;
@@ -182,13 +188,18 @@ public class TopScreenView extends AbstractScreenView {
     return this;
   }
 
+  @Nullable
   @Override
   public TerminalSize getTerminalSize() {
     TerminalSize terminalSize = super.getTerminalSize();
+    if (terminalSize == null) {
+      return null;
+    }
     updatePageSize(terminalSize);
     return terminalSize;
   }
 
+  @Nullable
   @Override
   public TerminalSize doResizeIfNecessary() {
     TerminalSize terminalSize = super.doResizeIfNecessary();
@@ -206,7 +217,8 @@ public class TopScreenView extends AbstractScreenView {
     }
   }
 
-  public int getPageSize() {
+  @Nullable
+  public Integer getPageSize() {
     return pageSize;
   }
 
@@ -244,8 +256,14 @@ public class TopScreenView extends AbstractScreenView {
 
   private void showRecords(List<Header> headers, List<Record> records, Record selectedRecord) {
     TerminalPrinter printer = getTerminalPrinter(RECORD_START_ROW);
+    int size;
+    if (pageSize != null) {
+      size = pageSize;
+    } else {
+      size = records.size();
+    }
     List<String> buf = new ArrayList<>(headers.size());
-    for (int i = 0; i < pageSize; i++) {
+    for (int i = 0; i < size; i++) {
       if(i < records.size()) {
         Record record = records.get(i);
         buf.clear();
diff --git a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/terminal/Terminal.java b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/terminal/Terminal.java
index 8da71af..44c77d5 100644
--- a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/terminal/Terminal.java
+++ b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/terminal/Terminal.java
@@ -29,7 +29,7 @@ import org.apache.hadoop.hbase.classification.InterfaceAudience;
 public interface Terminal extends Closeable {
   void clear();
   void refresh();
-  TerminalSize getSize();
+  @Nullable TerminalSize getSize();
   @Nullable TerminalSize doResizeIfNecessary();
   @Nullable KeyPress pollKeyPress();
   CursorPosition getCursorPosition();
diff --git a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/terminal/impl/batch/BatchTerminal.java b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/terminal/impl/batch/BatchTerminal.java
new file mode 100644
index 0000000..19d6426
--- /dev/null
+++ b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/terminal/impl/batch/BatchTerminal.java
@@ -0,0 +1,80 @@
+/**
+ * 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.hadoop.hbase.hbtop.terminal.impl.batch;
+
+import edu.umd.cs.findbugs.annotations.Nullable;
+import org.apache.hadoop.hbase.hbtop.terminal.CursorPosition;
+import org.apache.hadoop.hbase.hbtop.terminal.KeyPress;
+import org.apache.hadoop.hbase.hbtop.terminal.Terminal;
+import org.apache.hadoop.hbase.hbtop.terminal.TerminalPrinter;
+import org.apache.hadoop.hbase.hbtop.terminal.TerminalSize;
+
+public class BatchTerminal implements Terminal {
+
+  private static final TerminalPrinter TERMINAL_PRINTER = new BatchTerminalPrinter();
+
+  @Override
+  public void clear() {
+  }
+
+  @Override
+  public void refresh() {
+    // Add a new line
+    TERMINAL_PRINTER.endOfLine();
+  }
+
+  @Nullable
+  @Override
+  public TerminalSize getSize() {
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public TerminalSize doResizeIfNecessary() {
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public KeyPress pollKeyPress() {
+    return null;
+  }
+
+  @Override
+  public CursorPosition getCursorPosition() {
+    return null;
+  }
+
+  @Override
+  public void setCursorPosition(int column, int row) {
+  }
+
+  @Override
+  public void hideCursor() {
+  }
+
+  @Override
+  public TerminalPrinter getTerminalPrinter(int startRow) {
+    return TERMINAL_PRINTER;
+  }
+
+  @Override
+  public void close() {
+  }
+}
diff --git a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/terminal/Terminal.java b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/terminal/impl/batch/BatchTerminalPrinter.java
similarity index 52%
copy from hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/terminal/Terminal.java
copy to hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/terminal/impl/batch/BatchTerminalPrinter.java
index 8da71af..afae5e8 100644
--- a/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/terminal/Terminal.java
+++ b/hbase-hbtop/src/main/java/org/apache/hadoop/hbase/hbtop/terminal/impl/batch/BatchTerminalPrinter.java
@@ -15,25 +15,41 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.hadoop.hbase.hbtop.terminal;
+package org.apache.hadoop.hbase.hbtop.terminal.impl.batch;
 
-import edu.umd.cs.findbugs.annotations.Nullable;
-import java.io.Closeable;
+import org.apache.hadoop.hbase.hbtop.terminal.AbstractTerminalPrinter;
+import org.apache.hadoop.hbase.hbtop.terminal.TerminalPrinter;
 
-import org.apache.hadoop.hbase.classification.InterfaceAudience;
+public class BatchTerminalPrinter extends AbstractTerminalPrinter {
 
-/**
- * The terminal interface that is an abstraction of terminal screen.
- */
-@InterfaceAudience.Private
-public interface Terminal extends Closeable {
-  void clear();
-  void refresh();
-  TerminalSize getSize();
-  @Nullable TerminalSize doResizeIfNecessary();
-  @Nullable KeyPress pollKeyPress();
-  CursorPosition getCursorPosition();
-  void setCursorPosition(int column, int row);
-  void hideCursor();
-  TerminalPrinter getTerminalPrinter(int startRow);
+  @Override
+  public TerminalPrinter print(String value) {
+    System.out.print(value);
+    return this;
+  }
+
+  @Override
+  public TerminalPrinter startHighlight() {
+    return this;
+  }
+
+  @Override
+  public TerminalPrinter stopHighlight() {
+    return this;
+  }
+
+  @Override
+  public TerminalPrinter startBold() {
+    return this;
+  }
+
+  @Override
+  public TerminalPrinter stopBold() {
+    return this;
+  }
+
+  @Override
+  public void endOfLine() {
+    System.out.println();
+  }
 }
diff --git a/hbase-hbtop/src/test/java/org/apache/hadoop/hbase/hbtop/screen/top/TestTopScreenModel.java b/hbase-hbtop/src/test/java/org/apache/hadoop/hbase/hbtop/screen/top/TestTopScreenModel.java
index 9dec535..5b6f884 100644
--- a/hbase-hbtop/src/test/java/org/apache/hadoop/hbase/hbtop/screen/top/TestTopScreenModel.java
+++ b/hbase-hbtop/src/test/java/org/apache/hadoop/hbase/hbtop/screen/top/TestTopScreenModel.java
@@ -56,7 +56,7 @@ public class TestTopScreenModel {
   @Before
   public void setup() throws IOException {
     when(admin.getClusterStatus()).thenReturn(TestUtils.createDummyClusterStatus());
-    topScreenModel = new TopScreenModel(admin, Mode.REGION);
+    topScreenModel = new TopScreenModel(admin, Mode.REGION, null, null, null, null);
 
     fields = new ArrayList<>();
     for (FieldInfo fieldInfo : Mode.REGION.getFieldInfos()) {
@@ -78,17 +78,17 @@ public class TestTopScreenModel {
     TestUtils.assertRecordsInRegionMode(topScreenModel.getRecords());
 
     // Namespace Mode
-    topScreenModel.switchMode(Mode.NAMESPACE, null, false);
+    topScreenModel.switchMode(Mode.NAMESPACE, false, null);
     topScreenModel.refreshMetricsData();
     TestUtils.assertRecordsInNamespaceMode(topScreenModel.getRecords());
 
     // Table Mode
-    topScreenModel.switchMode(Mode.TABLE, null, false);
+    topScreenModel.switchMode(Mode.TABLE, false, null);
     topScreenModel.refreshMetricsData();
     TestUtils.assertRecordsInTableMode(topScreenModel.getRecords());
 
     // Namespace Mode
-    topScreenModel.switchMode(Mode.REGION_SERVER, null, false);
+    topScreenModel.switchMode(Mode.REGION_SERVER, false, null);
     topScreenModel.refreshMetricsData();
     TestUtils.assertRecordsInRegionServerMode(topScreenModel.getRecords());
   }
@@ -162,7 +162,7 @@ public class TestTopScreenModel {
 
   @Test
   public void testSwitchMode() {
-    topScreenModel.switchMode(Mode.TABLE, null, false);
+    topScreenModel.switchMode(Mode.TABLE, false, null);
     assertThat(topScreenModel.getCurrentMode(), is(Mode.TABLE));
 
     // Test for initialFilters
@@ -170,7 +170,7 @@ public class TestTopScreenModel {
       RecordFilter.parse("TABLE==table1", fields, true),
       RecordFilter.parse("TABLE==table2", fields, true));
 
-    topScreenModel.switchMode(Mode.TABLE, initialFilters, false);
+    topScreenModel.switchMode(Mode.TABLE, false, initialFilters);
 
     assertThat(topScreenModel.getFilters().size(), is(initialFilters.size()));
     for (int i = 0; i < topScreenModel.getFilters().size(); i++) {
@@ -180,13 +180,13 @@ public class TestTopScreenModel {
 
     // Test when keepSortFieldAndSortOrderIfPossible is true
     topScreenModel.setSortFieldAndFields(Field.NAMESPACE, fields);
-    topScreenModel.switchMode(Mode.NAMESPACE, null, true);
+    topScreenModel.switchMode(Mode.NAMESPACE, true, null);
     assertThat(topScreenModel.getCurrentSortField(), is(Field.NAMESPACE));
   }
 
   @Test
   public void testDrillDown() {
-    topScreenModel.switchMode(Mode.TABLE, null, false);
+    topScreenModel.switchMode(Mode.TABLE, false, null);
     topScreenModel.setSortFieldAndFields(Field.NAMESPACE, fields);
     topScreenModel.refreshMetricsData();
 
diff --git a/hbase-hbtop/src/test/java/org/apache/hadoop/hbase/hbtop/screen/top/TestTopScreenPresenter.java b/hbase-hbtop/src/test/java/org/apache/hadoop/hbase/hbtop/screen/top/TestTopScreenPresenter.java
index b16b260..8bb036e 100644
--- a/hbase-hbtop/src/test/java/org/apache/hadoop/hbase/hbtop/screen/top/TestTopScreenPresenter.java
+++ b/hbase-hbtop/src/test/java/org/apache/hadoop/hbase/hbtop/screen/top/TestTopScreenPresenter.java
@@ -94,7 +94,8 @@ public class TestTopScreenPresenter {
     when(topScreenModel.getRecords()).thenReturn(TEST_RECORDS);
     when(topScreenModel.getSummary()).thenReturn(TEST_SUMMARY);
 
-    topScreenPresenter = new TopScreenPresenter(topScreenView, 3000, topScreenModel);
+    topScreenPresenter = new TopScreenPresenter(topScreenView, 3000, topScreenModel,
+      null, Long.MAX_VALUE);
   }
 
   @Test