You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hive.apache.org by jd...@apache.org on 2016/04/26 19:53:26 UTC

[03/50] [abbrv] hive git commit: HIVE-13467: Show llap info on hs2 ui when available (Gunther Hagleitner, reviewed by Vikram Dixit K)

HIVE-13467: Show llap info on hs2 ui when available (Gunther Hagleitner, reviewed by Vikram Dixit K)


Project: http://git-wip-us.apache.org/repos/asf/hive/repo
Commit: http://git-wip-us.apache.org/repos/asf/hive/commit/749e27a9
Tree: http://git-wip-us.apache.org/repos/asf/hive/tree/749e27a9
Diff: http://git-wip-us.apache.org/repos/asf/hive/diff/749e27a9

Branch: refs/heads/llap
Commit: 749e27a92a74e36152c21981a1cf7adcb92f3e4a
Parents: a2b7b4d
Author: Gunther Hagleitner <gu...@apache.org>
Authored: Fri Apr 22 19:14:50 2016 -0700
Committer: Gunther Hagleitner <gu...@apache.org>
Committed: Fri Apr 22 19:14:50 2016 -0700

----------------------------------------------------------------------
 LICENSE                                         |  23 +
 .../java/org/apache/hive/http/HttpServer.java   |  16 +-
 .../llap/registry/impl/LlapRegistryService.java |   6 +-
 .../hive/llap/cli/LlapStatusServiceDriver.java  |  14 +-
 .../hive/ql/optimizer/physical/LlapDecider.java |  18 +-
 service/pom.xml                                 |   6 +
 .../java/org/apache/hive/http/LlapServlet.java  | 115 +++++
 .../apache/hive/service/server/HiveServer2.java |   2 +
 .../hive-webapps/hiveserver2/hiveserver2.jsp    |   1 +
 .../hive-webapps/hiveserver2/llap.html          |  47 ++
 .../hive-webapps/static/css/json.human.css      | 110 +++++
 .../hive-webapps/static/js/json.human.js        | 452 +++++++++++++++++++
 .../resources/hive-webapps/static/js/llap.js    |  37 ++
 13 files changed, 827 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hive/blob/749e27a9/LICENSE
----------------------------------------------------------------------
diff --git a/LICENSE b/LICENSE
index f26f4e1..96ea54b 100644
--- a/LICENSE
+++ b/LICENSE
@@ -558,3 +558,26 @@ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABI
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+For json.human.js/json.human.css:
+
+Copyright (c) 2016 Mariano Guerra
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+

http://git-wip-us.apache.org/repos/asf/hive/blob/749e27a9/common/src/java/org/apache/hive/http/HttpServer.java
----------------------------------------------------------------------
diff --git a/common/src/java/org/apache/hive/http/HttpServer.java b/common/src/java/org/apache/hive/http/HttpServer.java
index 32956b1..9ceaf3f 100644
--- a/common/src/java/org/apache/hive/http/HttpServer.java
+++ b/common/src/java/org/apache/hive/http/HttpServer.java
@@ -22,6 +22,8 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.net.URL;
 import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -31,6 +33,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import com.google.common.base.Preconditions;
+import org.apache.commons.math3.util.Pair;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.CommonConfigurationKeys;
 import org.apache.hadoop.hive.conf.HiveConf;
