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

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


The following commit(s) were added to refs/heads/branch-2.5 by this push:
     new 526bad77652 HBASE-29028 Backport missing UI patches to branch-2.5 
(#6542)
526bad77652 is described below

commit 526bad77652dda83c3e8d48b240323b80f9ac9b1
Author: Dávid Paksy <[email protected]>
AuthorDate: Wed Jan 8 12:57:39 2025 +0100

    HBASE-29028 Backport missing UI patches to branch-2.5 (#6542)
    
    * HBASE-27406 Make /prometheus endpoint accessible from HBase UI (#4833)
    
    Signed-off-by: Andor Molnar <[email protected]>
    Signed-off-by: Balazs Meszaros <[email protected]>
    (cherry picked from commit dffc8e0fbefda603e3c0557bc2ceac600152675e)
    
    * HBASE-27814 Add support for dump and process metrics servlet in REST 
InfoServer (#5215)
    Other changes:
    - Ensure info server stops during stop()
    - Extract header and footer. This would fix the log level page layout for 
rest web UI (See HBASE-20693)
    - Add hostname in the landing page instead of just port similar to other 
web UIs
    
    Signed-off-by: Nick Dimiduk <[email protected]>
    (cherry picked from commit a683fcfbe8c5c84e58fa8670f4414f9d01ff43ed)
    
    * HBASE-18382 add transport type info into Thrift UI (#880)
    
    Signed-off-by: Wellington Chevreuil <[email protected]>
    Signed-off-by: Bharath Vissapragada <[email protected]>
    Signed-off-by: Viraj Jasani <[email protected]>
    (cherry picked from commit 82e155eb26cddd642e151d9276e2f4f281b4df88)
    
    * HBASE-20693 Refactor thrift jsp's and extract header and footer (#5732)
    - Fixes the way logLevel page renders in UI
    
    Signed-off-by: Nick Dimiduk <[email protected]>
    (cherry picked from commit ede4ccd2dc5f5690a518f97b6d28a7e18df9584a)
    
    * HBASE-24624 Optimize table.jsp code (#1963)
    
    Signed-off-by: Guangxu Cheng <[email protected]>
    (cherry picked from commit 9ad16aa3763d05e85807a5c81b42c03eb2dec4f5)
    
    * HBASE-25402 Sorting order by start key or end key is not considering 
empty start key/end key (#2955)
    
    Signed-off-by: Duo Zhang <[email protected]>
    Signed-off-by: Pankaj Kumar<[email protected]>
    (cherry picked from commit 157200ef8375a91905efb3589393f2ef2b58b970)
    
    * HBASE-27309 Add major compact table or region operation on master web 
table page (#4793)
    
    Co-authored-by: zhengsicheng <[email protected]>
    Signed-off-by: Duo Zhang <[email protected]>
    (cherry picked from commit eb6b2745c550905e92dcfee34e558a0901944da5)
    
    * HBASE-28778 NPE may occur when opening master-status or table.jsp or 
procedure.jsp while Master is initializing (#6152)
    
    Signed-off-by: Duo Zhang <[email protected]>
    (cherry picked from commit 3caaf2d739106b56ab94a0561e730ff35802610d)
    
    * HBASE-28305 Add "Uncompressed StoreFileSize" column to the table.jsp 
(#5620)
    
    Co-authored-by: Haosen Chen <[email protected]>
    Signed-off-by: Duo Zhang <[email protected]>
    (cherry picked from commit e3a0174e20542e661f787a874870b629a274daf5)
    
    * HBASE-20452 Master UI: Table merge button should validate required fields 
before submit
    
    Signed-off-by: tedyu <[email protected]>
    (cherry picked from commit 6ce1136ebae75abc2a1d8d35e1963546b8e2d014)
    
    * HBASE-29028 Removed Prometheus links from navbar as the feature 
(HBASE-20904) is not even supported by backend.
    
    ---------
    
    Co-authored-by: Luca Kovács <[email protected]>
    Co-authored-by: Nihal Jain <[email protected]>
    Co-authored-by: Beata Sudi <[email protected]>
    Co-authored-by: xincunSong <[email protected]>
    Co-authored-by: Akshay Sudheer 
<[email protected]>
    Co-authored-by: SiCheng-Zheng <[email protected]>
    Co-authored-by: Peng Lu <[email protected]>
    Co-authored-by: haosen chen <[email protected]>
    Co-authored-by: Nihal Jain <[email protected]>
    
    Signed-off-by: Andrew Purtell <[email protected]>
    Signed-off-by: Nihal Jain <[email protected]>
---
 .../apache/hadoop/hbase/rest/RESTDumpServlet.java  |   80 +
 .../org/apache/hadoop/hbase/rest/RESTServer.java   |   39 +-
 .../main/resources/hbase-webapps/rest/footer.jsp   |   32 +
 .../main/resources/hbase-webapps/rest/header.jsp   |   72 +
 .../resources/hbase-webapps/rest/processRest.jsp   |  184 ++
 .../src/main/resources/hbase-webapps/rest/rest.jsp |   70 +-
 .../hbase/tmpl/master/MasterStatusTmpl.jamon       |   10 +-
 .../hbase/tmpl/regionserver/RSStatusTmpl.jamon     |   17 +-
 .../main/resources/hbase-webapps/master/header.jsp |   10 +-
 .../main/resources/hbase-webapps/master/table.jsp  | 1905 ++++++++++----------
 .../hbase-webapps/regionserver/header.jsp          |   10 +-
 .../main/resources/hbase-webapps/thrift/footer.jsp |   30 +
 .../main/resources/hbase-webapps/thrift/header.jsp |   72 +
 .../main/resources/hbase-webapps/thrift/thrift.jsp |   91 +-
 14 files changed, 1548 insertions(+), 1074 deletions(-)

diff --git 
a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTDumpServlet.java 
b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTDumpServlet.java
new file mode 100644
index 00000000000..8bb306f7829
--- /dev/null
+++ b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTDumpServlet.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.rest;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.Date;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.http.HttpServer;
+import org.apache.hadoop.hbase.monitoring.StateDumpServlet;
+import org.apache.hadoop.hbase.util.LogMonitoring;
+import org.apache.hadoop.hbase.util.Threads;
+import org.apache.yetus.audience.InterfaceAudience;
+
[email protected]
+public class RESTDumpServlet extends StateDumpServlet {
+  private static final long serialVersionUID = 1L;
+  private static final String LINE = 
"===========================================================";
+
+  @Override
+  public void doGet(HttpServletRequest request, HttpServletResponse response) 
throws IOException {
+    if (!HttpServer.isInstrumentationAccessAllowed(getServletContext(), 
request, response)) {
+      return;
+    }
+
+    RESTServer restServer = (RESTServer) 
getServletContext().getAttribute(RESTServer.REST_SERVER);
+    assert restServer != null : "No REST Server in context!";
+
+    response.setContentType("text/plain");
+    OutputStream os = response.getOutputStream();
+    try (PrintWriter out = new PrintWriter(os)) {
+
+      out.println("REST Server status for " + restServer.getServerName() + " 
as of " + new Date());
+
+      out.println("\n\nVersion Info:");
+      out.println(LINE);
+      dumpVersionInfo(out);
+
+      out.println("\n\nStacks:");
+      out.println(LINE);
+      out.flush();
+      PrintStream ps = new PrintStream(response.getOutputStream(), false, 
"UTF-8");
+      Threads.printThreadInfo(ps, "");
+      ps.flush();
+
+      out.println("\n\nREST Server configuration:");
+      out.println(LINE);
+      Configuration conf = restServer.conf;
+      out.flush();
+      conf.writeXml(os);
+      os.flush();
+
+      out.println("\n\nLogs");
+      out.println(LINE);
+      long tailKb = getTailKbParam(request);
+      LogMonitoring.dumpTailOfLogs(out, tailKb);
+
+      out.flush();
+    }
+  }
+}
diff --git 
a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java 
b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
index 5796cd066f9..dea6f290987 100644
--- a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
+++ b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
@@ -20,6 +20,7 @@ package org.apache.hadoop.hbase.rest;
 import static org.apache.hadoop.hbase.http.HttpServerUtil.PATH_SPEC_ANY;
 
 import java.lang.management.ManagementFactory;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
@@ -31,6 +32,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.HBaseConfiguration;
 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
+import org.apache.hadoop.hbase.ServerName;
 import org.apache.hadoop.hbase.http.HttpServerUtil;
 import org.apache.hadoop.hbase.http.InfoServer;
 import org.apache.hadoop.hbase.log.HBaseMarkers;
@@ -83,6 +85,7 @@ import 
org.apache.hbase.thirdparty.org.glassfish.jersey.servlet.ServletContainer
 @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS)
 public class RESTServer implements Constants {
   static Logger LOG = LoggerFactory.getLogger("RESTServer");
+  public static final String REST_SERVER = "rest";
 
   static final String REST_CSRF_ENABLED_KEY = "hbase.rest.csrf.enabled";
   static final boolean REST_CSRF_ENABLED_DEFAULT = false;
@@ -110,6 +113,7 @@ public class RESTServer implements Constants {
   private final UserProvider userProvider;
   private Server server;
   private InfoServer infoServer;
+  private ServerName serverName;
 
   public RESTServer(Configuration conf) {
     RESTServer.conf = conf;
@@ -143,8 +147,7 @@ public class RESTServer implements Constants {
     loginServerPrincipal(UserProvider userProvider, Configuration conf) throws 
Exception {
     Class<? extends ServletContainer> containerClass = ServletContainer.class;
     if (userProvider.isHadoopSecurityEnabled() && 
userProvider.isHBaseSecurityEnabled()) {
-      String machineName = 
Strings.domainNamePointerToHostName(DNS.getDefaultHost(
-        conf.get(REST_DNS_INTERFACE, "default"), conf.get(REST_DNS_NAMESERVER, 
"default")));
+      String machineName = getHostName(conf);
       String keytabFilename = conf.get(REST_KEYTAB_FILE);
       Preconditions.checkArgument(keytabFilename != null && 
!keytabFilename.isEmpty(),
         REST_KEYTAB_FILE + " should be set if security is enabled");
@@ -385,9 +388,14 @@ public class RESTServer implements Constants {
     // Put up info server.
     int port = conf.getInt("hbase.rest.info.port", 8085);
     if (port >= 0) {
-      conf.setLong("startcode", EnvironmentEdgeManager.currentTime());
-      String a = conf.get("hbase.rest.info.bindAddress", "0.0.0.0");
-      this.infoServer = new InfoServer("rest", a, port, false, conf);
+      final long startCode = EnvironmentEdgeManager.currentTime();
+      conf.setLong("startcode", startCode);
+      this.serverName = ServerName.valueOf(getHostName(conf), servicePort, 
startCode);
+
+      String addr = conf.get("hbase.rest.info.bindAddress", "0.0.0.0");
+      this.infoServer = new InfoServer(REST_SERVER, addr, port, false, conf);
+      this.infoServer.addPrivilegedServlet("dump", "/dump", 
RESTDumpServlet.class);
+      this.infoServer.setAttribute(REST_SERVER, this);
       this.infoServer.setAttribute("hbase.conf", conf);
       this.infoServer.start();
     }
@@ -395,6 +403,11 @@ public class RESTServer implements Constants {
     server.start();
   }
 
+  private static String getHostName(Configuration conf) throws 
UnknownHostException {
+    return Strings.domainNamePointerToHostName(DNS.getDefaultHost(
+      conf.get(REST_DNS_INTERFACE, "default"), conf.get(REST_DNS_NAMESERVER, 
"default")));
+  }
+
   public synchronized void join() throws Exception {
     if (server == null) {
       throw new IllegalStateException("Server is not running");
@@ -402,7 +415,19 @@ public class RESTServer implements Constants {
     server.join();
   }
 
+  private void stopInfoServer() {
+    if (this.infoServer != null) {
+      LOG.info("Stop info server");
+      try {
+        this.infoServer.stop();
+      } catch (Exception e) {
+        LOG.error("Failed to stop infoServer", e);
+      }
+    }
+  }
+
   public synchronized void stop() throws Exception {
+    stopInfoServer();
     if (server == null) {
       throw new IllegalStateException("Server is not running");
     }
@@ -426,6 +451,10 @@ public class RESTServer implements Constants {
     return infoServer.getPort();
   }
 
+  public ServerName getServerName() {
+    return serverName;
+  }
+
   public Configuration getConf() {
     return conf;
   }
diff --git a/hbase-rest/src/main/resources/hbase-webapps/rest/footer.jsp 
b/hbase-rest/src/main/resources/hbase-webapps/rest/footer.jsp
new file mode 100644
index 00000000000..a642ac36eff
--- /dev/null
+++ b/hbase-rest/src/main/resources/hbase-webapps/rest/footer.jsp
@@ -0,0 +1,32 @@
+<%--
+/**
+* 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.
+*/
+--%>
+
+    <script src="/static/js/jquery.min.js" type="text/javascript"></script>
+    <script src="/static/js/bootstrap.min.js" type="text/javascript"></script>
+    <script src="/static/js/tab.js" type="text/javascript"></script>
+    <script type="text/javascript">
+      $(document).ready(function() {
+        $('div.navbar li.active').removeClass('active');
+        $('a[href="' + location.pathname + 
'"]').closest('li').addClass('active');
+      });
+    </script>
+</body>
+</html>
+
diff --git a/hbase-rest/src/main/resources/hbase-webapps/rest/header.jsp 
b/hbase-rest/src/main/resources/hbase-webapps/rest/header.jsp
new file mode 100644
index 00000000000..cb148a708c9
--- /dev/null
+++ b/hbase-rest/src/main/resources/hbase-webapps/rest/header.jsp
@@ -0,0 +1,72 @@
+<%--
+/**
+* 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.
+*/
+--%>
+<%@ page contentType="text/html;charset=UTF-8"
+         import="org.apache.hadoop.hbase.HBaseConfiguration"%>
+
+<!DOCTYPE html>
+<?xml version="1.0" encoding="UTF-8" ?>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <title><%= request.getParameter("pageTitle")%></title>
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <meta name="description" content="">
+
+  <link href="/static/css/bootstrap.min.css" rel="stylesheet">
+  <link href="/static/css/bootstrap-theme.min.css" rel="stylesheet">
+  <link href="/static/css/hbase.css" rel="stylesheet">
+</head>
+
+<body>
+<div class="navbar  navbar-fixed-top navbar-default">
+  <div class="container-fluid">
+    <div class="navbar-header">
+      <button type="button" class="navbar-toggle" data-toggle="collapse" 
data-target=".navbar-collapse">
+        <span class="icon-bar"></span>
+        <span class="icon-bar"></span>
+        <span class="icon-bar"></span>
+      </button>
+      <a class="navbar-brand" href="/rest.jsp"><img 
src="/static/hbase_logo_small.png" alt="HBase Logo"/></a>
+    </div>
+    <div class="collapse navbar-collapse">
+      <ul class="nav navbar-nav">
+        <li class="active"><a href="/rest.jsp">Home</a></li>
+        <li><a href="/logs/">Local logs</a></li>
+        <li><a href="/processRest.jsp">Process Metrics</a></li>
+        <li><a href="/logLevel">Log Level</a></li>
+        <li><a href="/dump">Debug Dump</a></li>
+        <li class="nav-item dropdown">
+          <a class="nav-link dropdown-toggle" href="#" 
id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" 
aria-expanded="false">
+            Metrics <span class="caret"></span>
+          </a>
+          <ul class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
+            <li><a target="_blank" href="/jmx">JMX</a></li>
+            <li><a target="_blank" href="/jmx?description=true">JMX with 
description</a></li>
+          </ul>
+        </li>
+        <li><a href="/prof">Profiler</a></li>
+        <% if (HBaseConfiguration.isShowConfInServlet()) { %>
+        <li><a href="/conf">HBase Configuration</a></li>
+        <% } %>
+      </ul>
+    </div><!--/.nav-collapse -->
+  </div>
+</div>
+
diff --git a/hbase-rest/src/main/resources/hbase-webapps/rest/processRest.jsp 
b/hbase-rest/src/main/resources/hbase-webapps/rest/processRest.jsp
new file mode 100644
index 00000000000..2b2d35fbfb3
--- /dev/null
+++ b/hbase-rest/src/main/resources/hbase-webapps/rest/processRest.jsp
@@ -0,0 +1,184 @@
+<%--
+/**
+ * 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.
+ */
+--%>
+<%@ page contentType="text/html;charset=UTF-8"
+  import="java.util.Date"
+  import="java.util.List"
+  import="javax.management.ObjectName"
+  import="java.lang.management.ManagementFactory"
+  import="java.lang.management.MemoryPoolMXBean"
+  import="java.lang.management.RuntimeMXBean"
+  import="java.lang.management.GarbageCollectorMXBean"
+  import="org.apache.hadoop.hbase.util.JSONMetricUtil"
+  import="org.apache.hadoop.hbase.procedure2.util.StringUtils"
+  import="org.apache.hadoop.util.StringUtils.TraditionalBinaryPrefix"
+%>
+
+<%
+RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
+ObjectName jvmMetrics = new ObjectName("Hadoop:service=HBase,name=JvmMetrics");
+
+// There is always two of GC collectors
+List<GarbageCollectorMXBean> gcBeans = JSONMetricUtil.getGcCollectorBeans();
+GarbageCollectorMXBean collector1 = null;
+GarbageCollectorMXBean collector2 = null;
+try {
+collector1 = gcBeans.get(0);
+collector2 = gcBeans.get(1);
+} catch(IndexOutOfBoundsException e) {}
+List<MemoryPoolMXBean> mPools = JSONMetricUtil.getMemoryPools();
+pageContext.setAttribute("pageTitle", "Process info for PID: " + 
JSONMetricUtil.getProcessPID());
+%>
+
+<jsp:include page="header.jsp">
+  <jsp:param name="pageTitle" value="${pageTitle}"/>
+</jsp:include>
+
+<div class="container-fluid content">
+  <div class="row">
+      <div class="page-header">
+          <h1><%= JSONMetricUtil.getCommmand().split(" ")[0] %></h1>
+      </div>
+  </div>
+  <table class="table table-striped" width="90%" >
+    <tr>
+        <th>Started</th>
+        <th>Uptime</th>
+        <th>PID</th>
+        <th>Owner</th>
+    </tr>
+    <tr>
+      <tr>
+        <td><%= new Date(runtimeBean.getStartTime()) %></td>
+        <td><%= StringUtils.humanTimeDiff(runtimeBean.getUptime()) %></td>
+        <td><%= JSONMetricUtil.getProcessPID() %></td>
+        <td><%= runtimeBean.getSystemProperties().get("user.name") %></td>
+      </tr>
+  </table>
+</div>
+<div class="container-fluid content">
+  <div class="row">
+    <div class="page-header">
+    <h2>Threads</h2>
+    </div>
+    </div>
+  <table class="table table-striped" width="90%" >
+    <tr>
+        <th>ThreadsNew</th>
+        <th>ThreadsRunable</th>
+        <th>ThreadsBlocked</th>
+        <th>ThreadsWaiting</th>
+        <th>ThreadsTimeWaiting</th>
+        <th>ThreadsTerminated</th>
+    </tr>
+     <tr>
+        <td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsNew")  
%></td>
+        <td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, 
"ThreadsRunnable")%></td>
+        <td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, 
"ThreadsBlocked")%></td>
+        <td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, 
"ThreadsWaiting")%></td>
+        <td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, 
"ThreadsTimedWaiting")%></td>
+        <td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, 
"ThreadsTerminated")%></td>
+     </tr>
+  </table>
+</div>
+<div class="container-fluid content">
+  <div class="row">
+    <div class="page-header">
+    <h2>GC Collectors</h2>
+    </div>
+    </div>
+    <% if (gcBeans.size() == 2) { %>
+<div class="tabbable">
+  <ul class="nav nav-pills">
+    <li class="active">
+      <a href="#tab_gc1" data-toggle="tab"><%=collector1.getName() %></a>
+    </li>
+    <li class="">
+      <a href="#tab_gc2" data-toggle="tab"><%=collector2.getName() %></a>
+     </li>
+  </ul>
+    <div class="tab-content" style="padding-bottom: 9px; border-bottom: 1px 
solid #ddd;">
+      <div class="tab-pane active" id="tab_gc1">
+          <table class="table table-striped">
+            <tr>
+              <th>Collection Count</th>
+              <th>Collection Time</th>
+              <th>Last duration</th>
+            </tr>
+            <tr>
+              <td> <%= collector1.getCollectionCount() %></td>
+              <td> <%= 
StringUtils.humanTimeDiff(collector1.getCollectionTime()) %> </td>
+              <td> <%= 
StringUtils.humanTimeDiff(JSONMetricUtil.getLastGcDuration(
+                collector1.getObjectName())) %></td>
+            </tr>
+          </table>
+      </div>
+      <div class="tab-pane" id="tab_gc2">
+        <table class="table table-striped">
+          <tr>
+            <th>Collection Count</th>
+            <th>Collection Time</th>
+             <th>Last duration</th>
+          </tr>
+          <tr>
+            <td> <%= collector2.getCollectionCount()  %></td>
+            <td> <%= StringUtils.humanTimeDiff(collector2.getCollectionTime()) 
%> </td>
+            <td> <%= 
StringUtils.humanTimeDiff(JSONMetricUtil.getLastGcDuration(
+              collector2.getObjectName())) %></td>
+          </tr>
+          </table>
+      </div>
+      </div>
+  </div>
+  <%} else { %>
+  <p> Can not display GC Collector stats.</p>
+  <%} %>
+  Total GC Collection time: <%= 
StringUtils.humanTimeDiff(collector1.getCollectionTime() +
+    collector2.getCollectionTime())%>
+</div>
+<% for(MemoryPoolMXBean mp:mPools) {
+if(mp.getName().contains("Cache")) continue;%>
+<div class="container-fluid content">
+  <div class="row">
+      <div class="page-header">
+          <h2><%= mp.getName() %></h2>
+      </div>
+  </div>
+  <table class="table table-striped" width="90%" >
+    <tr>
+        <th>Commited</th>
+        <th>Init</th>
+        <th>Max</th>
+        <th>Used</th>
+        <th>Utilization [%]</th>
+    </tr>
+    <tr>
+      <tr>
+        <td><%= 
TraditionalBinaryPrefix.long2String(mp.getUsage().getCommitted(), "B", 1) 
%></td>
+        <td><%= TraditionalBinaryPrefix.long2String(mp.getUsage().getInit(), 
"B", 1) %></td>
+        <td><%= TraditionalBinaryPrefix.long2String(mp.getUsage().getMax(), 
"B", 1) %></td>
+        <td><%= TraditionalBinaryPrefix.long2String(mp.getUsage().getUsed(), 
"B", 1) %></td>
+        <td><%= JSONMetricUtil.calcPercentage(mp.getUsage().getUsed(),
+          mp.getUsage().getCommitted()) %></td>
+      </tr>
+  </table>
+</div>
+<% } %>
+
+<jsp:include page="footer.jsp" />
diff --git a/hbase-rest/src/main/resources/hbase-webapps/rest/rest.jsp 
b/hbase-rest/src/main/resources/hbase-webapps/rest/rest.jsp
index 3deb2bbc735..ce6725f283a 100644
--- a/hbase-rest/src/main/resources/hbase-webapps/rest/rest.jsp
+++ b/hbase-rest/src/main/resources/hbase-webapps/rest/rest.jsp
@@ -18,60 +18,29 @@
  */
 --%>
 <%@ page contentType="text/html;charset=UTF-8"
-  import="org.apache.hadoop.conf.Configuration"
-  import="org.apache.hadoop.hbase.HBaseConfiguration"
-  import="org.apache.hadoop.hbase.rest.model.VersionModel"
-  import="org.apache.hadoop.hbase.util.VersionInfo"
-  import="java.util.Date"%>
+         import="org.apache.hadoop.conf.Configuration"
+         import="org.apache.hadoop.hbase.rest.RESTServer"
+         import="org.apache.hadoop.hbase.rest.model.VersionModel"
+         import="org.apache.hadoop.hbase.util.VersionInfo"
+         import="java.util.Date"%>
+
 <%
-Configuration conf = 
(Configuration)getServletContext().getAttribute("hbase.conf");
-long startcode = conf.getLong("startcode", System.currentTimeMillis());
-String listenPort = conf.get("hbase.rest.port", "8080");
-%>
-<!DOCTYPE html>
-<?xml version="1.0" encoding="UTF-8" ?>
-<html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <title>HBase REST Server: <%= listenPort %></title>
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <meta name="description" content="">
+  Configuration conf = (Configuration) 
getServletContext().getAttribute("hbase.conf");
+  long startcode = conf.getLong("startcode", System.currentTimeMillis());
 
-    <link href="/static/css/bootstrap.min.css" rel="stylesheet">
-    <link href="/static/css/bootstrap-theme.min.css" rel="stylesheet">
-    <link href="/static/css/hbase.css" rel="stylesheet">
-  </head>
+  final RESTServer restServer = (RESTServer) 
getServletContext().getAttribute(RESTServer.REST_SERVER);
+  final String hostName = restServer.getServerName().getHostname();
+  pageContext.setAttribute("pageTitle", "HBase REST Server" + hostName);
+%>
 
-  <body>
-  <div class="navbar  navbar-fixed-top navbar-default">
-      <div class="container-fluid">
-          <div class="navbar-header">
-              <button type="button" class="navbar-toggle" 
data-toggle="collapse" data-target=".navbar-collapse">
-                  <span class="icon-bar"></span>
-                  <span class="icon-bar"></span>
-                  <span class="icon-bar"></span>
-              </button>
-              <a class="navbar-brand" href="/rest.jsp"><img 
src="/static/hbase_logo_small.png" alt="HBase Logo"/></a>
-          </div>
-          <div class="collapse navbar-collapse">
-              <ul class="nav navbar-nav">
-                  <li class="active"><a href="/rest.jsp">Home</a></li>
-                  <li><a href="/logs/">Local logs</a></li>
-                  <li><a href="/logLevel">Log Level</a></li>
-                  <li><a href="/jmx">Metrics Dump</a></li>
-                  <li><a href="/prof">Profiler</a></li>
-                  <% if (HBaseConfiguration.isShowConfInServlet()) { %>
-                  <li><a href="/conf">HBase Configuration</a></li>
-                  <% } %>
-              </ul>
-          </div><!--/.nav-collapse -->
-      </div>
-  </div>
+<jsp:include page="header.jsp">
+  <jsp:param name="pageTitle" value="${pageTitle}"/>
+</jsp:include>
 
 <div class="container-fluid content">
     <div class="row inner_header">
         <div class="page-header">
-            <h1>RESTServer <small><%= listenPort %></small></h1>
+            <h1>RESTServer <small><%= hostName %></small></h1>
         </div>
     </div>
     <div class="row">
@@ -114,9 +83,6 @@ String listenPort = conf.get("hbase.rest.port", "8080");
     </section>
     </div>
 </div>
-<script src="/static/js/jquery.min.js" type="text/javascript"></script>
-<script src="/static/js/bootstrap.min.js" type="text/javascript"></script>
-<script src="/static/js/tab.js" type="text/javascript"></script>
-</body>
-</html>
+
+<jsp:include page="footer.jsp" />
 
diff --git 
a/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/MasterStatusTmpl.jamon
 
b/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/MasterStatusTmpl.jamon
index 863d7e039a4..886be7801a2 100644
--- 
a/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/MasterStatusTmpl.jamon
+++ 
b/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/MasterStatusTmpl.jamon
@@ -162,7 +162,15 @@ AssignmentManager assignmentManager = 
master.getAssignmentManager();
                 <li><a href="/logs/">Local Logs</a></li>
                 <li><a href="/logLevel">Log Level</a></li>
                 <li><a href="/dump">Debug Dump</a></li>
-                <li><a href="/jmx">Metrics Dump</a></li>
+                <li class="nav-item dropdown">
+                  <a class="nav-link dropdown-toggle" href="#" 
id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" 
aria-expanded="false">
+                    Metrics <span class="caret"></span>
+                  </a>
+                  <ul class="dropdown-menu" 
aria-labelledby="navbarDropdownMenuLink">
+                    <li><a target="_blank" href="/jmx">JMX</a></li>
+                    <li><a target="_blank" href="/jmx?description=true">JMX 
with description</a></li>
+                  </ul>
+                </li>
                 <li><a href="/prof">Profiler</a></li>
                 <%if HBaseConfiguration.isShowConfInServlet()%>
                 <li><a href="/conf">HBase Configuration</a></li>
diff --git 
a/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/regionserver/RSStatusTmpl.jamon
 
b/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/regionserver/RSStatusTmpl.jamon
index 2068c760799..63054c92348 100644
--- 
a/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/regionserver/RSStatusTmpl.jamon
+++ 
b/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/regionserver/RSStatusTmpl.jamon
@@ -114,7 +114,15 @@ org.apache.hadoop.hbase.zookeeper.MasterAddressTracker;
                 <li><a href="/rsOperationDetails.jsp">Operation 
Details</a></li>
                 <li><a href="/logLevel">Log Level</a></li>
                 <li><a href="/dump">Debug Dump</a></li>
-                <li><a href="/jmx">Metrics Dump</a></li>
+                <li class="nav-item dropdown">
+                  <a class="nav-link dropdown-toggle" href="#" 
id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" 
aria-expanded="false">
+                    Metrics <span class="caret"></span>
+                  </a>
+                  <ul class="dropdown-menu" 
aria-labelledby="navbarDropdownMenuLink">
+                    <li><a target="_blank" href="/jmx">JMX</a></li>
+                    <li><a target="_blank" href="/jmx?description=true">JMX 
with description</a></li>
+                  </ul>
+                </li>
                 <li><a href="/prof">Profiler</a></li>
                 <%if HBaseConfiguration.isShowConfInServlet()%>
                 <li><a href="/conf">HBase Configuration</a></li>
@@ -289,7 +297,12 @@ $(document).ready(function()
             type: "numeric"
         });
 
-        $("#baseStatsTable").tablesorter();
+        $("#baseStatsTable").tablesorter({
+            headers: {
+                1: {empty: 'emptyMin'},
+                2: {empty: 'emptyMax'}
+            }
+        });
         $("#requestStatsTable").tablesorter({
             headers: {
                 1: {sorter: 'separator'},
diff --git a/hbase-server/src/main/resources/hbase-webapps/master/header.jsp 
b/hbase-server/src/main/resources/hbase-webapps/master/header.jsp
index 7f4fa55f59d..dac6be4fc5a 100644
--- a/hbase-server/src/main/resources/hbase-webapps/master/header.jsp
+++ b/hbase-server/src/main/resources/hbase-webapps/master/header.jsp
@@ -69,7 +69,15 @@
             <li><a href="/logs/">Local Logs</a></li>
             <li><a href="/logLevel">Log Level</a></li>
             <li><a href="/dump">Debug Dump</a></li>
-            <li><a href="/jmx">Metrics Dump</a></li>
+            <li class="nav-item dropdown">
+              <a class="nav-link dropdown-toggle" href="#" 
id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" 
aria-expanded="false">
+                Metrics <span class="caret"></span>
+              </a>
+              <ul class="dropdown-menu" 
aria-labelledby="navbarDropdownMenuLink">
+                <li><a target="_blank" href="/jmx">JMX</a></li>
+                <li><a target="_blank" href="/jmx?description=true">JMX with 
description</a></li>
+              </ul>
+            </li>
             <li><a href="/prof">Profiler</a></li>
             <% if (HBaseConfiguration.isShowConfInServlet()) { %>
             <li><a href="/conf">HBase Configuration</a></li>
diff --git a/hbase-server/src/main/resources/hbase-webapps/master/table.jsp 
b/hbase-server/src/main/resources/hbase-webapps/master/table.jsp
index 8b88f20301d..6ae98b6985e 100644
--- a/hbase-server/src/main/resources/hbase-webapps/master/table.jsp
+++ b/hbase-server/src/main/resources/hbase-webapps/master/table.jsp
@@ -93,7 +93,7 @@
    * its region server.
    * @return an anchor tag if one can be built, {@code null} otherwise.
    */
-  private static String buildRegionServerLink(final ServerName serverName, 
final int rsInfoPort,
+  private static String buildRegionLink(final ServerName serverName, final int 
rsInfoPort,
     final RegionInfo regionInfo, final RegionState.State regionState) {
     if (serverName == null || regionInfo == null) { return null; }
 
@@ -107,15 +107,74 @@
       + "?name=" + regionInfo.getEncodedName();
     return "<a href=\"" + URI + "\">" + serverName.getServerName() + "</a>";
   }
+
+  /**
+   * Render an <td> tag contents server name which the given region deploys.
+   * Links to the server rs-status page.
+   * <td class="undeployed-region">not deployed</td> instead if can not find 
the deploy message.
+   * @return an <td> tag contents server name links to server rs-status page.
+   */
+  private static String buildRegionDeployedServerTag(RegionInfo regionInfo, 
HMaster master,
+    Map<RegionInfo, ServerName> regionsToServer) {
+    ServerName serverName = regionsToServer.get(regionInfo);
+
+    if (serverName == null) {
+      return "<td class=\"undeployed-region\">not deployed</td>";
+    }
+
+    String hostName = serverName.getHostname();
+    String hostNameEncoded = URLEncoder.encode(hostName);
+    // This port might be wrong if RS actually ended up using something else.
+    int serverInfoPort = master.getRegionServerInfoPort(serverName);
+    String urlRegionServer = "//" + hostNameEncoded + ":" + serverInfoPort + 
"/rs-status";
+
+    return "<td><a href=\"" + urlRegionServer + "\">" + 
StringEscapeUtils.escapeHtml4(hostName)
+      + ":" + serverInfoPort + "</a></td>";
+  }
+
+  /**
+   * @return an <p> tag guide user to see all region messages.
+   */
+  private static String moreRegionsToRender(int numRegionsRendered, int 
numRegions, String fqtn) {
+    if (numRegions > numRegionsRendered) {
+      String allRegionsUrl = "?name=" + URLEncoder.encode(fqtn) + 
"&numRegions=all";
+
+      return "This table has <b>" + numRegions
+        + "</b> regions in total, in order to improve the page load time, only 
<b>"
+        + numRegionsRendered + "</b> regions are displayed here, <a href=\""
+        + allRegionsUrl + "\">click here</a> to see all regions.</p>";
+    }
+    return "";
+  }
 %>
+
+<jsp:include page="header.jsp">
+  <jsp:param name="pageTitle" value="${pageTitle}"/>
+</jsp:include>
+
 <%
   final String ZEROMB = "0 MB";
   HMaster master = (HMaster)getServletContext().getAttribute(HMaster.MASTER);
   Configuration conf = master.getConfiguration();
   String fqtn = request.getParameter("name");
+  // handle the case for fqtn is null or master is not initialized with error 
message + redirect
+  if (fqtn == null || !master.isInitialized()) {
+%>
+    <div class="container-fluid content">
+      <div class="row inner_header">
+        <div class="page-header">
+          <h1>Table not ready</h1>
+        </div>
+      </div>
+      <p><hr><p>
+      <jsp:include page="redirect.jsp" />
+    </div>
+<%  return;
+  } %>
+
+<%
   final String escaped_fqtn = StringEscapeUtils.escapeHtml4(fqtn);
-  Table table;
-  boolean withReplica = false;
+  Table table = master.getConnection().getTable(TableName.valueOf(fqtn));
   boolean showFragmentation = 
conf.getBoolean("hbase.master.ui.fragmentation.enabled", false);
   boolean readOnly = !InfoServer.canUserModifyUI(request, getServletContext(), 
conf);
   int numMetaReplicas =
@@ -162,35 +221,50 @@
   final MetaBrowser metaBrowser = new MetaBrowser(connection, request);
 %>
 
-<jsp:include page="header.jsp">
-  <jsp:param name="pageTitle" value="${pageTitle}"/>
-</jsp:include>
-
-<%
-  if (fqtn != null && master.isInitialized()) {
-    try {
-      table = master.getConnection().getTable(TableName.valueOf(fqtn));
-      if (table.getTableDescriptor().getRegionReplication() > 1) {
-        withReplica = true;
-      }
-      if ( !readOnly && action != null ) {
-%>
-<div class="container-fluid content">
-  <div class="row inner_header">
-    <div class="page-header">
-      <h1>Table action request accepted</h1>
+<% // unknow table
+  if (! admin.tableExists(TableName.valueOf(fqtn)).get()) { %>
+    <div class="container-fluid content">
+      <div class="row inner_header">
+        <div class="page-header">
+          <h1>Table not found</h1>
+        </div>
+      </div>
+      <p><hr><p>
+      <jsp:include page="redirect.jsp" />
     </div>
-  </div>
-  <p><hr><p>
-    <%
-    if (action.equals("split")) {
+<%  return;
+  } %>
+
+<% // table split/major compact/compact/merge actions
+  if ( !readOnly && action != null ) { %>
+    <div class="container-fluid content">
+      <div class="row inner_header">
+        <div class="page-header">
+          <h1>Table action request accepted</h1>
+        </div>
+      </div>
+      <p><hr><p>
+<%  if (action.equals("split")) {
       if (key != null && key.length() > 0) {
         admin.split(TableName.valueOf(fqtn), Bytes.toBytes(key));
       } else {
         admin.split(TableName.valueOf(fqtn));
       }
+%>    Split request accepted. <%
+    } else if (action.equals("major compact")) {
+      if (key != null && key.length() > 0) {
+        List<RegionInfo> regions = 
admin.getRegions(TableName.valueOf(fqtn)).get();
+        byte[] row = Bytes.toBytes(key);
 
-    %> Split request accepted. <%
+        for (RegionInfo region : regions) {
+          if (region.containsRow(row)) {
+            admin.majorCompactRegion(region.getRegionName());
+          }
+        }
+      } else {
+        admin.majorCompact(TableName.valueOf(fqtn));
+      }
+%>    major Compact request accepted. <%
     } else if (action.equals("compact")) {
       if (key != null && key.length() > 0) {
         List<RegionInfo> regions = 
admin.getRegions(TableName.valueOf(fqtn)).get();
@@ -204,239 +278,229 @@
       } else {
         admin.compact(TableName.valueOf(fqtn));
       }
-    %> Compact request accepted. <%
+%>    Compact request accepted. <%
     } else if (action.equals("merge")) {
-        if (left != null && left.length() > 0 && right != null && 
right.length() > 0) {
-            admin.mergeRegions(Bytes.toBytesBinary(left), 
Bytes.toBytesBinary(right), false);
-        }
-        %> Merge request accepted. <%
-    }
-%>
-  <jsp:include page="redirect.jsp" />
-</div>
-<%
-} else {
-%>
-<div class="container-fluid content">
-  <div class="row inner_header">
-    <div class="page-header">
-      <h1>Table <small><%= escaped_fqtn %></small></h1>
+      if (left != null && left.length() > 0 && right != null && right.length() 
> 0) {
+        admin.mergeRegions(Bytes.toBytesBinary(left), 
Bytes.toBytesBinary(right), false);
+      }
+%>    Merge request accepted. <%
+    } %>
+    <jsp:include page="redirect.jsp" />
     </div>
+<%  return;
+  } %>
+
+<div class="container-fluid content">
+<div class="row inner_header">
+  <div class="page-header">
+    <h1>Table <small><%= escaped_fqtn %></small></h1>
   </div>
-  <div class="row">
-    <%
-      if(fqtn.equals(TableName.META_TABLE_NAME.getNameAsString())) {
-    %>
-    <h2>Table Regions</h2>
-    <div class="tabbable">
-      <ul class="nav nav-pills">
-        <li class="active">
-          <a href="#metaTab_baseStats" data-toggle="tab">Base Stats</a>
-        </li>
-        <li class="">
-          <a href="#metaTab_localityStats" data-toggle="tab">Localities</a>
-        </li>
-        <li class="">
-          <a href="#metaTab_compactStats" data-toggle="tab">Compactions</a>
-        </li>
-      </ul>
-      <div class="tab-content" style="padding-bottom: 9px; border-bottom: 1px 
solid #ddd;">
-        <div class="tab-pane active" id="metaTab_baseStats">
-          <table id="tableRegionTable" class="tablesorter table table-striped">
-            <thead>
-            <tr>
-              <th>Name</th>
-              <th>Region Server</th>
-              <th>ReadRequests</th>
-              <th>WriteRequests</th>
-              <th>StorefileSize</th>
-              <th>Num.Storefiles</th>
-              <th>MemSize</th>
-              <th>Start Key</th>
-              <th>End Key</th>
-              <%
-                if (withReplica) {
-              %>
-              <th>ReplicaID</th>
-              <%
-                }
-              %>
-            </tr>
-            </thead>
-            <tbody>
-            <%
-              // NOTE: Presumes meta with one or more replicas
-              for (int j = 0; j < numMetaReplicas; j++) {
-                RegionInfo meta = RegionReplicaUtil.getRegionInfoForReplica(
-                        RegionInfoBuilder.FIRST_META_REGIONINFO, j);
-                //If a metaLocation is null, All of its info would be empty 
here to be displayed.
-                RegionStateNode rsn = 
master.getAssignmentManager().getRegionStates()
-                  .getRegionStateNode(meta);
-                ServerName metaLocation = rsn != null ? 
rsn.getRegionLocation() : null;
-                for (int i = 0; i < 1; i++) {
-                  //If metaLocation is null, default value below would be 
displayed in UI.
-                  String hostAndPort = "";
-                  String readReq = "N/A";
-                  String writeReq = "N/A";
-                  String fileSize = ZEROMB;
-                  String fileCount = "N/A";
-                  String memSize = ZEROMB;
-                  if (metaLocation != null) {
-                    ServerMetrics sl = 
master.getServerManager().getLoad(metaLocation);
-                    // The host name portion should be safe, but I don't know 
how we handle IDNs so err on the side of failing safely.
-                    hostAndPort = 
URLEncoder.encode(metaLocation.getHostname()) + ":" + 
master.getRegionServerInfoPort(metaLocation);
-                    if (sl != null) {
-                      Map<byte[], RegionMetrics> map = sl.getRegionMetrics();
-                      if (map.containsKey(meta.getRegionName())) {
-                        RegionMetrics load = map.get(meta.getRegionName());
-                        readReq = String.format("%,1d", 
load.getReadRequestCount());
-                        writeReq = String.format("%,1d", 
load.getWriteRequestCount());
-                        double rSize = 
load.getStoreFileSize().get(Size.Unit.BYTE);
-                        if (rSize > 0) {
-                          fileSize = StringUtils.byteDesc((long) rSize);
-                        }
-                        fileCount = String.format("%,1d", 
load.getStoreFileCount());
-                        double mSize = 
load.getMemStoreSize().get(Size.Unit.BYTE);
-                        if (mSize > 0) {
-                          memSize = StringUtils.byteDesc((long)mSize);
-                        }
-                      }
+</div>
+
+<div class="row">
+<% //Meta table.
+  if(fqtn.equals(TableName.META_TABLE_NAME.getNameAsString())) { %>
+<h2>Table Regions</h2>
+<div class="tabbable">
+  <ul class="nav nav-pills">
+    <li class="active"><a href="#metaTab_baseStats" data-toggle="tab">Base 
Stats</a></li>
+    <li class=""><a href="#metaTab_localityStats" 
data-toggle="tab">Localities</a></li>
+    <li class=""><a href="#metaTab_compactStats" 
data-toggle="tab">Compactions</a></li>
+  </ul>
+
+  <div class="tab-content" style="padding-bottom: 9px; border-bottom: 1px 
solid #ddd;">
+    <div class="tab-pane active" id="metaTab_baseStats">
+      <table id="metaTableBaseStatsTable" class="tablesorter table 
table-striped">
+        <thead>
+          <tr>
+            <th>Name</th>
+            <th>Region Server</th>
+            <th>ReadRequests</th>
+            <th>WriteRequests</th>
+            <th>Uncompressed StoreFileSize</th>
+            <th>StorefileSize</th>
+            <th>Num.Storefiles</th>
+            <th>MemSize</th>
+            <th>Start Key</th>
+            <th>End Key</th>
+            <th>ReplicaID</th>
+          </tr>
+        </thead>
+        <tbody>
+        <%
+          // NOTE: Presumes meta with one or more replicas
+          for (int j = 0; j < numMetaReplicas; j++) {
+            RegionInfo meta = RegionReplicaUtil.getRegionInfoForReplica(
+                    RegionInfoBuilder.FIRST_META_REGIONINFO, j);
+            //If a metaLocation is null, All of its info would be empty here 
to be displayed.
+            RegionStateNode rsn = 
master.getAssignmentManager().getRegionStates()
+              .getRegionStateNode(meta);
+            ServerName metaLocation = rsn != null ? rsn.getRegionLocation() : 
null;
+            for (int i = 0; i < 1; i++) {
+              //If metaLocation is null, default value below would be 
displayed in UI.
+              String hostAndPort = "";
+              String readReq = "N/A";
+              String writeReq = "N/A";
+              String fileSizeUncompressed = ZEROMB;
+              String fileSize = ZEROMB;
+              String fileCount = "N/A";
+              String memSize = ZEROMB;
+              if (metaLocation != null) {
+                ServerMetrics sl = 
master.getServerManager().getLoad(metaLocation);
+                // The host name portion should be safe, but I don't know how 
we handle IDNs so err on the side of failing safely.
+                hostAndPort = URLEncoder.encode(metaLocation.getHostname()) + 
":" + master.getRegionServerInfoPort(metaLocation);
+                if (sl != null) {
+                  Map<byte[], RegionMetrics> map = sl.getRegionMetrics();
+                  if (map.containsKey(meta.getRegionName())) {
+                    RegionMetrics load = map.get(meta.getRegionName());
+                    readReq = String.format("%,1d", 
load.getReadRequestCount());
+                    writeReq = String.format("%,1d", 
load.getWriteRequestCount());
+                    double rSize = load.getStoreFileSize().get(Size.Unit.BYTE);
+                    if (rSize > 0) {
+                      fileSize = StringUtils.byteDesc((long) rSize);
+                    }
+                    double rSizeUncompressed = 
load.getUncompressedStoreFileSize().get(Size.Unit.BYTE);
+                    if (rSizeUncompressed > 0) {
+                    fileSizeUncompressed = StringUtils.byteDesc((long) 
rSizeUncompressed);
+                    }
+                    fileCount = String.format("%,1d", 
load.getStoreFileCount());
+                    double mSize = load.getMemStoreSize().get(Size.Unit.BYTE);
+                    if (mSize > 0) {
+                      memSize = StringUtils.byteDesc((long)mSize);
                     }
                   }
-            %>
-            <tr>
-              <td><%= escapeXml(meta.getRegionNameAsString()) %></td>
-              <td><a href="http://<%= hostAndPort %>/rs-status"><%= 
StringEscapeUtils.escapeHtml4(hostAndPort) %></a></td>
-              <td><%= readReq%></td>
-              <td><%= writeReq%></td>
-              <td><%= fileSize%></td>
-              <td><%= fileCount%></td>
-              <td><%= memSize%></td>
-              <td><%= escapeXml(Bytes.toString(meta.getStartKey())) %></td>
-              <td><%= escapeXml(Bytes.toString(meta.getEndKey())) %></td>
-              <%
-                if (withReplica) {
-              %>
-              <td><%= meta.getReplicaId() %></td>
-              <%
                 }
-              %>
-            </tr>
-            <%  } %>
-            <%} %>
-            </tbody>
-          </table>
-        </div>
-        <div class="tab-pane" id="metaTab_localityStats">
-           <table id="tableRegionTable" class="tablesorter table 
table-striped">
-             <thead>
-               <tr>
-                 <th>Name</th>
-                 <th>Region Server</th>
-                 <th>Locality</th>
-                 <th>LocalityForSsd</th>
-               </tr>
-             </thead>
-             <tbody>
-             <%
-               // NOTE: Presumes meta with one or more replicas
-               for (int j = 0; j < numMetaReplicas; j++) {
-                 RegionInfo meta = RegionReplicaUtil.getRegionInfoForReplica(
-                                         
RegionInfoBuilder.FIRST_META_REGIONINFO, j);
-                 //If a metaLocation is null, All of its info would be empty 
here to be displayed.
-                 RegionStateNode rsn = 
master.getAssignmentManager().getRegionStates()
-                   .getRegionStateNode(meta);
-                 ServerName metaLocation = rsn != null ? 
rsn.getRegionLocation() : null;
-                 for (int i = 0; i < 1; i++) {
-                   //If metaLocation is null, default value below would be 
displayed in UI.
-                   String hostAndPort = "";
-                   float locality = 0.0f;
-                   float localityForSsd = 0.0f;
-                   if (metaLocation != null) {
-                     ServerMetrics sl = 
master.getServerManager().getLoad(metaLocation);
-                     hostAndPort = 
URLEncoder.encode(metaLocation.getHostname()) + ":" + 
master.getRegionServerInfoPort(metaLocation);
-                     if (sl != null) {
-                       Map<byte[], RegionMetrics> map = sl.getRegionMetrics();
-                       if (map.containsKey(meta.getRegionName())) {
-                         RegionMetrics load = map.get(meta.getRegionName());
-                         locality = load.getDataLocality();
-                         localityForSsd = load.getDataLocalityForSsd();
-                       }
-                     }
+              }
+            %>
+          <tr>
+            <td><%= escapeXml(meta.getRegionNameAsString()) %></td>
+            <td><a href="http://<%= hostAndPort %>/rs-status"><%= 
StringEscapeUtils.escapeHtml4(hostAndPort) %></a></td>
+            <td><%= readReq%></td>
+            <td><%= writeReq%></td>
+            <td><%= fileSizeUncompressed%></td>
+            <td><%= fileSize%></td>
+            <td><%= fileCount%></td>
+            <td><%= memSize%></td>
+            <td><%= escapeXml(Bytes.toString(meta.getStartKey())) %></td>
+            <td><%= escapeXml(Bytes.toString(meta.getEndKey())) %></td>
+            <td><%= meta.getReplicaId() %></td>
+          </tr>
+        <%  } %>
+        <%} %>
+        </tbody>
+      </table>
+    </div>
+    <div class="tab-pane" id="metaTab_localityStats">
+       <table id="metaTableLocalityStatsTable" class="tablesorter table 
table-striped">
+         <thead>
+           <tr>
+             <th>Name</th>
+             <th>Region Server</th>
+             <th>Locality</th>
+             <th>LocalityForSsd</th>
+           </tr>
+         </thead>
+         <tbody>
+         <%
+           // NOTE: Presumes meta with one or more replicas
+           for (int j = 0; j < numMetaReplicas; j++) {
+             RegionInfo meta = RegionReplicaUtil.getRegionInfoForReplica(
+                                     RegionInfoBuilder.FIRST_META_REGIONINFO, 
j);
+             //If a metaLocation is null, All of its info would be empty here 
to be displayed.
+             RegionStateNode rsn = 
master.getAssignmentManager().getRegionStates()
+               .getRegionStateNode(meta);
+             ServerName metaLocation = rsn != null ? rsn.getRegionLocation() : 
null;
+             for (int i = 0; i < 1; i++) {
+               //If metaLocation is null, default value below would be 
displayed in UI.
+               String hostAndPort = "";
+               float locality = 0.0f;
+               float localityForSsd = 0.0f;
+               if (metaLocation != null) {
+                 ServerMetrics sl = 
master.getServerManager().getLoad(metaLocation);
+                 hostAndPort = URLEncoder.encode(metaLocation.getHostname()) + 
":" + master.getRegionServerInfoPort(metaLocation);
+                 if (sl != null) {
+                   Map<byte[], RegionMetrics> map = sl.getRegionMetrics();
+                   if (map.containsKey(meta.getRegionName())) {
+                     RegionMetrics load = map.get(meta.getRegionName());
+                     locality = load.getDataLocality();
+                     localityForSsd = load.getDataLocalityForSsd();
                    }
-                 %>
-               <tr>
-                 <td><%= escapeXml(meta.getRegionNameAsString()) %></td>
-                 <td><a href="http://<%= hostAndPort %>/rs-status"><%= 
StringEscapeUtils.escapeHtml4(hostAndPort) %></a></td>
-                 <td><%= locality%></td>
-                 <td><%= localityForSsd%></td>
-               </tr>
-             <%  } %>
-             <%} %>
-             </tbody>
-           </table>
-         </div>
-        <div class="tab-pane" id="metaTab_compactStats">
-          <table id="metaTableCompactStatsTable" class="tablesorter table 
table-striped">
-            <thead>
-            <tr>
-              <th>Name</th>
-              <th>Region Server</th>
-              <th>Num. Compacting Cells</th>
-              <th>Num. Compacted Cells</th>
-              <th>Remaining Cells</th>
-              <th>Compaction Progress</th>
-            </tr>
-            </thead>
-            <tbody>
-            <%
-              // NOTE: Presumes meta with one or more replicas
-              for (int j = 0; j < numMetaReplicas; j++) {
-                RegionInfo meta = RegionReplicaUtil.getRegionInfoForReplica(
+                 }
+               }
+             %>
+           <tr>
+             <td><%= escapeXml(meta.getRegionNameAsString()) %></td>
+             <td><a href="http://<%= hostAndPort %>/rs-status"><%= 
StringEscapeUtils.escapeHtml4(hostAndPort) %></a></td>
+             <td><%= locality%></td>
+             <td><%= localityForSsd%></td>
+           </tr>
+         <%  } %>
+         <%} %>
+         </tbody>
+       </table>
+     </div>
+    <div class="tab-pane" id="metaTab_compactStats">
+      <table id="metaTableCompactStatsTable" class="tablesorter table 
table-striped">
+        <thead>
+          <tr>
+            <th>Name</th>
+            <th>Region Server</th>
+            <th>Num. Compacting Cells</th>
+            <th>Num. Compacted Cells</th>
+            <th>Remaining Cells</th>
+            <th>Compaction Progress</th>
+          </tr>
+        </thead>
+        <tbody>
+        <%
+          // NOTE: Presumes meta with one or more replicas
+          for (int j = 0; j < numMetaReplicas; j++) {
+            RegionInfo meta = RegionReplicaUtil.getRegionInfoForReplica(
                         RegionInfoBuilder.FIRST_META_REGIONINFO, j);
-                //If a metaLocation is null, All of its info would be empty 
here to be displayed.
-                RegionStateNode rsn = 
master.getAssignmentManager().getRegionStates()
-                  .getRegionStateNode(meta);
-                ServerName metaLocation = rsn != null ? 
rsn.getRegionLocation() : null;
-                for (int i = 0; i < 1; i++) {
-                  //If metaLocation is null, default value below would be 
displayed in UI.
-                  String hostAndPort = "";
-                  long compactingCells = 0;
-                  long compactedCells = 0;
-                  String compactionProgress = "";
-                  if (metaLocation != null) {
-                    ServerMetrics sl = 
master.getServerManager().getLoad(metaLocation);
-                    hostAndPort = 
URLEncoder.encode(metaLocation.getHostname()) + ":" + 
master.getRegionServerInfoPort(metaLocation);
-                    if (sl != null) {
-                      Map<byte[], RegionMetrics> map = sl.getRegionMetrics();
-                      if (map.containsKey(meta.getRegionName())) {
-                        RegionMetrics load = map.get(meta.getRegionName());
-                        compactingCells = load.getCompactingCellCount();
-                        compactedCells = load.getCompactedCellCount();
-                        if (compactingCells > 0) {
-                          compactionProgress = String.format("%.2f", 100 * 
((float)
-                                  compactedCells / compactingCells)) + "%";
-                        }
-                      }
+            //If a metaLocation is null, All of its info would be empty here 
to be displayed.
+            RegionStateNode rsn = 
master.getAssignmentManager().getRegionStates()
+              .getRegionStateNode(meta);
+            ServerName metaLocation = rsn != null ? rsn.getRegionLocation() : 
null;
+            for (int i = 0; i < 1; i++) {
+              //If metaLocation is null, default value below would be 
displayed in UI.
+              String hostAndPort = "";
+              long compactingCells = 0;
+              long compactedCells = 0;
+              String compactionProgress = "";
+              if (metaLocation != null) {
+                ServerMetrics sl = 
master.getServerManager().getLoad(metaLocation);
+                hostAndPort = URLEncoder.encode(metaLocation.getHostname()) + 
":" + master.getRegionServerInfoPort(metaLocation);
+                if (sl != null) {
+                  Map<byte[], RegionMetrics> map = sl.getRegionMetrics();
+                  if (map.containsKey(meta.getRegionName())) {
+                    RegionMetrics load = map.get(meta.getRegionName());
+                    compactingCells = load.getCompactingCellCount();
+                    compactedCells = load.getCompactedCellCount();
+                    if (compactingCells > 0) {
+                      compactionProgress = String.format("%.2f", 100 * ((float)
+                              compactedCells / compactingCells)) + "%";
                     }
                   }
-            %>
-            <tr>
-              <td><%= escapeXml(meta.getRegionNameAsString()) %></td>
-              <td><a href="http://<%= hostAndPort %>/rs-status"><%= 
StringEscapeUtils.escapeHtml4(hostAndPort) %></a></td>
-              <td><%= String.format("%,1d", compactingCells)%></td>
-              <td><%= String.format("%,1d", compactedCells)%></td>
-              <td><%= String.format("%,1d", compactingCells - 
compactedCells)%></td>
-              <td><%= compactionProgress%></td>
-            </tr>
-            <%  } %>
-            <%} %>
-            </tbody>
-          </table>
-        </div>
-      </div>
+                }
+              }
+        %>
+          <tr>
+            <td><%= escapeXml(meta.getRegionNameAsString()) %></td>
+            <td><a href="http://<%= hostAndPort %>/rs-status"><%= 
StringEscapeUtils.escapeHtml4(hostAndPort) %></a></td>
+            <td><%= String.format("%,1d", compactingCells)%></td>
+            <td><%= String.format("%,1d", compactedCells)%></td>
+            <td><%= String.format("%,1d", compactingCells - 
compactedCells)%></td>
+            <td><%= compactionProgress%></td>
+          </tr>
+        <%  } %>
+        <%} %>
+        </tbody>
+      </table>
     </div>
-    <h2 id="meta-entries">Meta Entries</h2>
+  </div>
+</div>
+
+<h2 id="meta-entries">Meta Entries</h2>
 <%
   if (!metaBrowser.getErrorMessages().isEmpty()) {
     for (final String errorMessage : metaBrowser.getErrorMessages()) {
@@ -504,6 +568,7 @@
       final RegionInfo regionInfo = regionReplicaInfo.getRegionInfo();
       final ServerName serverName = regionReplicaInfo.getServerName();
       final RegionState.State regionState = regionReplicaInfo.getRegionState();
+      final int rsPort = master.getRegionServerInfoPort(serverName);
 
       final long seqNum = regionReplicaInfo.getSeqNum();
 
@@ -521,21 +586,21 @@
         splitRegions.entrySet().stream()
           .map(entry -> String.format(regionSpanFormat, entry.getKey(), 
entry.getValue().getRegionNameAsString()))
           .collect(Collectors.joining("<br/>"));
-%>
-        <tr>
-          <td title="<%= regionInfoColumnName %>"><%= regionNameDisplay %></td>
-          <td title="startKey"><%= startKeyDisplay %></td>
-          <td title="endKey"><%= endKeyDisplay %></td>
-          <td title="replicaId"><%= replicaIdDisplay %></td>
-          <td title="regionState"><%= regionStateDisplay %></td>
-          <td title="<%= serverColumnName + "," + startCodeColumnName %>"><%= 
serverName != null ? buildRegionServerLink(serverName, 
master.getRegionServerInfoPort(serverName), regionInfo, regionState) : "" 
%></td>
-          <td title="<%= seqNumColumnName %>"><%= seqNum %></td>
-          <td title="<%= serverNameColumnName %>"><%= targetServerName %></td>
-          <td><%= mergeRegionNames %></td>
-          <td><%= splitName %></td>
-        </tr>
-<%
-    }
+  %>
+    <tr>
+      <td title="<%= regionInfoColumnName %>"><%= regionNameDisplay %></td>
+      <td title="startKey"><%= startKeyDisplay %></td>
+      <td title="endKey"><%= endKeyDisplay %></td>
+      <td title="replicaId"><%= replicaIdDisplay %></td>
+      <td title="regionState"><%= regionStateDisplay %></td>
+      <td title="<%= serverColumnName + "," + startCodeColumnName %>"><%= 
buildRegionLink(serverName, rsPort, regionInfo, regionState) %></td>
+      <td title="<%= seqNumColumnName %>"><%= seqNum %></td>
+      <td title="<%= serverNameColumnName %>"><%= targetServerName %></td>
+      <td><%= mergeRegionNames %></td>
+      <td><%= splitName %></td>
+    </tr>
+  <%
+      }
 
     metaScanHasMore = results.hasMoreResults();
   }
@@ -588,555 +653,491 @@
 <%
   }
 %>
-            </select>
-            <button type="submit" class="btn btn-primary" 
style="display:inline; width:auto">
-              Filter Results
-            </button>
-          </div>
-        </form>
+        </select>
+        <button type="submit" class="btn btn-primary" style="display:inline; 
width:auto">
+          Filter Results
+        </button>
       </div>
-    </div>
-    <%} else {
-      RegionStates states = master.getAssignmentManager().getRegionStates();
-      Map<RegionState.State, List<RegionInfo>> regionStates = 
states.getRegionByStateOfTable(table.getName());
-      Map<String, RegionState.State> stateMap = new HashMap<>();
-      for (RegionState.State regionState : regionStates.keySet()) {
-        for (RegionInfo regionInfo : regionStates.get(regionState)) {
-          stateMap.put(regionInfo.getEncodedName(), regionState);
-        }
+    </form>
+  </div>
+</div>
+<%} else {
+  //Common tables
+  RegionStates states = master.getAssignmentManager().getRegionStates();
+  Map<RegionState.State, List<RegionInfo>> regionStates = 
states.getRegionByStateOfTable(table.getName());
+  Map<String, RegionState.State> stateMap = new HashMap<>();
+  for (RegionState.State regionState : regionStates.keySet()) {
+    for (RegionInfo regionInfo : regionStates.get(regionState)) {
+        stateMap.put(regionInfo.getEncodedName(), regionState);
+    }
+  }
+  RegionLocator r = master.getConnection().getRegionLocator(table.getName());
+
+  try {
+%>
+<h2>Table Attributes</h2>
+<table class="table table-striped">
+  <tr>
+      <th>Attribute Name</th>
+      <th>Value</th>
+      <th>Description</th>
+  </tr>
+  <tr>
+      <td>Enabled</td>
+      <td><%= master.getTableStateManager().isTableState(table.getName(), 
TableState.State.ENABLED) %></td>
+      <td>Is the table enabled</td>
+  </tr>
+  <tr>
+      <td>Compaction</td>
+      <td>
+<%
+  if (master.getTableStateManager().isTableState(table.getName(), 
TableState.State.ENABLED)) {
+    CompactionState compactionState = 
master.getCompactionState(table.getName());
+    %><%= compactionState==null?"UNKNOWN":compactionState %><%
+  } else {
+  %><%= CompactionState.NONE %><%
+  }
+%>
+      </td>
+      <td>Is the table compacting</td>
+  </tr>
+<%  if (showFragmentation) { %>
+  <tr>
+      <td>Fragmentation</td>
+      <td><%= frags.get(fqtn) != null ? frags.get(fqtn).intValue() + "%" : 
"n/a" %></td>
+      <td>How fragmented is the table. After a major compaction it is 0%.</td>
+  </tr>
+<%  } %>
+<%
+  if (quotasEnabled) {
+    TableName tn = TableName.valueOf(fqtn);
+    SpaceQuotaSnapshot masterSnapshot = null;
+    Quotas quota = QuotaTableUtil.getTableQuota(master.getConnection(), tn);
+    if (quota == null || !quota.hasSpace()) {
+      quota = QuotaTableUtil.getNamespaceQuota(master.getConnection(), 
tn.getNamespaceAsString());
+      if (quota != null) {
+        masterSnapshot = 
master.getQuotaObserverChore().getNamespaceQuotaSnapshots()
+                .get(tn.getNamespaceAsString());
       }
-      RegionLocator r = 
master.getConnection().getRegionLocator(table.getName());
-      try { %>
-    <h2>Table Attributes</h2>
-    <table class="table table-striped">
-      <tr>
-        <th>Attribute Name</th>
-        <th>Value</th>
-        <th>Description</th>
-      </tr>
-      <tr>
-        <td>Enabled</td>
-        <td><%= master.getTableStateManager().isTableState(table.getName(), 
TableState.State.ENABLED) %></td>
-        <td>Is the table enabled</td>
-      </tr>
-      <tr>
-        <td>Compaction</td>
-        <td>
-          <%
-            if (master.getTableStateManager().isTableState(table.getName(), 
TableState.State.ENABLED)) {
-              CompactionState compactionState = 
master.getCompactionState(table.getName());
-              %><%= compactionState==null?"UNKNOWN":compactionState %><%
-            } else {
-            %><%= CompactionState.NONE %><%
-            }
-            %>
-        </td>
-        <td>Is the table compacting</td>
-      </tr>
-      <%  if (showFragmentation) { %>
-      <tr>
-        <td>Fragmentation</td>
-        <td><%= frags.get(fqtn) != null ? frags.get(fqtn).intValue() + "%" : 
"n/a" %></td>
-        <td>How fragmented is the table. After a major compaction it is 
0%.</td>
-      </tr>
-      <%  } %>
-      <%
-        if (quotasEnabled) {
-          TableName tn = TableName.valueOf(fqtn);
-          SpaceQuotaSnapshot masterSnapshot = null;
-          Quotas quota = QuotaTableUtil.getTableQuota(master.getConnection(), 
tn);
-          if (quota == null || !quota.hasSpace()) {
-            quota = QuotaTableUtil.getNamespaceQuota(master.getConnection(), 
tn.getNamespaceAsString());
-            if (quota != null) {
-              masterSnapshot = 
master.getQuotaObserverChore().getNamespaceQuotaSnapshots()
-                      .get(tn.getNamespaceAsString());
-            }
-          } else {
-            masterSnapshot = 
master.getQuotaObserverChore().getTableQuotaSnapshots().get(tn);
-          }
-          if (quota != null && quota.hasSpace()) {
-            SpaceQuota spaceQuota = quota.getSpace();
-      %>
-      <tr>
-        <td>Space Quota</td>
-        <td>
-          <table>
-            <tr>
-              <th>Property</th>
-              <th>Value</th>
-            </tr>
-            <tr>
-              <td>Limit</td>
-              <td><%= StringUtils.byteDesc(spaceQuota.getSoftLimit()) %></td>
-            </tr>
-            <tr>
-              <td>Policy</td>
-              <td><%= spaceQuota.getViolationPolicy() %></td>
-            </tr>
-            <%
-              if (masterSnapshot != null) {
-            %>
-            <tr>
-              <td>Usage</td>
-              <td><%= StringUtils.byteDesc(masterSnapshot.getUsage()) %></td>
-            </tr>
-            <tr>
-              <td>State</td>
-              <td><%= masterSnapshot.getQuotaStatus().isInViolation() ? "In 
Violation" : "In Observance" %></td>
-            </tr>
-            <%
-              }
-            %>
-          </table>
-        </td>
-        <td>Information about a Space Quota on this table, if set.</td>
-      </tr>
-      <%
-        }
-        if (quota != null && quota.hasThrottle()) {
-          List<ThrottleSettings> throttles = 
QuotaSettingsFactory.fromTableThrottles(table.getName(), quota.getThrottle());
-          if (throttles.size() > 0) {
-      %>
-      <tr>
-        <td>Throttle Quota</td>
-        <td>
-          <table>
-            <tr>
-              <th>Limit</th>
-              <th>Type</th>
-              <th>TimeUnit</th>
-              <th>Scope</th>
-            </tr>
-            <%
-              for (ThrottleSettings throttle : throttles) {
-            %>
-            <tr>
-              <td><%= throttle.getSoftLimit() %></td>
-              <td><%= throttle.getThrottleType() %></td>
-              <td><%= throttle.getTimeUnit() %></td>
-              <td><%= throttle.getQuotaScope() %></td>
-            </tr>
-            <%
-              }
-            %>
-          </table>
-        </td>
-        <td>Information about a Throttle Quota on this table, if set.</td>
-      </tr>
-      <%
-            }
-          }
-        }
-      %>
-    </table>
-    <h2>Table Schema</h2>
-    <table class="table table-striped">
-    <%
-      ColumnFamilyDescriptor[] families = 
table.getDescriptor().getColumnFamilies();
-      Set<Bytes> familyKeySet = new HashSet<>();
-      for (ColumnFamilyDescriptor family: families) {
-        familyKeySet.addAll(family.getValues().keySet());
+    } else {
+      masterSnapshot = 
master.getQuotaObserverChore().getTableQuotaSnapshots().get(tn);
+    }
+    if (quota != null && quota.hasSpace()) {
+      SpaceQuota spaceQuota = quota.getSpace();
+%>
+  <tr>
+    <td>Space Quota</td>
+    <td>
+      <table>
+        <tr>
+          <th>Property</th>
+          <th>Value</th>
+        </tr>
+        <tr>
+          <td>Limit</td>
+          <td><%= StringUtils.byteDesc(spaceQuota.getSoftLimit()) %></td>
+        </tr>
+        <tr>
+          <td>Policy</td>
+          <td><%= spaceQuota.getViolationPolicy() %></td>
+        </tr>
+<%
+      if (masterSnapshot != null) {
+%>
+        <tr>
+          <td>Usage</td>
+          <td><%= StringUtils.byteDesc(masterSnapshot.getUsage()) %></td>
+        </tr>
+        <tr>
+          <td>State</td>
+          <td><%= masterSnapshot.getQuotaStatus().isInViolation() ? "In 
Violation" : "In Observance" %></td>
+        </tr>
+<%
       }
+%>
+      </table>
+    </td>
+    <td>Information about a Space Quota on this table, if set.</td>
+  </tr>
+<%
+    }
+  if (quota != null && quota.hasThrottle()) {
+    List<ThrottleSettings> throttles = 
QuotaSettingsFactory.fromTableThrottles(table.getName(), quota.getThrottle());
+    if (throttles.size() > 0) {
+%>
+  <tr>
+    <td>Throttle Quota</td>
+    <td>
+      <table>
+        <tr>
+          <th>Limit</th>
+          <th>Type</th>
+          <th>TimeUnit</th>
+          <th>Scope</th>
+        </tr>
+<%
+    for (ThrottleSettings throttle : throttles) {
+%>
+        <tr>
+          <td><%= throttle.getSoftLimit() %></td>
+          <td><%= throttle.getThrottleType() %></td>
+          <td><%= throttle.getTimeUnit() %></td>
+          <td><%= throttle.getQuotaScope() %></td>
+        </tr>
+<%
+    }
+%>
+      </table>
+    </td>
+    <td>Information about a Throttle Quota on this table, if set.</td>
+  </tr>
+<%
+    }
+   }
+ }
+%>
+</table>
+<h2>Table Schema</h2>
+<table class="table table-striped">
+<%
+  ColumnFamilyDescriptor[] families = 
table.getDescriptor().getColumnFamilies();
+  Set<Bytes> familyKeySet = new HashSet<>();
+  for (ColumnFamilyDescriptor family: families) {
+    familyKeySet.addAll(family.getValues().keySet());
+  }
+%>
+  <tr>
+      <th>Property \ Column Family Name</th>
+  <%
+      for (ColumnFamilyDescriptor family: families) {
+  %>
+  <th>
+    <%= StringEscapeUtils.escapeHtml4(family.getNameAsString()) %>
+      </th>
+      <% } %>
+    </tr>
+    <%
+    for (Bytes familyKey: familyKeySet) {
     %>
       <tr>
-        <th>Property \ Column Family Name</th>
+        <td>
+          <%= StringEscapeUtils.escapeHtml4(familyKey.toString()) %>
+        </td>
         <%
         for (ColumnFamilyDescriptor family: families) {
-        %>
-        <th>
-          <%= StringEscapeUtils.escapeHtml4(family.getNameAsString()) %>
-        </th>
-        <% } %>
-      </tr>
-        <%
-        for (Bytes familyKey: familyKeySet) {
-        %>
-          <tr>
-            <td>
-              <%= StringEscapeUtils.escapeHtml4(familyKey.toString()) %>
-            </td>
-            <%
-            for (ColumnFamilyDescriptor family: families) {
-              String familyValue = "-";
-              if(family.getValues().containsKey(familyKey)){
-                familyValue = family.getValues().get(familyKey).toString();
-              }
-            %>
-            <td>
-              <%= StringEscapeUtils.escapeHtml4(familyValue) %>
-            </td>
-            <% } %>
-          </tr>
-        <% } %>
-    </table>
-    <%
-      long totalReadReq = 0;
-      long totalWriteReq = 0;
-      long totalSize = 0;
-      long totalStoreFileCount = 0;
-      long totalMemSize = 0;
-      long totalCompactingCells = 0;
-      long totalCompactedCells = 0;
-      long totalBlocksTotalWeight = 0;
-      long totalBlocksLocalWeight = 0;
-      long totalBlocksLocalWithSsdWeight = 0;
-      String totalCompactionProgress = "";
-      String totalMemSizeStr = ZEROMB;
-      String totalSizeStr = ZEROMB;
-      String totalLocality = "";
-      String totalLocalityForSsd = "";
-      String urlRegionServer = null;
-      Map<ServerName, Integer> regDistribution = new TreeMap<>();
-      Map<ServerName, Integer> primaryRegDistribution = new TreeMap<>();
-      List<HRegionLocation> regions = r.getAllRegionLocations();
-      Map<RegionInfo, RegionMetrics> regionsToLoad = new LinkedHashMap<>();
-      Map<RegionInfo, ServerName> regionsToServer = new LinkedHashMap<>();
-      for (HRegionLocation hriEntry : regions) {
-        RegionInfo regionInfo = hriEntry.getRegionInfo();
-        ServerName addr = hriEntry.getServerName();
-        regionsToServer.put(regionInfo, addr);
-
-        if (addr != null) {
-          ServerMetrics sl = master.getServerManager().getLoad(addr);
-          if (sl != null) {
-            RegionMetrics regionMetrics = 
sl.getRegionMetrics().get(regionInfo.getRegionName());
-            regionsToLoad.put(regionInfo, regionMetrics);
-            if (regionMetrics != null) {
-              totalReadReq += regionMetrics.getReadRequestCount();
-              totalWriteReq += regionMetrics.getWriteRequestCount();
-              totalSize += 
regionMetrics.getStoreFileSize().get(Size.Unit.MEGABYTE);
-              totalStoreFileCount += regionMetrics.getStoreFileCount();
-              totalMemSize += 
regionMetrics.getMemStoreSize().get(Size.Unit.MEGABYTE);
-              totalStoreFileSizeMB += 
regionMetrics.getStoreFileSize().get(Size.Unit.MEGABYTE);
-              totalCompactingCells += regionMetrics.getCompactingCellCount();
-              totalCompactedCells += regionMetrics.getCompactedCellCount();
-              totalBlocksTotalWeight += regionMetrics.getBlocksTotalWeight();
-              totalBlocksLocalWeight += regionMetrics.getBlocksLocalWeight();
-              totalBlocksLocalWithSsdWeight += 
regionMetrics.getBlocksLocalWithSsdWeight();
-            } else {
-              RegionMetrics load0 = getEmptyRegionMetrics(regionInfo);
-              regionsToLoad.put(regionInfo, load0);
+          String familyValue = "-";
+          if(family.getValues().containsKey(familyKey)){
+              familyValue = family.getValues().get(familyKey).toString();
             }
-          } else{
-            RegionMetrics load0 = getEmptyRegionMetrics(regionInfo);
-            regionsToLoad.put(regionInfo, load0);
-          }
+          %>
+    <td>
+      <%= StringEscapeUtils.escapeHtml4(familyValue) %>
+    </td>
+    <% } %>
+  </tr>
+  <% } %>
+</table>
+<%
+  long totalReadReq = 0;
+  long totalWriteReq = 0;
+  long totalSizeUncompressed = 0;
+  long totalSize = 0;
+  long totalStoreFileCount = 0;
+  long totalMemSize = 0;
+  long totalCompactingCells = 0;
+  long totalCompactedCells = 0;
+  long totalBlocksTotalWeight = 0;
+  long totalBlocksLocalWeight = 0;
+  long totalBlocksLocalWithSsdWeight = 0;
+  String totalCompactionProgress = "";
+  String totalMemSizeStr = ZEROMB;
+  String totalSizeUncompressedStr = ZEROMB;
+  String totalSizeStr = ZEROMB;
+  String totalLocality = "";
+  String totalLocalityForSsd = "";
+  String urlRegionServer = null;
+  Map<ServerName, Integer> regDistribution = new TreeMap<>();
+  Map<ServerName, Integer> primaryRegDistribution = new TreeMap<>();
+  List<HRegionLocation> regions = r.getAllRegionLocations();
+  Map<RegionInfo, RegionMetrics> regionsToLoad = new LinkedHashMap<>();
+  Map<RegionInfo, ServerName> regionsToServer = new LinkedHashMap<>();
+  for (HRegionLocation hriEntry : regions) {
+    RegionInfo regionInfo = hriEntry.getRegionInfo();
+    ServerName addr = hriEntry.getServerName();
+    regionsToServer.put(regionInfo, addr);
+
+    if (addr != null) {
+      ServerMetrics sl = master.getServerManager().getLoad(addr);
+      if (sl != null) {
+        RegionMetrics regionMetrics = 
sl.getRegionMetrics().get(regionInfo.getRegionName());
+        regionsToLoad.put(regionInfo, regionMetrics);
+        if (regionMetrics != null) {
+          totalReadReq += regionMetrics.getReadRequestCount();
+          totalWriteReq += regionMetrics.getWriteRequestCount();
+          totalSizeUncompressed += 
regionMetrics.getUncompressedStoreFileSize().get(Size.Unit.MEGABYTE);
+          totalSize += 
regionMetrics.getStoreFileSize().get(Size.Unit.MEGABYTE);
+          totalStoreFileCount += regionMetrics.getStoreFileCount();
+          totalMemSize += 
regionMetrics.getMemStoreSize().get(Size.Unit.MEGABYTE);
+          totalStoreFileSizeMB += 
regionMetrics.getStoreFileSize().get(Size.Unit.MEGABYTE);
+          totalCompactingCells += regionMetrics.getCompactingCellCount();
+          totalCompactedCells += regionMetrics.getCompactedCellCount();
+          totalBlocksTotalWeight += regionMetrics.getBlocksTotalWeight();
+          totalBlocksLocalWeight += regionMetrics.getBlocksLocalWeight();
+          totalBlocksLocalWithSsdWeight += 
regionMetrics.getBlocksLocalWithSsdWeight();
         } else {
           RegionMetrics load0 = getEmptyRegionMetrics(regionInfo);
           regionsToLoad.put(regionInfo, load0);
         }
+      } else{
+        RegionMetrics load0 = getEmptyRegionMetrics(regionInfo);
+        regionsToLoad.put(regionInfo, load0);
       }
-      if (totalSize > 0) {
-        totalSizeStr = StringUtils.byteDesc(totalSize*1024l*1024);
-      }
-      if (totalMemSize > 0) {
-        totalMemSizeStr = StringUtils.byteDesc(totalMemSize*1024l*1024);
-      }
-      if (totalCompactingCells > 0) {
-        totalCompactionProgress = String.format("%.2f", 100 *
-                ((float) totalCompactedCells / totalCompactingCells)) + "%";
-      }
-      if (totalBlocksTotalWeight > 0) {
-        totalLocality = String.format("%.1f",
-          ((float) totalBlocksLocalWeight / totalBlocksTotalWeight));
-        totalLocalityForSsd = String.format("%.1f",
-          ((float) totalBlocksLocalWithSsdWeight / totalBlocksTotalWeight));
-      }
-      if(regions != null && regions.size() > 0) { %>
-    <h2>Table Regions</h2>
-    <div class="tabbable">
-      <ul class="nav nav-pills">
-        <li class="active">
-          <a href="#tab_baseStats" data-toggle="tab">Base Stats</a>
-        </li>
-        <li class="">
-          <a href="#tab_localityStats" data-toggle="tab">Localities</a>
-        </li>
-        <li class="">
-          <a href="#tab_compactStats" data-toggle="tab">Compactions</a>
-        </li>
-      </ul>
-      <div class="tab-content" style="padding-bottom: 9px; border-bottom: 1px 
solid #ddd;">
-        <div class="tab-pane active" id="tab_baseStats">
-          <table id="regionServerDetailsTable" class="tablesorter table 
table-striped">
-            <thead>
-            <tr>
-              <th>Name(<%= String.format("%,1d", regions.size())%>)</th>
-              <th>Region Server</th>
-              <th>ReadRequests<br>(<%= String.format("%,1d", 
totalReadReq)%>)</th>
-              <th>WriteRequests<br>(<%= String.format("%,1d", 
totalWriteReq)%>)</th>
-              <th>StorefileSize<br>(<%= totalSizeStr %>)</th>
-              <th>Num.Storefiles<br>(<%= String.format("%,1d", 
totalStoreFileCount)%>)</th>
-              <th>MemSize<br>(<%= totalMemSizeStr %>)</th>
-              <th>Start Key</th>
-              <th>End Key</th>
-              <th>Region State</th>
-              <%
-                if (withReplica) {
-              %>
-              <th>ReplicaID</th>
-              <%
-                }
-              %>
-            </tr>
-            </thead>
-            <tbody>
-            <%
-              List<Map.Entry<RegionInfo, RegionMetrics>> entryList = new 
ArrayList<>(regionsToLoad.entrySet());
-              numRegions = regions.size();
-              int numRegionsRendered = 0;
-              // render all regions
-              if (numRegionsToRender < 0) {
-                numRegionsToRender = numRegions;
+    } else {
+      RegionMetrics load0 = getEmptyRegionMetrics(regionInfo);
+      regionsToLoad.put(regionInfo, load0);
+    }
+  }
+  if (totalSize > 0) {
+    totalSizeStr = StringUtils.byteDesc(totalSize*1024l*1024);
+  }
+  if (totalSizeUncompressed > 0){
+    totalSizeUncompressedStr = 
StringUtils.byteDesc(totalSizeUncompressed*1024l*1024);
+  }
+  if (totalMemSize > 0) {
+    totalMemSizeStr = StringUtils.byteDesc(totalMemSize*1024l*1024);
+  }
+  if (totalCompactingCells > 0) {
+    totalCompactionProgress = String.format("%.2f", 100 *
+            ((float) totalCompactedCells / totalCompactingCells)) + "%";
+  }
+  if (totalBlocksTotalWeight > 0) {
+    totalLocality = String.format("%.1f",
+      ((float) totalBlocksLocalWeight / totalBlocksTotalWeight));
+    totalLocalityForSsd = String.format("%.1f",
+      ((float) totalBlocksLocalWithSsdWeight / totalBlocksTotalWeight));
+  }
+  if(regions != null && regions.size() > 0) { %>
+<h2>Table Regions</h2>
+<div class="tabbable">
+  <ul class="nav nav-pills">
+    <li class="active"><a href="#tab_baseStats" data-toggle="tab">Base 
Stats</a></li>
+    <li class=""><a href="#tab_localityStats" 
data-toggle="tab">Localities</a></li>
+    <li class=""><a href="#tab_compactStats" 
data-toggle="tab">Compactions</a></li>
+  </ul>
+  <div class="tab-content" style="padding-bottom: 9px; border-bottom: 1px 
solid #ddd;">
+    <div class="tab-pane active" id="tab_baseStats">
+      <table id="tableBaseStatsTable" class="tablesorter table table-striped">
+        <thead>
+          <tr>
+            <th>Name(<%= String.format("%,1d", regions.size())%>)</th>
+            <th>Region Server</th>
+            <th>ReadRequests<br>(<%= String.format("%,1d", 
totalReadReq)%>)</th>
+            <th>WriteRequests<br>(<%= String.format("%,1d", 
totalWriteReq)%>)</th>
+            <th>Uncompressed StoreFileSize<br>(<%= totalSizeUncompressedStr 
%>)</th>
+            <th>StorefileSize<br>(<%= totalSizeStr %>)</th>
+            <th>Num.Storefiles<br>(<%= String.format("%,1d", 
totalStoreFileCount)%>)</th>
+            <th>MemSize<br>(<%= totalMemSizeStr %>)</th>
+            <th>Start Key</th>
+            <th>End Key</th>
+            <th>Region State</th>
+            <th>ReplicaID</th>
+          </tr>
+        </thead>
+        <tbody>
+        <%
+          List<Map.Entry<RegionInfo, RegionMetrics>> entryList = new 
ArrayList<>(regionsToLoad.entrySet());
+          numRegions = regions.size();
+          int numRegionsRendered = 0;
+          // render all regions
+          if (numRegionsToRender < 0) {
+            numRegionsToRender = numRegions;
+          }
+          for (Map.Entry<RegionInfo, RegionMetrics> hriEntry : entryList) {
+            RegionInfo regionInfo = hriEntry.getKey();
+            ServerName addr = regionsToServer.get(regionInfo);
+            RegionMetrics load = hriEntry.getValue();
+            String readReq = "N/A";
+            String writeReq = "N/A";
+            String regionSizeUncompressed = ZEROMB;
+            String regionSize = ZEROMB;
+            String fileCount = "N/A";
+            String memSize = ZEROMB;
+            String state = "N/A";
+            if (load != null) {
+              readReq = String.format("%,1d", load.getReadRequestCount());
+              writeReq = String.format("%,1d", load.getWriteRequestCount());
+              double rSizeUncompressed = 
load.getUncompressedStoreFileSize().get(Size.Unit.BYTE);
+              if (rSizeUncompressed > 0) {
+                regionSizeUncompressed = 
StringUtils.byteDesc((long)rSizeUncompressed);
               }
-              for (Map.Entry<RegionInfo, RegionMetrics> hriEntry : entryList) {
-                RegionInfo regionInfo = hriEntry.getKey();
-                ServerName addr = regionsToServer.get(regionInfo);
-                RegionMetrics load = hriEntry.getValue();
-                String readReq = "N/A";
-                String writeReq = "N/A";
-                String regionSize = ZEROMB;
-                String fileCount = "N/A";
-                String memSize = ZEROMB;
-                String state = "N/A";
-                if (load != null) {
-                  readReq = String.format("%,1d", load.getReadRequestCount());
-                  writeReq = String.format("%,1d", 
load.getWriteRequestCount());
-                  double rSize = load.getStoreFileSize().get(Size.Unit.BYTE);
-                  if (rSize > 0) {
-                    regionSize = StringUtils.byteDesc((long)rSize);
-                  }
-                  fileCount = String.format("%,1d", load.getStoreFileCount());
-                  double mSize = load.getMemStoreSize().get(Size.Unit.BYTE);
-                  if (mSize > 0) {
-                    memSize = StringUtils.byteDesc((long)mSize);
-                  }
-                }
-                if (stateMap.containsKey(regionInfo.getEncodedName())) {
-                  state = stateMap.get(regionInfo.getEncodedName()).toString();
-                }
-                if (addr != null) {
-                  ServerMetrics sl = master.getServerManager().getLoad(addr);
-                  // This port might be wrong if RS actually ended up using 
something else.
-                  urlRegionServer =
-                          "//" + URLEncoder.encode(addr.getHostname()) + ":" + 
master.getRegionServerInfoPort(addr) + "/rs-status";
-                  if(sl != null) {
-                    Integer i = regDistribution.get(addr);
-                    if (null == i) i = Integer.valueOf(0);
-                    regDistribution.put(addr, i + 1);
-                    if (withReplica && 
RegionReplicaUtil.isDefaultReplica(regionInfo.getReplicaId())) {
-                      i = primaryRegDistribution.get(addr);
-                      if (null == i) i = Integer.valueOf(0);
-                      primaryRegDistribution.put(addr, i+1);
-                    }
-                  }
-                }
-                if (numRegionsRendered < numRegionsToRender) {
-                  numRegionsRendered++;
-            %>
-            <tr>
-              <td><%= 
escapeXml(Bytes.toStringBinary(regionInfo.getRegionName())) %></td>
-              <%
-                if (urlRegionServer != null) {
-              %>
-              <td>
-                <a href="<%= urlRegionServer %>"><%= addr == null? "-": 
StringEscapeUtils.escapeHtml4(addr.getHostname().toString()) + ":" + 
master.getRegionServerInfoPort(addr) %></a>
-              </td>
-              <%
-              } else {
-              %>
-              <td class="undeployed-region">not deployed</td>
-              <%
-                }
-              %>
-              <td><%= readReq%></td>
-              <td><%= writeReq%></td>
-              <td><%= regionSize%></td>
-              <td><%= fileCount%></td>
-              <td><%= memSize%></td>
-              <td><%= 
escapeXml(Bytes.toStringBinary(regionInfo.getStartKey()))%></td>
-              <td><%= 
escapeXml(Bytes.toStringBinary(regionInfo.getEndKey()))%></td>
-              <td><%= state%></td>
-              <%
-                if (withReplica) {
-              %>
-              <td><%= regionInfo.getReplicaId() %></td>
-              <%
-                }
-              %>
-            </tr>
-            <% } %>
-            <% } %>
-            </tbody>
-          </table>
-          <% if (numRegions > numRegionsRendered) {
-            String allRegionsUrl = "?name=" + URLEncoder.encode(fqtn,"UTF-8") 
+ "&numRegions=all";
-          %>
-          <p>This table has <b><%= numRegions %></b> regions in total, in 
order to improve the page load time,
-            only <b><%= numRegionsRendered %></b> regions are displayed here, 
<a href="<%= allRegionsUrl %>">click
-              here</a> to see all regions.</p>
-          <% } %>
-        </div>
-        <div class="tab-pane" id="tab_localityStats">
-          <table id="regionServerDetailsTable" class="tablesorter table 
table-striped">
-            <thead>
-              <tr>
-                <th>Name(<%= String.format("%,1d", regions.size())%>)</th>
-                <th>Region Server</th>
-                <th>Locality<br>(<%= totalLocality %>)</th>
-                <th>LocalityForSsd<br>(<%= totalLocalityForSsd %>)</th>
-              </tr>
-            </thead>
-            <tbody>
-            <%
-              numRegionsRendered = 0;
-              for (Map.Entry<RegionInfo, RegionMetrics> hriEntry : entryList) {
-                RegionInfo regionInfo = hriEntry.getKey();
-                ServerName addr = regionsToServer.get(regionInfo);
-                RegionMetrics load = hriEntry.getValue();
-                float locality = 0.0f;
-                float localityForSsd = 0.0f;
-                String state = "N/A";
-                if (load != null) {
-                  locality = load.getDataLocality();
-                  localityForSsd = load.getDataLocalityForSsd();
-                }
-                if (addr != null) {
-                  // This port might be wrong if RS actually ended up using 
something else.
-                  urlRegionServer =
-                    "//" + URLEncoder.encode(addr.getHostname()) + ":" + 
master.getRegionServerInfoPort(addr) + "/rs-status";
-                }
-                if (numRegionsRendered < numRegionsToRender) {
-                  numRegionsRendered++;
-            %>
-            <tr>
-              <td><%= 
escapeXml(Bytes.toStringBinary(regionInfo.getRegionName())) %></td>
-              <%
-              if (urlRegionServer != null) {
-              %>
-              <td>
-                 <a href="<%= urlRegionServer %>"><%= addr == null? "-": 
StringEscapeUtils.escapeHtml4(addr.getHostname().toString()) + ":" + 
master.getRegionServerInfoPort(addr) %></a>
-              </td>
-              <%
-              } else {
-              %>
-              <td class="undeployed-region">not deployed</td>
-              <%
+              double rSize = load.getStoreFileSize().get(Size.Unit.BYTE);
+              if (rSize > 0) {
+                regionSize = StringUtils.byteDesc((long)rSize);
               }
-              %>
-              <td><%= locality%></td>
-              <td><%= localityForSsd%></td>
-            </tr>
-            <% } %>
-            <% } %>
-            </tbody>
-          </table>
-        </div>
-        <div class="tab-pane" id="tab_compactStats">
-          <table id="tableCompactStatsTable" class="tablesorter table 
table-striped">
-            <thead>
-            <tr>
-              <th>Name(<%= String.format("%,1d", regions.size())%>)</th>
-              <th>Region Server</th>
-              <th>Num. Compacting Cells<br>(<%= String.format("%,1d", 
totalCompactingCells)%>)</th>
-              <th>Num. Compacted Cells<br>(<%= String.format("%,1d", 
totalCompactedCells)%>)</th>
-              <th>Remaining Cells<br>(<%= String.format("%,1d", 
totalCompactingCells-totalCompactedCells)%>)</th>
-              <th>Compaction Progress<br>(<%= totalCompactionProgress %>)</th>
-            </tr>
-            </thead>
-            <tbody>
-            <%
-              numRegionsRendered = 0;
-              for (Map.Entry<RegionInfo, RegionMetrics> hriEntry : entryList) {
-                RegionInfo regionInfo = hriEntry.getKey();
-                ServerName addr = regionsToServer.get(regionInfo);
-                RegionMetrics load = hriEntry.getValue();
-                long compactingCells = 0;
-                long compactedCells = 0;
-                String compactionProgress = "";
-                if (load != null) {
-                  compactingCells = load.getCompactingCellCount();
-                  compactedCells = load.getCompactedCellCount();
-                  if (compactingCells > 0) {
-                    compactionProgress = String.format("%.2f", 100 * ((float)
-                            compactedCells / compactingCells)) + "%";
-                  }
-                }
-                if (addr != null) {
-                  // This port might be wrong if RS actually ended up using 
something else.
-                  urlRegionServer =
-                          "//" + URLEncoder.encode(addr.getHostname()) + ":" + 
master.getRegionServerInfoPort(addr) + "/rs-status";
-                }
-                if (numRegionsRendered < numRegionsToRender) {
-                  numRegionsRendered++;
-            %>
-            <tr>
-              <td><%= 
escapeXml(Bytes.toStringBinary(regionInfo.getRegionName())) %></td>
-              <%
-                if (urlRegionServer != null) {
-              %>
-              <td>
-                <a href="<%= urlRegionServer %>"><%= addr == null? "-": 
StringEscapeUtils.escapeHtml4(addr.getHostname().toString()) + ":" + 
master.getRegionServerInfoPort(addr) %></a>
-              </td>
-              <%
-              } else {
-              %>
-              <td class="undeployed-region">not deployed</td>
-              <%
+              fileCount = String.format("%,1d", load.getStoreFileCount());
+              double mSize = load.getMemStoreSize().get(Size.Unit.BYTE);
+              if (mSize > 0) {
+                memSize = StringUtils.byteDesc((long)mSize);
+              }
+            }
+
+            if (stateMap.containsKey(regionInfo.getEncodedName())) {
+              state = stateMap.get(regionInfo.getEncodedName()).toString();
+            }
+
+            if (addr != null) {
+              ServerMetrics sl = master.getServerManager().getLoad(addr);
+              if(sl != null) {
+                Integer i = regDistribution.get(addr);
+                if (null == i) i = Integer.valueOf(0);
+                regDistribution.put(addr, i + 1);
+                if 
(RegionReplicaUtil.isDefaultReplica(regionInfo.getReplicaId())) {
+                  i = primaryRegDistribution.get(addr);
+                  if (null == i) i = Integer.valueOf(0);
+                  primaryRegDistribution.put(addr, i+1);
                 }
-              %>
-              <td><%= String.format("%,1d", compactingCells)%></td>
-              <td><%= String.format("%,1d", compactedCells)%></td>
-              <td><%= String.format("%,1d", compactingCells - 
compactedCells)%></td>
-              <td><%= compactionProgress%></td>
-            </tr>
-            <% } %>
-            <% } %>
-            </tbody>
-          </table>
-          <% if (numRegions > numRegionsRendered) {
-            String allRegionsUrl = "?name=" + URLEncoder.encode(fqtn,"UTF-8") 
+ "&numRegions=all";
-          %>
-          <p>This table has <b><%= numRegions %></b> regions in total, in 
order to improve the page load time,
-            only <b><%= numRegionsRendered %></b> regions are displayed here, 
<a href="<%= allRegionsUrl %>">click
-              here</a> to see all regions.</p>
-          <% } %>
-        </div>
-      </div>
+              }
+            }
+            if (numRegionsRendered < numRegionsToRender) {
+              numRegionsRendered++;
+        %>
+        <tr>
+          <td><%= escapeXml(Bytes.toStringBinary(regionInfo.getRegionName())) 
%></td>
+          <%= buildRegionDeployedServerTag(regionInfo, master, 
regionsToServer) %>
+          <td><%= readReq%></td>
+          <td><%= writeReq%></td>
+          <td><%= regionSizeUncompressed%></td>
+          <td><%= regionSize%></td>
+          <td><%= fileCount%></td>
+          <td><%= memSize%></td>
+          <td><%= 
escapeXml(Bytes.toStringBinary(regionInfo.getStartKey()))%></td>
+          <td><%= 
escapeXml(Bytes.toStringBinary(regionInfo.getEndKey()))%></td>
+          <td><%= state%></td>
+          <td><%= regionInfo.getReplicaId() %></td>
+        </tr>
+        <% } %>
+        <% } %>
+        </tbody>
+      </table>
+      <%= moreRegionsToRender(numRegionsRendered, numRegions, fqtn) %>
     </div>
-    <h2>Regions by Region Server</h2>
-    <%
-      if (withReplica) {
-    %>
-    <table id="regionServerTable" class="tablesorter table 
table-striped"><thead><tr><th>Region Server</th><th>Region 
Count</th><th>Primary Region Count</th></tr></thead>
-        <%
-} else {
-%>
-      <table id="regionServerTable" class="tablesorter table 
table-striped"><thead><tr><th>Region Server</th><th>Region 
Count</th></tr></thead>
+    <div class="tab-pane" id="tab_localityStats">
+      <table id="tableLocalityStatsTable" class="tablesorter table 
table-striped">
+        <thead>
+          <tr>
+            <th>Name(<%= String.format("%,1d", regions.size())%>)</th>
+            <th>Region Server</th>
+            <th>Locality<br>(<%= totalLocality %>)</th>
+            <th>LocalityForSsd<br>(<%= totalLocalityForSsd %>)</th>
+          </tr>
+        </thead>
         <tbody>
         <%
-          }
+          numRegionsRendered = 0;
+          for (Map.Entry<RegionInfo, RegionMetrics> hriEntry : entryList) {
+            RegionInfo regionInfo = hriEntry.getKey();
+            ServerName addr = regionsToServer.get(regionInfo);
+            RegionMetrics load = hriEntry.getValue();
+            float locality = 0.0f;
+            float localityForSsd = 0.0f;
+            String state = "N/A";
+            if (load != null) {
+              locality = load.getDataLocality();
+              localityForSsd = load.getDataLocalityForSsd();
+            }
+
+            if (numRegionsRendered < numRegionsToRender) {
+              numRegionsRendered++;
         %>
+        <tr>
+          <td><%= escapeXml(Bytes.toStringBinary(regionInfo.getRegionName())) 
%></td>
+          <%= buildRegionDeployedServerTag(regionInfo, master, 
regionsToServer) %>
+          <td><%= locality%></td>
+          <td><%= localityForSsd%></td>
+        </tr>
+        <% } %>
+        <% } %>
+        </tbody>
+      </table>
+      <%= moreRegionsToRender(numRegionsRendered, numRegions, fqtn) %>
+    </div>
+    <div class="tab-pane" id="tab_compactStats">
+      <table id="tableCompactStatsTable" class="tablesorter table 
table-striped">
+        <thead>
+          <tr>
+            <th>Name(<%= String.format("%,1d", regions.size())%>)</th>
+            <th>Region Server</th>
+            <th>Num. Compacting Cells<br>(<%= String.format("%,1d", 
totalCompactingCells)%>)</th>
+            <th>Num. Compacted Cells<br>(<%= String.format("%,1d", 
totalCompactedCells)%>)</th>
+            <th>Remaining Cells<br>(<%= String.format("%,1d", 
totalCompactingCells-totalCompactedCells)%>)</th>
+            <th>Compaction Progress<br>(<%= totalCompactionProgress %>)</th>
+          </tr>
+        </thead>
+        <tbody>
         <%
-          for (Map.Entry<ServerName, Integer> rdEntry : 
regDistribution.entrySet()) {
-            ServerName addr = rdEntry.getKey();
-            String url = "//" + URLEncoder.encode(addr.getHostname()) + ":" + 
master.getRegionServerInfoPort(addr) + "/rs-status";
+          numRegionsRendered = 0;
+          for (Map.Entry<RegionInfo, RegionMetrics> hriEntry : entryList) {
+            RegionInfo regionInfo = hriEntry.getKey();
+            ServerName addr = regionsToServer.get(regionInfo);
+            RegionMetrics load = hriEntry.getValue();
+            long compactingCells = 0;
+            long compactedCells = 0;
+            String compactionProgress = "";
+            if (load != null) {
+              compactingCells = load.getCompactingCellCount();
+              compactedCells = load.getCompactedCellCount();
+              if (compactingCells > 0) {
+                compactionProgress = String.format("%.2f", 100 * ((float)
+                        compactedCells / compactingCells)) + "%";
+              }
+            }
+
+            if (numRegionsRendered < numRegionsToRender) {
+              numRegionsRendered++;
         %>
         <tr>
-          <td><a href="<%= url %>"><%= 
StringEscapeUtils.escapeHtml4(addr.getHostname().toString()) + ":" + 
master.getRegionServerInfoPort(addr) %></a></td>
-          <td><%= rdEntry.getValue()%></td>
-          <%
-            if (withReplica) {
-          %>
-          <td><%= primaryRegDistribution.get(addr)%></td>
-          <%
-            }
-          %>
+          <td><%= escapeXml(Bytes.toStringBinary(regionInfo.getRegionName())) 
%></td>
+          <%= buildRegionDeployedServerTag(regionInfo, master, 
regionsToServer) %>
+          <td><%= String.format("%,1d", compactingCells)%></td>
+          <td><%= String.format("%,1d", compactedCells)%></td>
+          <td><%= String.format("%,1d", compactingCells - 
compactedCells)%></td>
+          <td><%= compactionProgress%></td>
         </tr>
         <% } %>
+        <% } %>
         </tbody>
       </table>
-        <% }
+      <%= moreRegionsToRender(numRegionsRendered, numRegions, fqtn) %>
+    </div>
+  </div>
+</div>
+
+<h2>Regions by Region Server</h2>
+<table id="regionServerTable" class="tablesorter table table-striped">
+  <thead>
+    <tr>
+      <th>Region Server</th><th>Region Count</th><th>Primary Region Count</th>
+    </tr>
+  </thead>
+
+  <tbody>
+  <%
+    for (Map.Entry<ServerName, Integer> rdEntry : regDistribution.entrySet()) {
+      ServerName addr = rdEntry.getKey();
+      String url = "//" + URLEncoder.encode(addr.getHostname()) + ":"
+        + master.getRegionServerInfoPort(addr) + "/rs-status";
+  %>
+      <tr>
+        <td><a href="<%= url %>"><%= 
StringEscapeUtils.escapeHtml4(addr.getHostname().toString())
+          + ":" + master.getRegionServerInfoPort(addr) %></a></td>
+        <td><%= rdEntry.getValue()%></td>
+        <td><%= primaryRegDistribution.get(addr) == null ? 0 : 
primaryRegDistribution.get(addr)%></td>
+      </tr>
+  <% } %>
+  </tbody>
+</table>
+
+<% }
 } catch(Exception ex) { %>
   Unknown Issue with Regions
   <div 
onclick="document.getElementById('closeStackTrace').style.display='block';document.getElementById('openStackTrace').style.display='none';">
@@ -1182,105 +1183,87 @@
         </tr>
       </table>
 
-        <% if (!readOnly) { %>
-      <p><hr/></p>
-      Actions:
-      <p>
-      <center>
-        <table class="table" style="border: 0;" width="95%" >
-          <tr>
-            <form method="get">
-              <input type="hidden" name="action" value="compact" />
-              <input type="hidden" name="name" value="<%= escaped_fqtn %>" />
-              <td class="centered">
-                <input style="font-size: 12pt; width: 10em" type="submit" 
value="Compact" class="btn" />
-              </td>
-              <td style="text-align: center;">
-                <input type="text" name="key" size="40" placeholder="Row Key 
(optional)" />
-              </td>
-              <td>
-                This action will force a compaction of all regions of the 
table, or,
-                if a key is supplied, only the region containing the
-                given key.
-              </td>
-            </form>
-          </tr>
-          <tr>
-            <form method="get">
-              <input type="hidden" name="action" value="split" />
-              <input type="hidden" name="name" value="<%= escaped_fqtn %>" />
-              <td class="centered">
-                <input style="font-size: 12pt; width: 10em" type="submit" 
value="Split" class="btn" />
-              </td>
-              <td style="text-align: center;">
-                <input type="text" name="key" size="40" placeholder="Row Key 
(optional)" />
-              </td>
-              <td>
-                This action will force a split of all eligible
-                regions of the table, or, if a key is supplied, only the 
region containing the
-                given key. An eligible region is one that does not contain any 
references to
-                other regions. Split requests for noneligible regions will be 
ignored.
-              </td>
-            </form>
-          </tr>
-          <tr>
-            <form method="get">
-              <input type="hidden" name="action" value="merge" />
-              <input type="hidden" name="name" value="<%= escaped_fqtn %>" />
-              <td class="centered">
-                <input style="font-size: 12pt; width: 10em" type="submit" 
value="Merge" class="btn" />
-              </td>
-              <td style="text-align: center;">
-                <input type="text" name="left" size="40" placeholder="Region 
Key (required)" />
-                <input type="text" name="right" size="40" placeholder="Region 
Key (required)" />
-              </td>
-              <td>
-                This action will merge two regions of the table, Merge 
requests for
-                noneligible regions will be ignored.
-              </td>
-            </form>
-          </tr>
-        </table>
-      </center>
-      </p>
-        <% } %>
-  </div>
+<% if (!readOnly) { %>
+<p><hr/></p>
+Actions:
+<p>
+<center>
+<table class="table" style="border: 0;" width="95%" >
+<tr>
+  <form method="get">
+  <input type="hidden" name="action" value="major compact" />
+  <input type="hidden" name="name" value="<%= escaped_fqtn %>" />
+  <td class="centered">
+    <input style="font-size: 12pt; width: 10em" type="submit" value="Major 
Compact" class="btn" />
+  </td>
+  <td style="text-align: center;">
+    <input type="text" name="key" size="40" placeholder="Row Key (optional)" />
+  </td>
+  <td>
+    This action will force a major compaction of all regions of the table, or,
+    if a key is supplied, only the region major containing the
+    given key.
+  </td>
+  </form>
+</tr>
+<tr>
+  <form method="get">
+  <input type="hidden" name="action" value="compact" />
+  <input type="hidden" name="name" value="<%= escaped_fqtn %>" />
+  <td class="centered">
+    <input style="font-size: 12pt; width: 10em" type="submit" value="Compact" 
class="btn" />
+  </td>
+  <td style="text-align: center;">
+    <input type="text" name="key" size="40" placeholder="Row Key (optional)" />
+  </td>
+  <td>
+    This action will force a compaction of all regions of the table, or,
+    if a key is supplied, only the region containing the
+    given key.
+  </td>
+  </form>
+</tr>
+<tr>
+  <form method="get">
+  <input type="hidden" name="action" value="split" />
+  <input type="hidden" name="name" value="<%= escaped_fqtn %>" />
+  <td class="centered">
+    <input style="font-size: 12pt; width: 10em" type="submit" value="Split" 
class="btn" />
+  </td>
+  <td style="text-align: center;">
+    <input type="text" name="key" size="40" placeholder="Row Key (optional)" />
+  </td>
+  <td>
+      This action will force a split of all eligible
+      regions of the table, or, if a key is supplied, only the region 
containing the
+      given key. An eligible region is one that does not contain any 
references to
+      other regions. Split requests for noneligible regions will be ignored.
+  </td>
+  </form>
+</tr>
+<tr>
+  <form method="get">
+  <input type="hidden" name="action" value="merge" />
+  <input type="hidden" name="name" value="<%= escaped_fqtn %>" />
+  <td class="centered">
+    <input style="font-size: 12pt; width: 10em" type="submit" value="Merge" 
class="btn" />
+  </td>
+  <td style="text-align: center;">
+    <input type="text" name="left" size="40" required="required" 
placeholder="Region Key (required)" />
+    <input type="text" name="right" size="40" required="required" 
placeholder="Region Key (required)" />
+  </td>
+  <td>
+    This action will merge two regions of the table, Merge requests for
+    noneligible regions will be ignored.
+  </td>
+  </form>
+</tr>
+</table>
+</center>
+</p>
+<% } %>
 </div>
-<% }
-} catch(TableNotFoundException e) { %>
-<div class="container-fluid content">
-  <div class="row inner_header">
-    <div class="page-header">
-      <h1>Table not found</h1>
-    </div>
-  </div>
-  <p><hr><p>
-  <p>Go <a href="javascript:history.back()">Back</a>
-</div> <%
-} catch(IllegalArgumentException e) { %>
-<div class="container-fluid content">
-  <div class="row inner_header">
-    <div class="page-header">
-      <h1>Table qualifier must not be empty</h1>
-    </div>
-  </div>
-  <p><hr><p>
-  <p>Go <a href="javascript:history.back()">Back</a>
-</div> <%
-  }
-}
-else { // handle the case for fqtn is null or master is not initialized with 
error message + redirect
-%>
-<div class="container-fluid content">
-  <div class="row inner_header">
-    <div class="page-header">
-      <h1>Table not ready</h1>
-    </div>
-  </div>
-  <p><hr><p>
-  <jsp:include page="redirect.jsp" />
 </div>
-<% } %>
 
 <jsp:include page="footer.jsp" />
 <script src="/static/js/jquery.min.js" type="text/javascript"></script>
@@ -1288,78 +1271,94 @@ else { // handle the case for fqtn is null or master is 
not initialized with err
 <script src="/static/js/bootstrap.min.js" type="text/javascript"></script>
 
 <script>
-    $(document).ready(function()
+$(document).ready(function()
+    {
+        $.tablesorter.addParser(
         {
-            $.tablesorter.addParser(
-                {
-                    id: 'filesize',
-                    is: function(s) {
-                        return s.match(new RegExp( /([\.0-9]+)\ 
(B|KB|MB|GB|TB)/ ));
-                    },
-                    format: function(s) {
-                        var suf = s.match(new RegExp( /(B|KB|MB|GB|TB)$/ ))[1];
-                        var num = parseFloat(s.match( new RegExp( /([\.0-9]+)\ 
(B|KB|MB|GB|TB)/ ))[0]);
-                        switch(suf) {
-                            case 'B':
-                                return num;
-                            case 'KB':
-                                return num * 1024;
-                            case 'MB':
-                                return num * 1024 * 1024;
-                            case 'GB':
-                                return num * 1024 * 1024 * 1024;
-                            case 'TB':
-                                return num * 1024 * 1024 * 1024 * 1024;
-                        }
-                    },
-                    type: 'numeric'
-                });
-            $.tablesorter.addParser(
-                {
-                    id: "separator",
-                    is: function (s) {
-                        return /^[0-9]?[0-9,]*$/.test(s);
-                    }, format: function (s) {
-                        return $.tablesorter.formatFloat( s.replace(/,/g,'') );
-                    }, type: "numeric"
-                });
-            $("#regionServerTable").tablesorter({
-                headers: {
-                    1: {sorter: 'separator'}
-                }
-            });
-            $("#regionServerDetailsTable").tablesorter({
-                headers: {
-                    2: {sorter: 'separator'},
-                    3: {sorter: 'separator'},
-                    4: {sorter: 'filesize'},
-                    5: {sorter: 'separator'},
-                    6: {sorter: 'filesize'}
-                }
-            });
-            $("#tableRegionTable").tablesorter({
-                headers: {
-                    2: {sorter: 'separator'},
-                    3: {sorter: 'separator'},
-                    4: {sorter: 'filesize'},
-                    5: {sorter: 'separator'},
-                    6: {sorter: 'filesize'}
-                }
-            });
-            $("#tableCompactStatsTable").tablesorter({
-                headers: {
-                    2: {sorter: 'separator'},
-                    3: {sorter: 'separator'},
-                    4: {sorter: 'separator'}
+            id: 'filesize',
+            is: function(s) {
+                return s.match(new RegExp( /([\.0-9]+)\ (B|KB|MB|GB|TB)/ ));
+            },
+            format: function(s) {
+                var suf = s.match(new RegExp( /(B|KB|MB|GB|TB)$/ ))[1];
+                var num = parseFloat(s.match( new RegExp( /([\.0-9]+)\ 
(B|KB|MB|GB|TB)/ ))[0]);
+                switch(suf) {
+                    case 'B':
+                        return num;
+                    case 'KB':
+                        return num * 1024;
+                    case 'MB':
+                        return num * 1024 * 1024;
+                    case 'GB':
+                        return num * 1024 * 1024 * 1024;
+                    case 'TB':
+                        return num * 1024 * 1024 * 1024 * 1024;
                 }
-            });
-            $("#metaTableCompactStatsTable").tablesorter({
-                headers: {
-                    2: {sorter: 'separator'},
-                    3: {sorter: 'separator'},
-                    4: {sorter: 'separator'}
-                }
-            });
-        }
-    );
+            },
+            type: 'numeric'
+        });
+        $.tablesorter.addParser(
+        {
+            id: "separator",
+            is: function (s) {
+                return /^[0-9]?[0-9,]*$/.test(s);
+            }, format: function (s) {
+                return $.tablesorter.formatFloat( s.replace(/,/g,'') );
+            }, type: "numeric"
+        });
+        $("#regionServerTable").tablesorter({
+            headers: {
+                1: {sorter: 'separator'}
+            }
+        });
+        $("#tableBaseStatsTable").tablesorter({
+            headers: {
+                2: {sorter: 'separator'},
+                3: {sorter: 'separator'},
+                4: {sorter: 'filesize'},
+                5: {sorter: 'separator'},
+                6: {sorter: 'filesize'},
+                7: {empty: 'emptyMin'},
+                8: {empty: 'emptyMax'}
+            }
+        });
+        $("#metaTableBaseStatsTable").tablesorter({
+            headers: {
+                2: {sorter: 'separator'},
+                3: {sorter: 'separator'},
+                4: {sorter: 'filesize'},
+                5: {sorter: 'separator'},
+                6: {sorter: 'filesize'},
+                7: {empty: 'emptyMin'},
+                8: {empty: 'emptyMax'}
+            }
+        });
+        $("#tableLocalityStatsTable").tablesorter({
+            headers: {
+                2: {sorter: 'separator'},
+                3: {sorter: 'separator'}
+            }
+        });
+        $("#metaTableLocalityStatsTable").tablesorter({
+            headers: {
+                2: {sorter: 'separator'},
+                3: {sorter: 'separator'}
+            }
+        });
+        $("#tableCompactStatsTable").tablesorter({
+            headers: {
+                2: {sorter: 'separator'},
+                3: {sorter: 'separator'},
+                4: {sorter: 'separator'}
+            }
+        });
+        $("#metaTableCompactStatsTable").tablesorter({
+            headers: {
+                2: {sorter: 'separator'},
+                3: {sorter: 'separator'},
+                4: {sorter: 'separator'}
+            }
+        });
+    }
+);
 </script>
diff --git 
a/hbase-server/src/main/resources/hbase-webapps/regionserver/header.jsp 
b/hbase-server/src/main/resources/hbase-webapps/regionserver/header.jsp
index edbecc424c6..6a616780e3c 100644
--- a/hbase-server/src/main/resources/hbase-webapps/regionserver/header.jsp
+++ b/hbase-server/src/main/resources/hbase-webapps/regionserver/header.jsp
@@ -56,7 +56,15 @@
             <li><a href="/rsOperationDetails.jsp">Operation Details</a></li>
             <li><a href="/logLevel">Log Level</a></li>
             <li><a href="/dump">Debug Dump</a></li>
-            <li><a href="/jmx">Metrics Dump</a></li>
+            <li class="nav-item dropdown">
+              <a class="nav-link dropdown-toggle" href="#" 
id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" 
aria-expanded="false">
+                Metrics <span class="caret"></span>
+              </a>
+              <ul class="dropdown-menu" 
aria-labelledby="navbarDropdownMenuLink">
+                <li><a target="_blank" href="/jmx">JMX</a></li>
+                <li><a target="_blank" href="/jmx?description=true">JMX with 
description</a></li>
+              </ul>
+            </li>
             <li><a href="/prof">Profiler</a></li>
             <% if (HBaseConfiguration.isShowConfInServlet()) { %>
             <li><a href="/conf">HBase Configuration</a></li>
diff --git a/hbase-thrift/src/main/resources/hbase-webapps/thrift/footer.jsp 
b/hbase-thrift/src/main/resources/hbase-webapps/thrift/footer.jsp
new file mode 100644
index 00000000000..53a7d0cdbdb
--- /dev/null
+++ b/hbase-thrift/src/main/resources/hbase-webapps/thrift/footer.jsp
@@ -0,0 +1,30 @@
+<%--
+/**
+* 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.
+*/
+--%>
+    <script src="/static/js/jquery.min.js" type="text/javascript"></script>
+    <script src="/static/js/bootstrap.min.js" type="text/javascript"></script>
+    <script src="/static/js/tab.js" type="text/javascript"></script>
+    <script type="text/javascript">
+      $(document).ready(function() {
+        $('li.active').removeClass('active');
+        $('a[href="' + location.pathname + 
'"]').closest('li').addClass('active');
+      });
+    </script>
+  </body>
+</html>
diff --git a/hbase-thrift/src/main/resources/hbase-webapps/thrift/header.jsp 
b/hbase-thrift/src/main/resources/hbase-webapps/thrift/header.jsp
new file mode 100644
index 00000000000..92566c7e828
--- /dev/null
+++ b/hbase-thrift/src/main/resources/hbase-webapps/thrift/header.jsp
@@ -0,0 +1,72 @@
+<%--
+/**
+* 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.
+*/
+--%>
+<%@ page contentType="text/html;charset=UTF-8"
+         import="org.apache.hadoop.hbase.HBaseConfiguration"%>
+
+<!DOCTYPE html>
+<?xml version="1.0" encoding="UTF-8" ?>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <title><%= request.getParameter("pageTitle")%></title>
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <meta name="description" content="">
+
+  <link href="/static/css/bootstrap.min.css" rel="stylesheet">
+  <link href="/static/css/bootstrap-theme.min.css" rel="stylesheet">
+  <link href="/static/css/hbase.css" rel="stylesheet">
+</head>
+
+<body>
+<div class="navbar  navbar-fixed-top navbar-default">
+  <div class="container-fluid">
+    <div class="navbar-header">
+      <button type="button"
+              class="navbar-toggle" data-toggle="collapse" 
data-target=".navbar-collapse">
+        <span class="icon-bar"></span>
+        <span class="icon-bar"></span>
+        <span class="icon-bar"></span>
+      </button>
+      <a class="navbar-brand" href="/thrift.jsp">
+        <img src="/static/hbase_logo_small.png" alt="HBase Logo"/>
+      </a>
+    </div>
+    <div class="collapse navbar-collapse">
+      <ul class="nav navbar-nav">
+        <li class="active"><a href="/thrift.jsp">Home</a></li>
+        <li><a href="/logs/">Local logs</a></li>
+        <li><a href="/logLevel">Log Level</a></li>
+        <li class="nav-item dropdown">
+          <a class="nav-link dropdown-toggle" href="#" 
id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" 
aria-expanded="false">
+            Metrics <span class="caret"></span>
+          </a>
+          <ul class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
+            <li><a target="_blank" href="/jmx">JMX</a></li>
+            <li><a target="_blank" href="/jmx?description=true">JMX with 
description</a></li>
+          </ul>
+        </li>
+        <li><a href="/prof">Profiler</a></li>
+        <% if (HBaseConfiguration.isShowConfInServlet()) { %>
+        <li><a href="/conf">HBase Configuration</a></li>
+        <% } %>
+      </ul>
+    </div><!--/.nav-collapse -->
+  </div>
+</div>
diff --git a/hbase-thrift/src/main/resources/hbase-webapps/thrift/thrift.jsp 
b/hbase-thrift/src/main/resources/hbase-webapps/thrift/thrift.jsp
index cb22007bea6..d148df1f2e2 100644
--- a/hbase-thrift/src/main/resources/hbase-webapps/thrift/thrift.jsp
+++ b/hbase-thrift/src/main/resources/hbase-webapps/thrift/thrift.jsp
@@ -19,7 +19,6 @@
 --%>
 <%@ page contentType="text/html;charset=UTF-8"
   import="org.apache.hadoop.conf.Configuration"
-  import="org.apache.hadoop.hbase.HBaseConfiguration"
   import="org.apache.hadoop.hbase.util.VersionInfo"
   import="java.util.Date"
 %>
@@ -27,54 +26,25 @@
 <%@ page import="org.apache.hadoop.hbase.util.JvmVersion" %>
 
 <%
-Configuration conf = 
(Configuration)getServletContext().getAttribute("hbase.conf");
-String serverType = 
(String)getServletContext().getAttribute("hbase.thrift.server.type");
-long startcode = conf.getLong("startcode", System.currentTimeMillis());
-String listenPort = conf.get("hbase.regionserver.thrift.port", "9090");
-ImplType implType = ImplType.getServerImpl(conf);
-String framed = implType.isAlwaysFramed()
-    ? "true" : conf.get("hbase.regionserver.thrift.framed", "false");
-String compact = conf.get("hbase.regionserver.thrift.compact", "false");
-%>
-<!DOCTYPE html>
-<?xml version="1.0" encoding="UTF-8" ?>
-<html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <title>HBase Thrift Server: <%= listenPort %></title>
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <meta name="description" content="">
+  Configuration conf = 
(Configuration)getServletContext().getAttribute("hbase.conf");
+  String serverType = 
(String)getServletContext().getAttribute("hbase.thrift.server.type");
+  long startcode = conf.getLong("startcode", System.currentTimeMillis());
+  String listenPort = conf.get("hbase.regionserver.thrift.port", "9090");
+  ImplType implType = ImplType.getServerImpl(conf);
+
+  String transport =
+    (implType.isAlwaysFramed() ||
+      conf.getBoolean("hbase.regionserver.thrift.framed", false)) ? "Framed" : 
"Standard";
+  String protocol =
+    conf.getBoolean("hbase.regionserver.thrift.compact", false) ? "Compact" : 
"Binary";
+  String qop = conf.get("hbase.thrift.security.qop", "None");
 
-    <link href="/static/css/bootstrap.min.css" rel="stylesheet">
-    <link href="/static/css/bootstrap-theme.min.css" rel="stylesheet">
-    <link href="/static/css/hbase.css" rel="stylesheet">
-  </head>
+  pageContext.setAttribute("pageTitle", "HBase Thrift Server: " + listenPort);
+%>
 
-  <body>
-  <div class="navbar  navbar-fixed-top navbar-default">
-      <div class="container-fluid">
-          <div class="navbar-header">
-              <button type="button" class="navbar-toggle" 
data-toggle="collapse" data-target=".navbar-collapse">
-                  <span class="icon-bar"></span>
-                  <span class="icon-bar"></span>
-                  <span class="icon-bar"></span>
-              </button>
-              <a class="navbar-brand" href="/thrift.jsp"><img 
src="/static/hbase_logo_small.png" alt="HBase Logo"/></a>
-          </div>
-          <div class="collapse navbar-collapse">
-              <ul class="nav navbar-nav">
-                <li class="active"><a href="/">Home</a></li>
-                <li><a href="/logs/">Local logs</a></li>
-                <li><a href="/logLevel">Log Level</a></li>
-                <li><a href="/jmx">Metrics Dump</a></li>
-                <li><a href="/prof">Profiler</a></li>
-                <% if (HBaseConfiguration.isShowConfInServlet()) { %>
-                <li><a href="/conf">HBase Configuration</a></li>
-                <% } %>
-            </ul>
-          </div><!--/.nav-collapse -->
-      </div>
-  </div>
+<jsp:include page="header.jsp">
+  <jsp:param name="pageTitle" value="${pageTitle}"/>
+</jsp:include>
 
 <div class="container-fluid content">
     <div class="row inner_header">
@@ -118,31 +88,34 @@ String compact = 
conf.get("hbase.regionserver.thrift.compact", "false");
             <td>Thrift RPC engine implementation type chosen by this Thrift 
server</td>
         </tr>
         <tr>
-            <td>Compact Protocol</td>
-            <td><%= compact %></td>
-            <td>Thrift RPC engine uses compact protocol</td>
+            <td>Protocol</td>
+            <td><%= protocol %></td>
+            <td>Thrift RPC engine protocol type</td>
         </tr>
         <tr>
-            <td>Framed Transport</td>
-            <td><%= framed %></td>
-            <td>Thrift RPC engine uses framed transport</td>
+            <td>Transport</td>
+            <td><%= transport %></td>
+            <td>Thrift RPC engine transport type</td>
         </tr>
         <tr>
             <td>Thrift Server Type</td>
             <td><%= serverType %></td>
             <td>The type of this Thrift server</td>
         </tr>
+      <tr>
+        <td>Quality of Protection</td>
+        <td><%= qop %></td>
+        <td>QOP Settings for SASL</td>
+      </tr>
     </table>
     </section>
     </div>
     <div class="row">
         <section>
-            <a href="http://hbase.apache.org/book.html#_thrift";>Apache HBase 
Reference Guide chapter on Thrift</a>
+            <a href="http://hbase.apache.org/book.html#_thrift";>
+              Apache HBase Reference Guide chapter on Thrift</a>
         </section>
     </div>
 </div>
-<script src="/static/js/jquery.min.js" type="text/javascript"></script>
-<script src="/static/js/bootstrap.min.js" type="text/javascript"></script>
-<script src="/static/js/tab.js" type="text/javascript"></script>
-</body>
-</html>
+
+<jsp:include page="footer.jsp" />

Reply via email to