diff --git common/src/java/org/apache/hive/http/HttpServer.java common/src/java/org/apache/hive/http/HttpServer.java index 32956b1..9ceaf3f 100644 --- common/src/java/org/apache/hive/http/HttpServer.java +++ common/src/java/org/apache/hive/http/HttpServer.java @@ -22,6 +22,8 @@ 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.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 @@ private HttpServer(final Builder b) throws IOException { private int port; private int maxThreads; private HiveConf conf; - private Map contextAttrs = new HashMap(); + private final Map contextAttrs = new HashMap(); private String keyStorePassword; private String keyStorePath; private String spnegoPrincipal; private String spnegoKeytab; private boolean useSPNEGO; private boolean useSSL; + private final List>> servlets = + new LinkedList>>(); public Builder(String name) { Preconditions.checkArgument(name != null && !name.isEmpty(), "Name must be specified"); @@ -188,6 +193,11 @@ public Builder setContextAttribute(String name, Object value) { contextAttrs.put(name, value); return this; } + + public Builder addServlet(String endpoint, Class servlet) { + servlets.add(new Pair>(endpoint, servlet)); + return this; + } } public void start() throws Exception { @@ -383,6 +393,10 @@ void initializeWebServer(Builder b) { addServlet("conf", "/conf", ConfServlet.class); addServlet("stacks", "/stacks", StackServlet.class); + for (Pair> p : b.servlets) { + addServlet(p.getFirst(), "/" + p.getFirst(), p.getSecond()); + } + ServletContextHandler staticCtx = new ServletContextHandler(contexts, "/static"); staticCtx.setResourceBase(appDir + "/static"); diff --git llap-server/src/java/org/apache/hadoop/hive/llap/cli/LlapStatusServiceDriver.java llap-server/src/java/org/apache/hadoop/hive/llap/cli/LlapStatusServiceDriver.java index d1193ad..45ba5d0 100644 --- llap-server/src/java/org/apache/hadoop/hive/llap/cli/LlapStatusServiceDriver.java +++ llap-server/src/java/org/apache/hadoop/hive/llap/cli/LlapStatusServiceDriver.java @@ -20,6 +20,7 @@ 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 LlapStatusServiceDriver() { } - private int run(String[] args) { + public int run(String[] args) { SliderClient sliderClient = null; try { @@ -148,14 +149,13 @@ private int run(String[] args) { } } - 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 @@ private ExitCode populateAppStatusFromLlapRegistry(LlapStatusOptions options, Ap private Long appStartTime; private Long appFinishTime; - private List llapInstances = new LinkedList<>(); + private final List llapInstances = new LinkedList<>(); private transient Map containerToInstanceMap = new HashMap<>(); @@ -771,7 +771,7 @@ public ExitCode getExitCode() { RUNNING_ALL, COMPLETE, UNKNOWN } - enum ExitCode { + public enum ExitCode { SUCCESS(0), INCORRECT_USAGE(10), YARN_ERROR(20), @@ -806,7 +806,7 @@ public static void main(String[] args) { LlapStatusServiceDriver statusServiceDriver = new LlapStatusServiceDriver(); ret = statusServiceDriver.run(args); if (ret == ExitCode.SUCCESS.getInt()) { - statusServiceDriver.outputJson(); + statusServiceDriver.outputJson(new PrintWriter(System.out)); } } catch (Throwable t) { diff --git ql/src/java/org/apache/hadoop/hive/ql/optimizer/physical/LlapDecider.java ql/src/java/org/apache/hadoop/hive/ql/optimizer/physical/LlapDecider.java index d3ec7ff..c0b7a32 100644 --- ql/src/java/org/apache/hadoop/hive/ql/optimizer/physical/LlapDecider.java +++ ql/src/java/org/apache/hadoop/hive/ql/optimizer/physical/LlapDecider.java @@ -73,17 +73,17 @@ 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 { diff --git service/pom.xml service/pom.xml index 41a4ef1..ae82cd3 100644 --- service/pom.xml +++ service/pom.xml @@ -49,6 +49,12 @@ hive-service-rpc ${project.version} + + org.apache.hive + hive-llap-server + ${project.version} + + commons-codec diff --git service/src/java/org/apache/hive/http/LlapServlet.java service/src/java/org/apache/hive/http/LlapServlet.java new file mode 100644 index 0000000..2095f07 --- /dev/null +++ service/src/java/org/apache/hive/http/LlapServlet.java @@ -0,0 +1,83 @@ +/** + * 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.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 { + } + + /** + * 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; + 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(); + LlapStatusServiceDriver driver = new LlapStatusServiceDriver(); + int ret = driver.run(new String[] { "-n", "llap0" }); + 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); + } + } +} \ No newline at end of file diff --git service/src/java/org/apache/hive/service/server/HiveServer2.java service/src/java/org/apache/hive/service/server/HiveServer2.java index 882f4ae..d61edf5 100644 --- service/src/java/org/apache/hive/service/server/HiveServer2.java +++ service/src/java/org/apache/hive/service/server/HiveServer2.java @@ -59,6 +59,7 @@ 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 void run() { builder.setSPNEGOKeytab(spnegoKeytab); builder.setUseSPNEGO(true); } + builder.addServlet("llap", LlapServlet.class); webServer = builder.build(); webServer.addServlet("query_page", "/query_page", QueryProfileServlet.class); } diff --git service/src/resources/hive-webapps/hiveserver2/hiveserver2.jsp service/src/resources/hive-webapps/hiveserver2/hiveserver2.jsp index 293a8ef..3c187b6 100644 --- service/src/resources/hive-webapps/hiveserver2/hiveserver2.jsp +++ service/src/resources/hive-webapps/hiveserver2/hiveserver2.jsp @@ -75,6 +75,7 @@ SessionManager sessionManager =
  • Metrics Dump
  • Hive Configuration
  • Stack Trace
  • +
  • Llap Daemons
  • diff --git service/src/resources/hive-webapps/hiveserver2/llap.html service/src/resources/hive-webapps/hiveserver2/llap.html new file mode 100644 index 0000000..e1424b8 --- /dev/null +++ service/src/resources/hive-webapps/hiveserver2/llap.html @@ -0,0 +1,47 @@ + + + + + + HiveServer2 + + + + + + + + + + + + + + + +
    + + diff --git service/src/resources/hive-webapps/static/css/json.human.css service/src/resources/hive-webapps/static/css/json.human.css new file mode 100644 index 0000000..2ee552f --- /dev/null +++ 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; +} + diff --git service/src/resources/hive-webapps/static/js/json.human.js service/src/resources/hive-webapps/static/js/json.human.js new file mode 100644 index 0000000..9ff6d64 --- /dev/null +++ service/src/resources/hive-webapps/static/js/json.human.js @@ -0,0 +1,449 @@ +/*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 + }; +})); diff --git service/src/resources/hive-webapps/static/js/llap.js service/src/resources/hive-webapps/static/js/llap.js new file mode 100644 index 0000000..e84fd03 --- /dev/null +++ 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.'); +});