@@ -108,13 +111,15 @@ public class HttpServer {
     private int port;
     private int maxThreads;
     private HiveConf conf;
-    private Map<String, Object> contextAttrs = new HashMap<String, Object>();
+    private final Map<String, Object> contextAttrs = new HashMap<String, Object>();
     private String keyStorePassword;
     private String keyStorePath;
     private String spnegoPrincipal;
     private String spnegoKeytab;
     private boolean useSPNEGO;
     private boolean useSSL;
+    private final List<Pair<String, Class<? extends HttpServlet>>> servlets =
+        new LinkedList<Pair<String, Class<? extends HttpServlet>>>();
 
     public Builder(String name) {
       Preconditions.checkArgument(name != null && !name.isEmpty(), "Name must be specified");
@@ -188,6 +193,11 @@ public class HttpServer {
       contextAttrs.put(name, value);
       return this;
     }
+
+    public Builder addServlet(String endpoint, Class<? extends HttpServlet> servlet) {
+      servlets.add(new Pair<String, Class<? extends HttpServlet>>(endpoint, servlet));
+      return this;
+    }
   }
 
   public void start() throws Exception {
@@ -383,6 +393,10 @@ public class HttpServer {
     addServlet("conf", "/conf", ConfServlet.class);
     addServlet("stacks", "/stacks", StackServlet.class);
 
+    for (Pair<String, Class<? extends HttpServlet>> p : b.servlets) {
+      addServlet(p.getFirst(), "/" + p.getFirst(), p.getSecond());
+    }
+
     ServletContextHandler staticCtx =
       new ServletContextHandler(contexts, "/static");
     staticCtx.setResourceBase(appDir + "/static");

http://git-wip-us.apache.org/repos/asf/hive/blob/749e27a9/llap-client/src/java/org/apache/hadoop/hive/llap/registry/impl/LlapRegistryService.java
----------------------------------------------------------------------
diff --git a/llap-client/src/java/org/apache/hadoop/hive/llap/registry/impl/LlapRegistryService.java b/llap-client/src/java/org/apache/hadoop/hive/llap/registry/impl/LlapRegistryService.java
index 2b4516b..badc182 100644
--- a/llap-client/src/java/org/apache/hadoop/hive/llap/registry/impl/LlapRegistryService.java
+++ b/llap-client/src/java/org/apache/hadoop/hive/llap/registry/impl/LlapRegistryService.java
@@ -17,8 +17,6 @@ import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
-import com.google.common.base.Preconditions;
-
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hive.conf.HiveConf;
 import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
@@ -29,6 +27,8 @@ import org.apache.hadoop.service.AbstractService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Preconditions;
+
 public class LlapRegistryService extends AbstractService {
 
   private static final Logger LOG = LoggerFactory.getLogger(LlapRegistryService.class);
@@ -59,7 +59,7 @@ public class LlapRegistryService extends AbstractService {
     if (hosts.startsWith("@")) {
       // Caching instances only in case of the YARN registry. Each host based list will get it's own copy.
       String name = hosts.substring(1);
-      if (yarnRegistries.containsKey(name)) {
+      if (yarnRegistries.containsKey(name) && yarnRegistries.get(name).isInState(STATE.STARTED)) {
         registry = yarnRegistries.get(name);
       } else {
         registry = new LlapRegistryService(false);

http://git-wip-us.apache.org/repos/asf/hive/blob/749e27a9/llap-server/src/java/org/apache/hadoop/hive/llap/cli/LlapStatusServiceDriver.java
----------------------------------------------------------------------
diff --git a/llap-server/src/java/org/apache/hadoop/hive/llap/cli/LlapStatusServiceDriver.java b/llap-server/src/java/org/apache/hadoop/hive/llap/cli/LlapStatusServiceDriver.java
index d1193ad..45ba5d0 100644
--- a/llap-server/src/java/org/apache/hadoop/hive/llap/cli/LlapStatusServiceDriver.java
+++ b/llap-server/src/java/org/apache/hadoop/hive/llap/cli/LlapStatusServiceDriver.java
@@ -20,6 +20,7 @@ package org.apache.hadoop.hive.llap.cli;
 
 
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -67,7 +68,7 @@ public class LlapStatusServiceDriver {
   }
 
 
-  private int run(String[] args) {
+  public int run(String[] args) {
 
     SliderClient sliderClient = null;
     try {
@@ -148,14 +149,13 @@ public class LlapStatusServiceDriver {
     }
   }
 
-  private void outputJson() throws LlapStatusCliException {
+  public void outputJson(PrintWriter writer) throws LlapStatusCliException {
     ObjectMapper mapper = new ObjectMapper();
     mapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
     mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
     mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_EMPTY);
     try {
-      System.out
-          .println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(appStatusBuilder));
+      writer.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(appStatusBuilder));
     } catch (IOException e) {
       throw new LlapStatusCliException(ExitCode.LLAP_JSON_GENERATION_ERROR, "Failed to create JSON",
           e);
@@ -455,7 +455,7 @@ public class LlapStatusServiceDriver {
     private Long appStartTime;
     private Long appFinishTime;
 
-    private List<LlapInstance> llapInstances = new LinkedList<>();
+    private final List<LlapInstance> llapInstances = new LinkedList<>();
 
     private transient Map<String, LlapInstance> containerToInstanceMap = new HashMap<>();
 
@@ -771,7 +771,7 @@ public class LlapStatusServiceDriver {
     RUNNING_ALL, COMPLETE, UNKNOWN
   }
 
-  enum ExitCode {
+  public enum ExitCode {
     SUCCESS(0),
     INCORRECT_USAGE(10),
     YARN_ERROR(20),
@@ -806,7 +806,7 @@ public class LlapStatusServiceDriver {
       LlapStatusServiceDriver statusServiceDriver = new LlapStatusServiceDriver();
       ret = statusServiceDriver.run(args);
       if (ret == ExitCode.SUCCESS.getInt()) {
-          statusServiceDriver.outputJson();
+        statusServiceDriver.outputJson(new PrintWriter(System.out));
       }
 
     } catch (Throwable t) {

http://git-wip-us.apache.org/repos/asf/hive/blob/749e27a9/ql/src/java/org/apache/hadoop/hive/ql/optimizer/physical/LlapDecider.java
----------------------------------------------------------------------
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/physical/LlapDecider.java b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/physical/LlapDecider.java
index d3ec7ff..c0b7a32 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/physical/LlapDecider.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/physical/LlapDecider.java
@@ -73,17 +73,17 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * LlapDecider takes care of tagging certain vertices in the execution
- * graph as "llap", which in turn causes them to be submitted to an
- * llap daemon instead of a regular yarn container.
+ * LlapDecider takes care of tagging certain vertices in the execution graph as
+ * "llap", which in turn causes them to be submitted to an llap daemon instead
+ * of a regular yarn container.
  *
- * The actual algoritm used is driven by LLAP_EXECUTION_MODE. "all",
- * "none" and "map" mechanically tag those elements. "auto" tries to
- * be smarter by looking for suitable vertices.
+ * The actual algorithm used is driven by LLAP_EXECUTION_MODE. "all", "none" and
+ * "map" mechanically tag those elements. "auto" tries to be smarter by looking
+ * for suitable vertices.
  *
- * Regardless of the algorithm used, it's always ensured that there's
- * not user code that will be sent to the daemon (ie.: script
- * operators, temporary functions, etc)
+ * Regardless of the algorithm used, it's always ensured that there's not user
+ * code that will be sent to the daemon (ie.: script operators, temporary
+ * functions, etc)
  */
 public class LlapDecider implements PhysicalPlanResolver {
 

http://git-wip-us.apache.org/repos/asf/hive/blob/749e27a9/service/pom.xml
----------------------------------------------------------------------
diff --git a/service/pom.xml b/service/pom.xml
index 41a4ef1..ae82cd3 100644
--- a/service/pom.xml
+++ b/service/pom.xml
@@ -49,6 +49,12 @@
       <artifactId>hive-service-rpc</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.hive</groupId>
+      <artifactId>hive-llap-server</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
     <!-- inter-project -->
     <dependency>
       <groupId>commons-codec</groupId>

http://git-wip-us.apache.org/repos/asf/hive/blob/749e27a9/service/src/java/org/apache/hive/http/LlapServlet.java
----------------------------------------------------------------------
diff --git a/service/src/java/org/apache/hive/http/LlapServlet.java b/service/src/java/org/apache/hive/http/LlapServlet.java
new file mode 100644
index 0000000..65e23fc
--- /dev/null
+++ b/service/src/java/org/apache/hive/http/LlapServlet.java
@@ -0,0 +1,115 @@
+/**
+ * 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.hive.http;
+
+import java.io.PrintWriter;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hive.conf.HiveConf;
+import org.apache.hadoop.hive.llap.cli.LlapStatusServiceDriver;
+
+@SuppressWarnings("serial")
+public class LlapServlet extends HttpServlet {
+
+  private static final Log LOG = LogFactory.getLog(JMXJsonServlet.class);
+  static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
+  static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
+
+  /**
+   * Initialize this servlet.
+   */
+  @Override
+  public void init() throws ServletException {
+  }
+
+  /**
+   * Return the Configuration of the daemon hosting this servlet.
+   * This is populated when the HttpServer starts.
+   */
+  private Configuration getConfFromContext() {
+    Configuration conf = (Configuration)getServletContext().getAttribute(
+        HttpServer.CONF_CONTEXT_ATTRIBUTE);
+    assert conf != null;
+    return conf;
+  }
+
+  /**
+   * Process a GET request for the specified resource.
+   *
+   * @param request
+   *          The servlet request we are processing
+   * @param response
+   *          The servlet response we are creating
+   */
+  @Override
+  public void doGet(HttpServletRequest request, HttpServletResponse response) {
+    try {
+      if (!HttpServer.isInstrumentationAccessAllowed(getServletContext(), request, response)) {
+        return;
+      }
+      PrintWriter writer = null;
+      String clusterName =
+          HiveConf.getVar(getConfFromContext(), HiveConf.ConfVars.LLAP_DAEMON_SERVICE_HOSTS);
+
+      try {
+        response.setContentType("application/json; charset=utf8");
+        response.setHeader(ACCESS_CONTROL_ALLOW_METHODS, "GET");
+        response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
+        response.setHeader("Cache-Control", "no-transform,public,max-age=60,s-maxage=60");
+
+        writer = response.getWriter();
+
+        if (clusterName != null) {
+          clusterName = clusterName.trim();
+        }
+
+        if (clusterName == null || clusterName.isEmpty()) {
+          writer.print("{\"LLAP\": \"No llap daemons configured. ");
+          writer.print("Check hive.llap.daemon.service.hosts.\"}");
+          return;
+        }
+
+        if (clusterName.startsWith("@")) {
+          clusterName = clusterName.substring(1);
+        }
+
+        LOG.info("Retrieving info for cluster: " + clusterName);
+        LlapStatusServiceDriver driver = new LlapStatusServiceDriver();
+        int ret = driver.run(new String[] { "-n", clusterName });
+        if (ret == LlapStatusServiceDriver.ExitCode.SUCCESS.getInt()) {
+          driver.outputJson(writer);
+        }
+
+      } finally {
+        if (writer != null) {
+          writer.close();
+        }
+      }
+    } catch (Exception e) {
+      LOG.error("Caught exception while processing llap status request", e);
+      response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/hive/blob/749e27a9/service/src/java/org/apache/hive/service/server/HiveServer2.java
----------------------------------------------------------------------
diff --git a/service/src/java/org/apache/hive/service/server/HiveServer2.java b/service/src/java/org/apache/hive/service/server/HiveServer2.java
index 882f4ae..d61edf5 100644
--- a/service/src/java/org/apache/hive/service/server/HiveServer2.java
+++ b/service/src/java/org/apache/hive/service/server/HiveServer2.java
@@ -59,6 +59,7 @@ import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hive.common.util.HiveStringUtils;
 import org.apache.hive.common.util.HiveVersionInfo;
 import org.apache.hive.http.HttpServer;
+import org.apache.hive.http.LlapServlet;
 import org.apache.hive.service.CompositeService;
 import org.apache.hive.service.ServiceException;
 import org.apache.hive.service.cli.CLIService;
@@ -181,6 +182,7 @@ public class HiveServer2 extends CompositeService {
             builder.setSPNEGOKeytab(spnegoKeytab);
             builder.setUseSPNEGO(true);
           }
+          builder.addServlet("llap", LlapServlet.class);
           webServer = builder.build();
           webServer.addServlet("query_page", "/query_page", QueryProfileServlet.class);
         }

http://git-wip-us.apache.org/repos/asf/hive/blob/749e27a9/service/src/resources/hive-webapps/hiveserver2/hiveserver2.jsp
----------------------------------------------------------------------
diff --git a/service/src/resources/hive-webapps/hiveserver2/hiveserver2.jsp b/service/src/resources/hive-webapps/hiveserver2/hiveserver2.jsp
index 293a8ef..3c187b6 100644
--- a/service/src/resources/hive-webapps/hiveserver2/hiveserver2.jsp
+++ b/service/src/resources/hive-webapps/hiveserver2/hiveserver2.jsp
@@ -75,6 +75,7 @@ SessionManager sessionManager =
                 <li><a href="/jmx">Metrics Dump</a></li>
                 <li><a href="/conf">Hive Configuration</a></li>
                 <li><a href="/stacks">Stack Trace</a></li>
+                <li><a href="/llap.html">Llap Daemons</a></li>
             </ul>
           </div><!--/.nav-collapse -->
         </div>

http://git-wip-us.apache.org/repos/asf/hive/blob/749e27a9/service/src/resources/hive-webapps/hiveserver2/llap.html
----------------------------------------------------------------------
diff --git a/service/src/resources/hive-webapps/hiveserver2/llap.html b/service/src/resources/hive-webapps/hiveserver2/llap.html
new file mode 100644
index 0000000..e1424b8
--- /dev/null
+++ b/service/src/resources/hive-webapps/hiveserver2/llap.html
@@ -0,0 +1,47 @@
+<!--[if IE]>
+<!DOCTYPE html>
+<![endif]-->
+<?xml version="1.0" encoding="UTF-8" ?>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>HiveServer2</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/hive.css" rel="stylesheet">
+
+    <link rel="stylesheet" type="text/css" href="/static/css/json.human.css">
+    <script src="/static/js/jquery.min.js"></script>
+    <script src="/static/js/json.human.js"></script>
+    <script src="/static/js/llap.js"></script>
+  </head>
+  <body>
+      <div class="navbar  navbar-fixed-top navbar-default">
+      <div class="container">
+          <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="/hiveserver2.jsp"><img src="/static/hive_logo.jpeg" alt="Hive 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="/jmx">Metrics Dump</a></li>
+                <li><a href="/conf">Hive Configuration</a></li>
+                <li><a href="/stacks">Stack Trace</a></li>
+                <li><a href="/llap.html">Llap Daemons</a></li>
+            </ul>
+          </div><!--/.nav-collapse -->
+        </div>
+      </div>
+    </div>
+    <div id="show-data"></div>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/hive/blob/749e27a9/service/src/resources/hive-webapps/static/css/json.human.css
----------------------------------------------------------------------
diff --git a/service/src/resources/hive-webapps/static/css/json.human.css b/service/src/resources/hive-webapps/static/css/json.human.css
new file mode 100644
index 0000000..2ee552f
--- /dev/null
+++ b/service/src/resources/hive-webapps/static/css/json.human.css
@@ -0,0 +1,110 @@
+.jh-root, .jh-type-object, .jh-type-array, .jh-key, .jh-value, .jh-root tr{
+ -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+ -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+ box-sizing: border-box;         /* Opera/IE 8+ */
+ font-weight: bold;
+}
+
+.jh-key, .jh-value{
+ margin: 0;
+ padding: 0.2em;
+ font-weight: bold;
+}
+
+.jh-value{
+    border-left: 1px solid #ddd;
+}
+
+.jh-type-number{
+    text-align: center;
+    color: #5286BC;
+}
+
+.jh-type-bool-true{
+    text-align: center;
+    color: #5A811C;
+}
+
+.jh-type-bool-false{
+    text-align: center;
+    color: #D45317;
+}
+
+.jh-type-bool-image {
+    width: 20px;
+    height: 20px;
+    margin-right: 5px;
+    vertical-align: bottom;
+}
+
+.jh-type-string{
+    font-style: italic;
+    color: #6E6E6E;
+}
+
+.jh-array-key{
+    font-style: italic;
+    font-size: small;
+    text-align: center;
+}
+
+.jh-object-key, .jh-array-key{
+    color: #444;
+    vertical-align: top;
+}
+
+.jh-type-object > tbody > tr:nth-child(odd), .jh-type-array > tbody > tr:nth-child(odd){
+    background-color: #f5f5f5;
+}
+
+.jh-type-object > tbody > tr:nth-child(even), .jh-type-array > tbody > tr:nth-child(even){
+    background-color: #fff;
+}
+
+.jh-type-object, .jh-type-array{
+    width: 100%;
+    border-collapse: collapse;
+}
+
+.jh-root{
+ border: 1px solid #ccc;
+ margin: 0.2em;
+}
+
+th.jh-key{
+ text-align: left;
+}
+
+.jh-type-object > tbody > tr, .jh-type-array > tbody > tr{
+ border: 1px solid #ddd;
+ border-bottom: none;
+}
+
+.jh-type-object > tbody > tr:last-child, .jh-type-array > tbody > tr:last-child{
+ border-bottom: 1px solid #ddd;
+}
+
+.jh-type-object > tbody > tr:hover, .jh-type-array > tbody > tr:hover{
+ border: 1px solid #F99927;
+}
+
+.jh-empty{
+ font-style: italic;
+ color: #999;
+ font-size: small;
+}
+
+.jh-a {
+    text-decoration: none;
+}
+
+.jh-a:hover{
+    text-decoration: underline;
+}
+
+.jh-a span.jh-type-string {
+    text-decoration: none;
+    color : #268ddd;
+    font-style: normal;
+}
+

http://git-wip-us.apache.org/repos/asf/hive/blob/749e27a9/service/src/resources/hive-webapps/static/js/json.human.js
----------------------------------------------------------------------
diff --git a/service/src/resources/hive-webapps/static/js/json.human.js b/service/src/resources/hive-webapps/static/js/json.human.js
new file mode 100644
index 0000000..2241623
--- /dev/null
+++ b/service/src/resources/hive-webapps/static/js/json.human.js
@@ -0,0 +1,452 @@
+/* json.human.js -  http://marianoguerra.github.io/json.human.js */
+/* Licensed under MIT - see above site for details */
+
+/*globals define, module, require, document*/
+(function (root, factory) {
+    "use strict";
+    if (typeof define === 'function' && define.amd) {
+        define([], factory);
+    } else if (typeof module !== 'undefined' && module.exports) {
+        module.exports = factory();
+    } else {
+        root.JsonHuman = factory();
+    }
+}(this, function () {
+    "use strict";
+
+    var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+    function makePrefixer(prefix) {
+        return function (name) {
+            return prefix + "-" + name;
+        };
+    }
+
+    function isArray(obj) {
+        return toString.call(obj) === '[object Array]';
+    }
+
+    function sn(tagName, className, data) {
+        var result = document.createElement(tagName);
+
+        result.className = className;
+        result.appendChild(document.createTextNode("" + data));
+
+        return result;
+    }
+
+    function scn(tagName, className, child) {
+        var result = document.createElement(tagName),
+            i, len;
+
+        result.className = className;
+
+        if (isArray(child)) {
+            for (i = 0, len = child.length; i < len; i += 1) {
+                result.appendChild(child[i]);
+            }
+        } else {
+            result.appendChild(child);
+        }
+
+        return result;
+    }
+
+    function linkNode(child, href, target){
+        var a = scn("a", HYPERLINK_CLASS_NAME, child);
+        a.setAttribute('href', href);
+        a.setAttribute('target', target);
+        return a;
+    }
+
+    var toString = Object.prototype.toString,
+        prefixer = makePrefixer("jh"),
+        p = prefixer,
+        ARRAY = 2,
+        BOOL = 4,
+        INT = 8,
+        FLOAT = 16,
+        STRING = 32,
+        OBJECT = 64,
+        SPECIAL_OBJECT = 128,
+        FUNCTION = 256,
+        UNK = 1,
+
+        STRING_CLASS_NAME = p("type-string"),
+        STRING_EMPTY_CLASS_NAME = p("type-string") + " " + p("empty"),
+
+        BOOL_TRUE_CLASS_NAME = p("type-bool-true"),
+        BOOL_FALSE_CLASS_NAME = p("type-bool-false"),
+        BOOL_IMAGE = p("type-bool-image"),
+        INT_CLASS_NAME = p("type-int") + " " + p("type-number"),
+        FLOAT_CLASS_NAME = p("type-float") + " " + p("type-number"),
+
+        OBJECT_CLASS_NAME = p("type-object"),
+        OBJ_KEY_CLASS_NAME = p("key") + " " + p("object-key"),
+        OBJ_VAL_CLASS_NAME = p("value") + " " + p("object-value"),
+        OBJ_EMPTY_CLASS_NAME = p("type-object") + " " + p("empty"),
+
+        FUNCTION_CLASS_NAME = p("type-function"),
+
+        ARRAY_KEY_CLASS_NAME = p("key") + " " + p("array-key"),
+        ARRAY_VAL_CLASS_NAME = p("value") + " " + p("array-value"),
+        ARRAY_CLASS_NAME = p("type-array"),
+        ARRAY_EMPTY_CLASS_NAME = p("type-array") + " " + p("empty"),
+
+        HYPERLINK_CLASS_NAME = p('a'),
+
+        UNKNOWN_CLASS_NAME = p("type-unk");
+
+    function getType(obj) {
+        var type = typeof obj;
+
+        switch (type) {
+        case "boolean":
+            return BOOL;
+        case "string":
+            return STRING;
+        case "number":
+            return (obj % 1 === 0) ? INT : FLOAT;
+        case "function":
+            return FUNCTION;
+        default:
+            if (isArray(obj)) {
+                return ARRAY;
+            } else if (obj === Object(obj)) {
+                if (obj.constructor === Object) {
+                    return OBJECT;
+                }
+                return OBJECT | SPECIAL_OBJECT
+            } else {
+                return UNK;
+            }
+        }
+    }
+
+    function _format(data, options, parentKey) {
+
+        var result, container, key, keyNode, valNode, len, children, tr, value,
+            isEmpty = true,
+            isSpecial = false,
+            accum = [],
+            type = getType(data);
+
+        // Initialized & used only in case of objects & arrays
+        var hyperlinksEnabled, aTarget, hyperlinkKeys ;
+
+        if (type === BOOL) {
+            var boolOpt = options.bool;
+            container = document.createElement('div');
+
+            if (boolOpt.showImage) {
+                var img = document.createElement('img');
+                img.setAttribute('class', BOOL_IMAGE);
+
+                img.setAttribute('src',
+                                 '' + (data ? boolOpt.img.true : boolOpt.img.false));
+
+                container.appendChild(img);
+            }
+
+            if (boolOpt.showText) {
+                container.appendChild(data ?
+                                      sn("span", BOOL_TRUE_CLASS_NAME, boolOpt.text.true) :
+                                      sn("span", BOOL_FALSE_CLASS_NAME, boolOpt.text.false));
+            }
+
+            result = container;
+
+        } else if (type === STRING) {
+            if (data === "") {
+                result = sn("span", STRING_EMPTY_CLASS_NAME, "(Empty Text)");
+            } else {
+                aTarget = options.hyperlinks.target;
+                hyperlinkKeys = options.hyperlinks.keys;
+                hyperlinksEnabled =
+                    options.hyperlinks.enable &&
+                    hyperlinkKeys &&
+                    hyperlinkKeys.length > 0;
+                if (hyperlinksEnabled && indexOf.call(hyperlinkKeys, parentKey) >= 0) {
+                    result = scn("span", STRING_CLASS_NAME, linkNode(sn("span",STRING_CLASS_NAME, data), data, aTarget));
+                } else {
+                    result = sn("span", STRING_CLASS_NAME, data);
+                }
+            }
+        } else if (type === INT) {
+            result = sn("span", INT_CLASS_NAME, data);
+        } else if (type === FLOAT) {
+            result = sn("span", FLOAT_CLASS_NAME, data);
+        } else if (type & OBJECT) {
+            if (type & SPECIAL_OBJECT) {
+                isSpecial = true;
+            }
+            children = [];
+
+            aTarget =  options.hyperlinks.target;
+            hyperlinkKeys = options.hyperlinks.keys;
+
+            // Is Hyperlink Key
+            hyperlinksEnabled =
+                options.hyperlinks.enable &&
+                hyperlinkKeys &&
+                hyperlinkKeys.length > 0;
+
+            for (key in data) {
+                isEmpty = false;
+
+                value = data[key];
+
+                valNode = _format(value, options, key);
+
+                var converted = key.replace( /([A-Z])/g, " $1" );
+                converted = converted.charAt(0).toUpperCase() + converted.slice(1);
+
+                keyNode = sn("th", OBJ_KEY_CLASS_NAME, converted);
+
+                if( hyperlinksEnabled &&
+                    typeof(value) === 'string' &&
+                    indexOf.call(hyperlinkKeys, key) >= 0){
+
+                    valNode = scn("td", OBJ_VAL_CLASS_NAME, linkNode(valNode, value, aTarget));
+                } else {
+                    valNode = scn("td", OBJ_VAL_CLASS_NAME, valNode);
+                }
+
+                tr = document.createElement("tr");
+                tr.appendChild(keyNode);
+                tr.appendChild(valNode);
+
+                children.push(tr);
+            }
+
+            if (isSpecial) {
+                result = sn('span', STRING_CLASS_NAME, data.toString())
+            } else if (isEmpty) {
+                result = sn("span", OBJ_EMPTY_CLASS_NAME, "(Empty Object)");
+            } else {
+                result = scn("table", OBJECT_CLASS_NAME, scn("tbody", '', children));
+            }
+        } else if (type === FUNCTION) {
+            result = sn("span", FUNCTION_CLASS_NAME, data);
+        } else if (type === ARRAY) {
+            if (data.length > 0) {
+                children = [];
+                var showArrayIndices = options.showArrayIndex;
+                var showArrayAsFlatTable = options.showArrayAsFlatTable;
+                var flatTableKeys = options.flatTableKeys = options.flatTableKeys;
+
+                aTarget = options.hyperlinks.target;
+                hyperlinkKeys = options.hyperlinks.keys;
+
+                // Hyperlink of arrays?
+                hyperlinksEnabled = parentKey && options.hyperlinks.enable &&
+                    hyperlinkKeys &&
+                    hyperlinkKeys.length > 0 &&
+                    indexOf.call(hyperlinkKeys, parentKey) >= 0;
+
+                if (showArrayAsFlatTable && data.length > 0) {
+                    var first = data[0];
+                    var objKeys = Object.keys(first);
+                    var headerNodes = [];
+
+                    if (flatTableKeys.length == 0) {
+                        for (var k in objKeys) {
+                            flatTableKeys.push(objKeys[k]);
+                        }
+                    }
+
+                    var objKeyMod = [];
+                    for (var k in objKeys) {
+                        if ($.inArray(objKeys[k], flatTableKeys) >= 0) {
+                            objKeyMod.push(objKeys[k]);
+                            var converted = objKeys[k].replace(/([A-Z])/g, " $1" );
+                            converted = converted.charAt(0).toUpperCase() + converted.slice(1);
+                            headerNodes.push(sn("th", ARRAY_KEY_CLASS_NAME, converted));
+                        }
+                    }
+                    objKeys = objKeyMod;
+
+                    var headerRow = scn("tr", UNKNOWN_CLASS_NAME, headerNodes);
+                    children.push(headerRow);
+                    for (key = 0, len = data.length; key < len; key += 1) {
+                        value = data[key];
+                        var row = document.createElement("tr");
+                        for (var k in objKeys) {
+                            var v = value[objKeys[k]];
+                            if (hyperlinksEnabled && typeof(v) === "string") {
+                                valNode = _format(v, options, objKeys[k]);
+                                valNode = scn("td", ARRAY_VAL_CLASS_NAME,
+                                              linkNode(valNode, v, aTarget));
+                            } else {
+                                valNode = scn("td", ARRAY_VAL_CLASS_NAME,
+                                              _format(v, options, objKeys[k]));
+                            }
+                            row.appendChild(valNode);
+                        }
+                        children.push(row);
+                    }
+                }
+                else {
+                    for (key = 0, len = data.length; key < len; key += 1) {
+
+                        keyNode = sn("th", ARRAY_KEY_CLASS_NAME, key);
+                        value = data[key];
+
+                        if (hyperlinksEnabled && typeof(value) === "string") {
+                            valNode = _format(value, options, key);
+                            valNode = scn("td", ARRAY_VAL_CLASS_NAME,
+                                          linkNode(valNode, value, aTarget));
+                        } else {
+                            valNode = scn("td", ARRAY_VAL_CLASS_NAME,
+                                          _format(value, options, key));
+                        }
+
+                        tr = document.createElement("tr");
+
+                        if (showArrayIndices) {
+                            tr.appendChild(keyNode);
+                        }
+                        tr.appendChild(valNode);
+
+                        children.push(tr);
+                    }
+                }
+                result = scn("table", ARRAY_CLASS_NAME, scn("tbody", '', children));
+            } else {
+                result = sn("span", ARRAY_EMPTY_CLASS_NAME, "(Empty List)");
+            }
+        } else {
+            result = sn("span", UNKNOWN_CLASS_NAME, data);
+        }
+
+        return result;
+    }
+
+    function format(data, options) {
+        options = validateOptions(options || {});
+
+        var result;
+
+        result = _format(data, options);
+        result.className = result.className + " " + prefixer("root");
+
+        return result;
+    }
+
+    function validateOptions(options){
+        options = validateArrayIndexOption(options);
+        options = validateArrayAsFlatTableOption(options);
+        options = validateHyperlinkOptions(options);
+        options = validateBoolOptions(options);
+
+        // Add any more option validators here
+
+        return options;
+    }
+
+    function validateArrayIndexOption(options) {
+        if(options.showArrayIndex === undefined){
+            options.showArrayIndex = true;
+        } else {
+            // Force to boolean just in case
+            options.showArrayIndex = options.showArrayIndex ? true: false;
+        }
+
+        return options;
+    }
+
+    function validateArrayAsFlatTableOption(options) {
+        if(options.showArrayAsFlatTable === undefined){
+            options.showArrayAsFlatTable = false;
+        } else {
+            // Force to boolean just in case
+            options.showArrayAsFlatTable = options.showArrayAsFlatTable ? true: false;
+        }
+
+        if (options.showArrayAsFlatTable) {
+            options.flatTableKeys = isArray(options.flatTableKeys) ? options.flatTableKeys : [];
+        }
+
+        return options;
+    }
+
+    function validateHyperlinkOptions(options){
+        var hyperlinks = {
+            enable : false,
+            keys : null,
+            target : ''
+        };
+
+        if(options.hyperlinks && options.hyperlinks.enable) {
+            hyperlinks.enable = true;
+
+            hyperlinks.keys =  isArray(options.hyperlinks.keys) ? options.hyperlinks.keys : [];
+
+            if(options.hyperlinks.target) {
+                hyperlinks.target = '' + options.hyperlinks.target;
+            } else {
+                hyperlinks.target = '_blank';
+            }
+        }
+
+        options.hyperlinks = hyperlinks;
+
+        return options;
+    }
+
+    function validateBoolOptions(options){
+        if(!options.bool){
+            options.bool = {
+                text:  {
+                    true : "true",
+                    false : "false"
+                },
+                img : {
+                    true: "",
+                    false: ""
+                },
+                showImage : false,
+                showText : true
+            };
+        } else {
+            var boolOptions = options.bool;
+
+            // Show text if no option
+            if(!boolOptions.showText && !boolOptions.showImage){
+                boolOptions.showImage  =  false;
+                boolOptions.showText  =  true;
+            }
+
+            if(boolOptions.showText){
+                if(!boolOptions.text){
+                    boolOptions.text = {
+                        true : "true",
+                        false : "false"
+                    };
+                } else {
+                    var t = boolOptions.text.true, f = boolOptions.text.false;
+
+                    if(getType(t) != STRING || t === ''){
+                        boolOptions.text.true = 'true';
+                    }
+
+                    if(getType(f) != STRING || f === ''){
+                        boolOptions.text.false = 'false';
+                    }
+                }
+            }
+
+            if(boolOptions.showImage){
+                if(!boolOptions.img.true && !boolOptions.img.false){
+                    boolOptions.showImage = false;
+                }
+            }
+        }
+
+        return options;
+    }
+
+    return {
+        format: format
+    };
+}));

http://git-wip-us.apache.org/repos/asf/hive/blob/749e27a9/service/src/resources/hive-webapps/static/js/llap.js
----------------------------------------------------------------------
diff --git a/service/src/resources/hive-webapps/static/js/llap.js b/service/src/resources/hive-webapps/static/js/llap.js
new file mode 100644
index 0000000..e84fd03
--- /dev/null
+++ b/service/src/resources/hive-webapps/static/js/llap.js
@@ -0,0 +1,37 @@
+window.options = {
+    showArrayAsFlatTable: true,
+    flatTableKeys: ['containerId','webUrl'],
+
+    showArrayIndex: false,
+    hyperlinks : {
+        enable : true,
+        keys: ['amWebUrl','statusUrl','webUrl'],
+        target : '_blank'
+    },
+
+    bool : {
+        showText : true,
+        text : {
+            true : "true",
+            false : "false"
+        },
+        showImage : true,
+        img : {
+            true : 'css/true.png',
+            false : 'css/false.png'
+        }
+    }
+};
+
+$(document).ready(function () {
+    var showData = $('#show-data');
+
+    $.getJSON('llap', function (data) {
+        console.log(data);
+
+        showData.empty();
+        var node = JsonHuman.format(data, window.options);
+        showData[0].appendChild(node);
+    });
+    showData.text('Loading the JSON file.');
+});