HIVE-12952 : Show query sub-pages on webui (Szehon, reviewed by Aihua Xu and 
Lenni Kuff)


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

Branch: refs/heads/master
Commit: 2d94c0b0bd9fe88747eb749dba8e1ab51d94a4ff
Parents: f2e9edb
Author: Szehon Ho <sze...@cloudera.com>
Authored: Wed Feb 3 22:18:49 2016 +0100
Committer: Szehon Ho <sze...@cloudera.com>
Committed: Wed Feb 3 22:18:49 2016 +0100

----------------------------------------------------------------------
 .../org/apache/hadoop/hive/conf/HiveConf.java   |   15 +
 .../apache/hadoop/hive/ql/log/PerfLogger.java   |    9 +
 .../java/org/apache/hive/http/HttpServer.java   |    2 +-
 pom.xml                                         |   12 +
 .../java/org/apache/hadoop/hive/ql/Driver.java  |   48 +-
 .../org/apache/hadoop/hive/ql/QueryDisplay.java |  253 +++
 .../apache/hadoop/hive/ql/metadata/Hive.java    |    6 +-
 service/pom.xml                                 |   51 +-
 .../org/apache/hive/tmpl/QueryProfileTmpl.jamon |  298 +++
 .../hive/service/cli/operation/Operation.java   |    4 +
 .../service/cli/operation/OperationManager.java |   82 +-
 .../service/cli/operation/SQLOperation.java     |   39 +-
 .../cli/operation/SQLOperationDisplay.java      |   99 +
 .../cli/operation/SQLOperationDisplayCache.java |   39 +
 .../service/cli/operation/SQLOperationInfo.java |   48 -
 .../apache/hive/service/server/HiveServer2.java |    2 +
 .../service/servlet/QueryProfileServlet.java    |   49 +
 .../hive-webapps/hiveserver2/hiveserver2.jsp    |   72 +-
 .../hive-webapps/static/js/bootstrap.js         | 1999 ++++++++++++++++++
 .../hive-webapps/static/js/bootstrap.min.js     |    6 +
 .../hive-webapps/static/js/jquery.min.js        |    2 +
 .../src/resources/hive-webapps/static/js/tab.js |   38 +
 22 files changed, 3028 insertions(+), 145 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
----------------------------------------------------------------------
diff --git a/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java 
b/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
index 6678de6..73e6c21 100644
--- a/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
+++ b/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
@@ -3741,6 +3741,21 @@ public class HiveConf extends Configuration {
     }
   }
 
+  /**
+   * @return true if HS2 webui is enabled
+   */
+  public boolean isWebUiEnabled() {
+    return this.getIntVar(ConfVars.HIVE_SERVER2_WEBUI_PORT) != 0;
+  }
+
+  /**
+   * @return true if HS2 webui query-info cache is enabled
+   */
+  public boolean isWebUiQueryInfoCacheEnabled() {
+    return isWebUiEnabled() && 
this.getIntVar(ConfVars.HIVE_SERVER2_WEBUI_MAX_HISTORIC_QUERIES) > 0;
+  }
+
+
   public static boolean isLoadMetastoreConfig() {
     return loadMetastoreConfig;
   }

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/common/src/java/org/apache/hadoop/hive/ql/log/PerfLogger.java
----------------------------------------------------------------------
diff --git a/common/src/java/org/apache/hadoop/hive/ql/log/PerfLogger.java 
b/common/src/java/org/apache/hadoop/hive/ql/log/PerfLogger.java
index d4194cf..8fa5cbf 100644
--- a/common/src/java/org/apache/hadoop/hive/ql/log/PerfLogger.java
+++ b/common/src/java/org/apache/hadoop/hive/ql/log/PerfLogger.java
@@ -18,6 +18,7 @@
 
 package org.apache.hadoop.hive.ql.log;
 
+import com.google.common.collect.ImmutableMap;
 import org.apache.hadoop.hive.common.metrics.common.Metrics;
 import org.apache.hadoop.hive.common.metrics.common.MetricsFactory;
 import org.apache.hadoop.hive.conf.HiveConf;
@@ -222,4 +223,12 @@ public class PerfLogger {
       LOG.warn("Error recording metrics", e);
     }
   }
+
+  public ImmutableMap<String, Long> getStartTimes() {
+    return ImmutableMap.copyOf(startTimes);
+  }
+
+  public ImmutableMap<String, Long> getEndTimes() {
+    return ImmutableMap.copyOf(endTimes);
+  }
 }

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/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 9e23b11..b8836de 100644
--- a/common/src/java/org/apache/hive/http/HttpServer.java
+++ b/common/src/java/org/apache/hive/http/HttpServer.java
@@ -435,7 +435,7 @@ public class HttpServer {
    * @param pathSpec The path spec for the servlet
    * @param clazz The servlet class
    */
-  void addServlet(String name, String pathSpec,
+  public void addServlet(String name, String pathSpec,
       Class<? extends HttpServlet> clazz) {
     ServletHolder holder = new ServletHolder(clazz);
     if (name != null) {

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 8c2257f..3c06188 100644
--- a/pom.xml
+++ b/pom.xml
@@ -140,6 +140,8 @@
     <!-- jackson 1 and 2 lines can coexist without issue, as they have 
different artifactIds -->
     <jackson.new.version>2.4.2</jackson.new.version>
     <jasper.version>5.5.23</jasper.version>
+    <jamon.plugin.version>2.3.4</jamon.plugin.version>
+    <jamon-runtime.version>2.3.1</jamon-runtime.version>
     <javaewah.version>0.3.2</javaewah.version>
     <javax-servlet.version>3.0.0.v201112011016</javax-servlet.version>
     <javolution.version>5.5.1</javolution.version>
@@ -725,6 +727,11 @@
           </exclusion>
         </exclusions>
       </dependency>
+      <dependency>
+        <groupId>org.jamon</groupId>
+        <artifactId>jamon-runtime</artifactId>
+        <version>${jamon-runtime.version}</version>
+      </dependency>
     </dependencies>
   </dependencyManagement>
 
@@ -1064,6 +1071,11 @@
          </excludes>
        </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.jamon</groupId>
+        <artifactId>jamon-maven-plugin</artifactId>
+        <version>${jamon.plugin.version}</version>
+      </plugin>
     </plugins>
   </build>
 

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/ql/src/java/org/apache/hadoop/hive/ql/Driver.java
----------------------------------------------------------------------
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/Driver.java 
b/ql/src/java/org/apache/hadoop/hive/ql/Driver.java
index bcf62a7..7147a9a 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/Driver.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/Driver.java
@@ -36,6 +36,7 @@ import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
 import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.mapreduce.MRJobConfig;
@@ -53,7 +54,6 @@ import org.apache.hadoop.hive.metastore.api.Schema;
 import org.apache.hadoop.hive.ql.exec.ConditionalTask;
 import org.apache.hadoop.hive.ql.exec.ExplainTask;
 import org.apache.hadoop.hive.ql.exec.FetchTask;
-import org.apache.hadoop.hive.ql.exec.Operator;
 import org.apache.hadoop.hive.ql.exec.TableScanOperator;
 import org.apache.hadoop.hive.ql.exec.Task;
 import org.apache.hadoop.hive.ql.exec.TaskFactory;
@@ -159,7 +159,7 @@ public class Driver implements CommandProcessor {
   private String operationId;
 
   // For WebUI.  Kept alive after queryPlan is freed.
-  private String savedQueryString;
+  private final QueryDisplay queryDisplay = new QueryDisplay();
 
   private boolean checkConcurrency() {
     boolean supportConcurrency = 
conf.getBoolVar(HiveConf.ConfVars.HIVE_SUPPORT_CONCURRENCY);
@@ -387,7 +387,6 @@ public class Driver implements CommandProcessor {
     } catch (Exception e) {
       LOG.warn("WARNING! Query command could not be redacted." + e);
     }
-    this.savedQueryString = queryStr;
 
     //holder for parent command type/string when executing reentrant queries
     QueryState queryState = new QueryState();
@@ -409,6 +408,10 @@ public class Driver implements CommandProcessor {
       conf.setVar(HiveConf.ConfVars.HIVEQUERYID, queryId);
     }
 
+    //save some info for webUI for use after plan is freed
+    this.queryDisplay.setQueryStr(queryStr);
+    this.queryDisplay.setQueryId(queryId);
+
     LOG.info("Compiling command(queryId=" + queryId + "): " + queryStr);
 
     SessionState.get().setupQueryCurrentTimestamp();
@@ -519,11 +522,17 @@ public class Driver implements CommandProcessor {
         }
       }
 
-      if (conf.getBoolVar(ConfVars.HIVE_LOG_EXPLAIN_OUTPUT)) {
+      if (conf.getBoolVar(ConfVars.HIVE_LOG_EXPLAIN_OUTPUT) ||
+           conf.isWebUiQueryInfoCacheEnabled()) {
         String explainOutput = getExplainOutput(sem, plan, tree);
         if (explainOutput != null) {
-          LOG.info("EXPLAIN output for queryid " + queryId + " : "
+          if (conf.getBoolVar(ConfVars.HIVE_LOG_EXPLAIN_OUTPUT)) {
+            LOG.info("EXPLAIN output for queryid " + queryId + " : "
               + explainOutput);
+          }
+          if (conf.isWebUiQueryInfoCacheEnabled()) {
+            queryDisplay.setExplainPlan(explainOutput);
+          }
         }
       }
       return 0;
@@ -553,18 +562,20 @@ public class Driver implements CommandProcessor {
       // since it exceeds valid range of shell return values
     } finally {
       double duration = perfLogger.PerfLogEnd(CLASS_NAME, 
PerfLogger.COMPILE)/1000.00;
-      dumpMetaCallTimingWithoutEx("compilation");
+      ImmutableMap<String, Long> compileHMSTimings = 
dumpMetaCallTimingWithoutEx("compilation");
+      queryDisplay.setHmsTimings(QueryDisplay.Phase.COMPILATION, 
compileHMSTimings);
       restoreSession(queryState);
       LOG.info("Completed compiling command(queryId=" + queryId + "); Time 
taken: " + duration + " seconds");
     }
   }
 
-  private void dumpMetaCallTimingWithoutEx(String phase) {
+  private ImmutableMap<String, Long> dumpMetaCallTimingWithoutEx(String phase) 
{
     try {
-      Hive.get().dumpAndClearMetaCallTiming(phase);
+      return Hive.get().dumpAndClearMetaCallTiming(phase);
     } catch (HiveException he) {
       LOG.warn("Caught exception attempting to write metadata call information 
" + he, he);
     }
+    return null;
   }
 
   /**
@@ -1186,6 +1197,13 @@ public class Driver implements CommandProcessor {
             + org.apache.hadoop.util.StringUtils.stringifyException(e));
       }
     }
+
+    //Save compile-time PerfLogging for WebUI.
+    //Execution-time Perf logs are done by either another thread's PerfLogger
+    //or a reset PerfLogger.
+    PerfLogger perfLogger = SessionState.getPerfLogger();
+    queryDisplay.setPerfLogStarts(QueryDisplay.Phase.COMPILATION, 
perfLogger.getStartTimes());
+    queryDisplay.setPerfLogEnds(QueryDisplay.Phase.COMPILATION, 
perfLogger.getEndTimes());
     return ret;
   }
 
@@ -1343,6 +1361,8 @@ public class Driver implements CommandProcessor {
     }
 
     perfLogger.PerfLogEnd(CLASS_NAME, PerfLogger.DRIVER_RUN);
+    queryDisplay.setPerfLogStarts(QueryDisplay.Phase.EXECUTION, 
perfLogger.getStartTimes());
+    queryDisplay.setPerfLogEnds(QueryDisplay.Phase.EXECUTION, 
perfLogger.getEndTimes());
 
     // Take all the driver run hooks and post-execute them.
     try {
@@ -1414,6 +1434,7 @@ public class Driver implements CommandProcessor {
   }
 
   private CommandProcessorResponse createProcessorResponse(int ret) {
+    queryDisplay.setErrorMessage(errorMessage);
     return new CommandProcessorResponse(ret, errorMessage, SQLState, 
downstreamError);
   }
 
@@ -1544,6 +1565,7 @@ public class Driver implements CommandProcessor {
         // Launch upto maxthreads tasks
         Task<? extends Serializable> task;
         while ((task = driverCxt.getRunnable(maxthreads)) != null) {
+          queryDisplay.addTask(task);
           TaskRunner runner = launchTask(task, queryId, noName, jobname, jobs, 
driverCxt);
           if (!runner.isRunning()) {
             break;
@@ -1556,6 +1578,7 @@ public class Driver implements CommandProcessor {
           continue;
         }
         hookContext.addCompleteTask(tskRun);
+        queryDisplay.setTaskCompleted(tskRun.getTask().getId(), 
tskRun.getTaskResult());
 
         Task<? extends Serializable> tsk = tskRun.getTask();
         TaskResult result = tskRun.getTaskResult();
@@ -1694,9 +1717,11 @@ public class Driver implements CommandProcessor {
       if (noName) {
         conf.set(MRJobConfig.JOB_NAME, "");
       }
-      dumpMetaCallTimingWithoutEx("execution");
       double duration = perfLogger.PerfLogEnd(CLASS_NAME, 
PerfLogger.DRIVER_EXECUTE)/1000.00;
 
+      ImmutableMap<String, Long> executionHMSTimings = 
dumpMetaCallTimingWithoutEx("execution");
+      queryDisplay.setHmsTimings(QueryDisplay.Phase.EXECUTION, 
executionHMSTimings);
+
       Map<String, MapRedStats> stats = SessionState.get().getMapRedStats();
       if (stats != null && !stats.isEmpty()) {
         long totalCpu = 0;
@@ -1952,8 +1977,8 @@ public class Driver implements CommandProcessor {
   }
 
 
-  public String getQueryString() {
-    return savedQueryString == null ? "Unknown" : savedQueryString;
+  public QueryDisplay getQueryDisplay() {
+    return queryDisplay;
   }
 
   /**
@@ -1963,5 +1988,4 @@ public class Driver implements CommandProcessor {
   public void setOperationId(String opId) {
     this.operationId = opId;
   }
-
 }

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/ql/src/java/org/apache/hadoop/hive/ql/QueryDisplay.java
----------------------------------------------------------------------
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/QueryDisplay.java 
b/ql/src/java/org/apache/hadoop/hive/ql/QueryDisplay.java
new file mode 100644
index 0000000..c87c825
--- /dev/null
+++ b/ql/src/java/org/apache/hadoop/hive/ql/QueryDisplay.java
@@ -0,0 +1,253 @@
+/**
+ * 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.hive.ql;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.hadoop.hive.ql.exec.Task;
+import org.apache.hadoop.hive.ql.exec.TaskResult;
+import org.apache.hadoop.hive.ql.plan.api.StageType;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Some limited query information to save for WebUI.
+ *
+ * The class is synchronized, as WebUI may access information about a running 
query.
+ */
+public class QueryDisplay {
+
+  // Member variables
+  private String queryStr;
+  private String explainPlan;
+  private String errorMessage;
+  private String queryId;
+
+  private final Map<Phase, Map<String, Long>> hmsTimingMap = new HashMap();
+  private final Map<Phase, Map<String, Long>> perfLogStartMap = new HashMap();
+  private final Map<Phase, Map<String, Long>> perfLogEndMap = new HashMap();
+
+  private final LinkedHashMap<String, TaskInfo> tasks = new 
LinkedHashMap<String, TaskInfo>();
+
+  //Inner classes
+  public static enum Phase {
+    COMPILATION,
+    EXECUTION,
+  }
+
+  public static class TaskInfo {
+    private Integer returnVal;  //if set, determines that task is complete.
+    private String errorMsg;
+    private long endTime;
+
+    final long beginTime;
+    final String taskId;
+    final StageType taskType;
+    final String name;
+    final boolean requireLock;
+    final boolean retryIfFail;
+
+    public TaskInfo (Task task) {
+      beginTime = System.currentTimeMillis();
+      taskId = task.getId();
+      taskType = task.getType();
+      name = task.getName();
+      requireLock = task.requireLock();
+      retryIfFail = task.ifRetryCmdWhenFail();
+    }
+
+    public synchronized String getStatus() {
+      if (returnVal == null) {
+        return "Running";
+      } else if (returnVal == 0) {
+        return "Success, ReturnVal 0";
+      } else {
+        return "Failure, ReturnVal " + String.valueOf(returnVal);
+      }
+    }
+
+    public synchronized long getElapsedTime() {
+      if (endTime == 0) {
+        return System.currentTimeMillis() - beginTime;
+      } else {
+        return endTime - beginTime;
+      }
+    }
+
+    public synchronized String getErrorMsg() {
+      return errorMsg;
+    }
+
+    public synchronized long getEndTime() {
+      return endTime;
+    }
+
+    //Following methods do not need to be synchronized, because they are final 
fields.
+    public long getBeginTime() {
+      return beginTime;
+    }
+
+    public String getTaskId() {
+      return taskId;
+    }
+
+    public StageType getTaskType() {
+      return taskType;
+    }
+
+    public String getName() {
+      return name;
+    }
+
+    public boolean isRequireLock() {
+      return requireLock;
+    }
+
+    public boolean isRetryIfFail() {
+      return retryIfFail;
+    }
+  }
+
+  public synchronized void addTask(Task task) {
+    tasks.put(task.getId(), new TaskInfo(task));
+  }
+
+  public synchronized void setTaskCompleted(String taskId, TaskResult result) {
+    TaskInfo taskInfo = tasks.get(taskId);
+    if (taskInfo != null) {
+      taskInfo.returnVal = result.getExitVal();
+      if (result.getTaskError() != null) {
+        taskInfo.errorMsg = result.getTaskError().toString();
+      }
+      taskInfo.endTime = System.currentTimeMillis();
+    }
+  }
+
+  public synchronized List<TaskInfo> getTaskInfos() {
+    List<TaskInfo> taskInfos = new ArrayList<TaskInfo>();
+    taskInfos.addAll(tasks.values());
+    return taskInfos;
+  }
+
+  public synchronized void setQueryStr(String queryStr) {
+    this.queryStr = queryStr;
+  }
+
+  public synchronized String getQueryString() {
+    return returnStringOrUnknown(queryStr);
+  }
+
+  public synchronized String getExplainPlan() {
+    return returnStringOrUnknown(explainPlan);
+  }
+
+  public synchronized void setExplainPlan(String explainPlan) {
+    this.explainPlan = explainPlan;
+  }
+
+  /**
+   * @param phase phase of query
+   * @return map of HMS Client method-calls and duration in miliseconds, 
during given phase.
+   */
+  public synchronized Map<String, Long> getHmsTimings(Phase phase) {
+    return hmsTimingMap.get(phase);
+  }
+
+  /**
+   * @param phase phase of query
+   * @param hmsTimings map of HMS Client method-calls and duration in 
miliseconds, during given phase.
+   */
+  public synchronized void setHmsTimings(Phase phase, ImmutableMap<String, 
Long> hmsTimings) {
+    hmsTimingMap.put(phase, hmsTimings);
+  }
+
+  /**
+   * @param phase phase of query
+   * @return map of PerfLogger call-trace name and start time in miliseconds, 
during given phase.
+   */
+  public synchronized Map<String, Long> getPerfLogStarts(Phase phase) {
+    return perfLogStartMap.get(phase);
+  }
+
+  /**
+   * @param phase phase of query
+   * @param perfLogStarts map of PerfLogger call-trace name and start time in 
miliseconds, during given phase.
+   */
+  public synchronized void setPerfLogStarts(Phase phase, ImmutableMap<String, 
Long> perfLogStarts) {
+    perfLogStartMap.put(phase, perfLogStarts);
+  }
+
+  /**
+   * @param phase phase of query
+   * @return map of PerfLogger call-trace name and end time in miliseconds, 
during given phase.
+   */
+  public synchronized Map<String, Long> getPerfLogEnds(Phase phase) {
+    return perfLogEndMap.get(phase);
+  }
+
+  /**
+   * @param phase phase of query
+   * @param perfLogEnds map of PerfLogger call-trace name and end time in 
miliseconds, during given phase.
+   */
+   public synchronized void setPerfLogEnds(Phase phase, ImmutableMap<String, 
Long> perfLogEnds) {
+    perfLogEndMap.put(phase, perfLogEnds);
+  }
+
+  /**
+   * @param phase phase of query
+   * @return map of PerfLogger call-trace name and duration in miliseconds, 
during given phase.
+   */
+  public synchronized Map<String, Long> getPerfLogTimes(Phase phase) {
+    Map<String, Long> times = new HashMap<>();
+    Map<String, Long> startTimes = perfLogStartMap.get(phase);
+    Map<String, Long> endTimes = perfLogEndMap.get(phase);
+    if (endTimes != null && startTimes != null) {
+      for (String timeKey : endTimes.keySet()) {
+        Long endTime = endTimes.get(timeKey);
+        Long startTime = startTimes.get(timeKey);
+        if (startTime != null) {
+          times.put(timeKey, endTime - startTime);
+        }
+      }
+    }
+    return times;
+  }
+
+  public synchronized String getErrorMessage() {
+    return errorMessage;
+  }
+
+  public synchronized void setErrorMessage(String errorMessage) {
+    this.errorMessage = errorMessage;
+  }
+
+  public synchronized String getQueryId() {
+    return returnStringOrUnknown(queryId);
+  }
+
+  public synchronized void setQueryId(String queryId) {
+    this.queryId = queryId;
+  }
+
+  private String returnStringOrUnknown(String s) {
+    return s == null ? "UNKNOWN" : s;
+  }
+}

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/ql/src/java/org/apache/hadoop/hive/ql/metadata/Hive.java
----------------------------------------------------------------------
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/metadata/Hive.java 
b/ql/src/java/org/apache/hadoop/hive/ql/metadata/Hive.java
index 0bab769..4e574f7 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/metadata/Hive.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/metadata/Hive.java
@@ -44,6 +44,7 @@ import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import com.google.common.collect.ImmutableMap;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileStatus;
 import org.apache.hadoop.fs.FileSystem;
@@ -3400,7 +3401,7 @@ private void constructOneLBLocationMap(FileStatus fSta,
     metaCallTimeMap.clear();
   }
 
-  public void dumpAndClearMetaCallTiming(String phase) {
+  public ImmutableMap<String, Long> dumpAndClearMetaCallTiming(String phase) {
     boolean phaseInfoLogged = false;
     if (LOG.isDebugEnabled()) {
       phaseInfoLogged = logDumpPhase(phase);
@@ -3420,7 +3421,10 @@ private void constructOneLBLocationMap(FileStatus fSta,
         }
       }
     }
+
+    ImmutableMap<String, Long> result = ImmutableMap.copyOf(metaCallTimeMap);
     metaCallTimeMap.clear();
+    return result;
   }
 
   private boolean logDumpPhase(String phase) {

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/service/pom.xml
----------------------------------------------------------------------
diff --git a/service/pom.xml b/service/pom.xml
index b2e3a84..e3f61d0 100644
--- a/service/pom.xml
+++ b/service/pom.xml
@@ -128,7 +128,13 @@
       <version>${hadoop.version}</version>
       <optional>true</optional>
     </dependency>
-     <!-- intra-project -->
+    <dependency>
+      <groupId>org.jamon</groupId>
+      <artifactId>jamon-runtime</artifactId>
+      <version>${jamon-runtime.version}</version>
+    </dependency>
+
+      <!-- intra-project -->
     <dependency>
       <groupId>org.apache.hive</groupId>
       <artifactId>hive-exec</artifactId>
@@ -219,7 +225,48 @@
           </execution>
         </executions>
       </plugin>
-      <plugin>
+        <plugin>
+            <groupId>org.jamon</groupId>
+            <artifactId>jamon-maven-plugin</artifactId>
+            <executions>
+                <execution>
+                    <phase>generate-sources</phase>
+                    <goals>
+                        <goal>translate</goal>
+                    </goals>
+                    <configuration>
+                        <templateSourceDir>src/jamon</templateSourceDir>
+                        
<templateOutputDir>target/generated-jamon</templateOutputDir>
+                    </configuration>
+                </execution>
+            </executions>
+        </plugin>
+        <!-- General plugins -->
+        <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-eclipse-plugin</artifactId>
+            <configuration>
+                <additionalProjectnatures>
+                    
<projectnature>org.jamon.project.jamonnature</projectnature>
+                </additionalProjectnatures>
+                <buildcommands>
+                    
<buildcommand>org.jamon.project.templateBuilder</buildcommand>
+                    
<buildcommand>org.eclipse.jdt.core.javabuilder</buildcommand>
+                    
<buildcommand>org.jamon.project.markerUpdater</buildcommand>
+                </buildcommands>
+                <additionalConfig>
+                    <file>
+                        <name>.settings/org.jamon.prefs</name>
+                        <content># now
+                            eclipse.preferences.version=1
+                            templateSourceDir=src/main/jamon
+                            templateOutputDir=target/generated-jamon
+                        </content>
+                    </file>
+                </additionalConfig>
+            </configuration>
+        </plugin>
+        <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jar-plugin</artifactId>
         <executions>

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/service/src/jamon/org/apache/hive/tmpl/QueryProfileTmpl.jamon
----------------------------------------------------------------------
diff --git a/service/src/jamon/org/apache/hive/tmpl/QueryProfileTmpl.jamon 
b/service/src/jamon/org/apache/hive/tmpl/QueryProfileTmpl.jamon
new file mode 100644
index 0000000..c513689
--- /dev/null
+++ b/service/src/jamon/org/apache/hive/tmpl/QueryProfileTmpl.jamon
@@ -0,0 +1,298 @@
+<%doc>
+
+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.
+</%doc>
+<%args>
+SQLOperationDisplay sod;
+</%args>
+<%import>
+java.util.*;
+org.apache.hadoop.hive.ql.QueryDisplay;
+org.apache.hive.service.cli.operation.SQLOperationDisplay;
+</%import>
+<!--[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">
+  </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>
+            </ul>
+          </div><!--/.nav-collapse -->
+        </div>
+      </div>
+    </div>
+
+
+    <%if sod == null %>
+         <div class="jumbotron">
+           <p>Query not found.  It may have been deleted, increase 
<i>hive.server2.webui.max.historic.queries</i>
+              to retain more historic query information.</p>
+         </div>
+    <%else>
+
+
+    <div class="container">
+      <div class="row inner_header">
+        <div class="page-header">
+          <h1>Query Information: <% sod.getQueryDisplay() == null ? "Unknown" 
: sod.getQueryDisplay().getQueryString() %></h1>
+        </div>
+      </div>
+      <div class="row">
+
+
+      <div class="tabbable">
+          <ul class="nav nav-pills">
+              <li class="active"><a href="#tab_baseProfile" 
data-toggle="tab">Base Profile</a></li>
+              <li class=""><a href="#tab_stages" 
data-toggle="tab">Stages</a></li>
+              <li class=""><a href="#tab_queryPlan" data-toggle="tab">Query 
Plan</a></li>
+              <li class=""><a href="#tab_perfLogging" 
data-toggle="tab">Performance Logging</a></li>
+          </ul>
+          <div class="tab-content" style="padding-bottom: 9px; border-bottom: 
1px solid #ddd;">
+              <div class="tab-pane active" id="tab_baseProfile">
+                  <& baseProfile; sod = sod &>
+              </div>
+              <div class="tab-pane" id="tab_stages">
+                  <& stages; sod = sod &>
+              </div>
+              <div class="tab-pane" id="tab_queryPlan">
+                  <& queryPlan; sod = sod &>
+              </div>
+              <div class="tab-pane" id="tab_perfLogging">
+                  <& perfLogging; sod = sod &>
+              </div>
+          </div>
+      </div>
+
+   </%if>
+
+</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>
+
+<%def baseProfile>
+<%args>
+    SQLOperationDisplay sod;
+</%args>
+    <table class="table table-striped">
+        <tr>
+            <td>User Name</td>
+            <td><% sod.getUserName() %></td>
+        </tr>
+        <tr>
+            <td>Query String</td>
+            <td><% sod.getQueryDisplay() == null ? "Unknown" : 
sod.getQueryDisplay().getQueryString() %></td>
+        </tr>
+        <tr>
+            <td>Query Id</td>
+            <td><% sod.getQueryDisplay() == null ? "Unknown" : 
sod.getQueryDisplay().getQueryId() %></td>
+        </tr>
+        <tr>
+            <td>Execution Engine</td>
+            <td><% sod.getExecutionEngine() %>
+        </tr>
+        <tr>
+            <td>State</td>
+            <td><% sod.getState() %></td>
+        </tr>
+        <tr>
+            <td>Begin Time</td>
+            <td><% new Date(sod.getBeginTime()) %></td>
+        </tr>
+        <tr>
+            <td>Elapsed Time (s)</td>
+            <td><% sod.getElapsedTime()/1000 %></td>
+        </tr>
+        <tr>
+            <td>End Time</td>
+            <td><% sod.getEndTime() == null ? "In Progress" : new 
Date(sod.getEndTime()) %></td>
+        </tr>
+        <%if sod.getQueryDisplay() != null && 
sod.getQueryDisplay().getErrorMessage() != null %>
+            <tr>
+                <td>Error</td>
+                <td><% sod.getEndTime() == null ? "In Progress" : new 
Date(sod.getEndTime()) %></td>
+            </tr>
+        </%if>
+    </table>
+</%def>
+
+<%def stages>
+<%args>
+    SQLOperationDisplay sod;
+</%args>
+   <table id="attributes_table" class="table table-striped">
+       <tr>
+           <th>Stage Id</th>
+           <th>Status</th>
+           <th>Begin Time</th>
+           <th>End Time</th>
+           <th>Elapsed Time (s)</th>
+           <th>Requires Lock</th>
+           <th>Retry If Fail</th>
+        </tr>
+
+       <%if sod.getQueryDisplay() != null && 
sod.getQueryDisplay().getTaskInfos() != null %>
+           <%for QueryDisplay.TaskInfo taskInfo : 
sod.getQueryDisplay().getTaskInfos() %>
+               <tr>
+                   <td><% taskInfo.getTaskId() + ":" + taskInfo.getTaskType() 
%></td>
+                   <td><% taskInfo.getStatus() %></td>
+                   <td><% new Date(taskInfo.getBeginTime()) %>
+                   <td><% taskInfo.getEndTime() == 0 ? "" : new 
Date(taskInfo.getEndTime()) %></td>
+                   <td><% taskInfo.getElapsedTime()/1000 %> (s) </td>
+                   <td><% taskInfo.isRequireLock() %></td>
+                   <td><% taskInfo.isRetryIfFail() %></td>
+               </tr>
+           </%for>
+       </%if>
+   </table>
+</%def>
+
+
+<%def queryPlan>
+<%args>
+    SQLOperationDisplay sod;
+</%args>
+    <div class="panel panel-default">
+      <div class="panel-heading">Explain plan</div>
+      <div class="panel-body">
+        <pre>
+        <% sod.getQueryDisplay() == null ? "Unknown" : 
sod.getQueryDisplay().getExplainPlan() %>
+        </pre>
+      </div>
+    </div>
+</%def>
+
+
+<%def perfLogging>
+<%args>
+    SQLOperationDisplay sod;
+</%args>
+    <section>
+      <h3>Compile-time metadata operations</h3>
+        <table id="attributes_table" class="table table-striped">
+          <tr>
+             <th>Call Name</th>
+             <th>Time (ms)</th>
+          </tr>
+
+          <%if sod.getQueryDisplay() != null && 
sod.getQueryDisplay().getHmsTimings(QueryDisplay.Phase.COMPILATION) != null %>
+             <%for Map.Entry<String, Long> time : 
sod.getQueryDisplay().getHmsTimings(QueryDisplay.Phase.COMPILATION).entrySet() 
%>
+                 <tr>
+                     <td><% time.getKey() %></td>
+                     <td><% time.getValue() %></td>
+                 </tr>
+             </%for>
+         </%if>
+        </table>
+      </section>
+
+    <section>
+      <h3>Execution-time metadata operations</h3>
+        <table id="attributes_table" class="table table-striped">
+          <tr>
+             <th>Call Name</th>
+             <th>Time (ms)</th>
+          </tr>
+
+          <%if sod.getQueryDisplay() != null && 
sod.getQueryDisplay().getHmsTimings(QueryDisplay.Phase.EXECUTION) != null %>
+             <%for Map.Entry<String, Long> time : 
sod.getQueryDisplay().getHmsTimings(QueryDisplay.Phase.EXECUTION).entrySet() %>
+                 <tr>
+                     <td><% time.getKey() %></td>
+                     <td><% time.getValue() %></td>
+                 </tr>
+             </%for>
+         </%if>
+        </table>
+      </section>
+
+    <section>
+      <h3>Compile-Time Perf-Logger</h3>
+        <table id="attributes_table" class="table table-striped">
+          <tr>
+             <th>Compile-time Call Name</th>
+             <th>Time (ms)</th>
+          </tr>
+
+          <%if sod.getQueryDisplay() != null && 
sod.getQueryDisplay().getPerfLogTimes(QueryDisplay.Phase.COMPILATION) != null %>
+             <%for Map.Entry<String, Long> time : 
sod.getQueryDisplay().getPerfLogTimes(QueryDisplay.Phase.COMPILATION).entrySet()
  %>
+                 <tr>
+                     <td><% time.getKey() %></td>
+                     <td><% time.getValue() %></td>
+                 </tr>
+             </%for>
+         </%if>
+        </table>
+      </section>
+
+    <section>
+      <h3>Execution-Time Perf-Logger</h3>
+        <table id="attributes_table" class="table table-striped">
+          <tr>
+             <th>Execution-time Call Name</th>
+             <th>Time (ms)</th>
+          </tr>
+
+          <%if sod.getQueryDisplay() != null && 
sod.getQueryDisplay().getPerfLogTimes(QueryDisplay.Phase.EXECUTION) != null %>
+             <%for Map.Entry<String, Long> time : 
sod.getQueryDisplay().getPerfLogTimes(QueryDisplay.Phase.EXECUTION).entrySet()  
%>
+                 <tr>
+                     <td><% time.getKey() %></td>
+                     <td><% time.getValue() %></td>
+                 </tr>
+             </%for>
+         </%if>
+        </table>
+      </section>
+</%def>
+
+</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>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/service/src/java/org/apache/hive/service/cli/operation/Operation.java
----------------------------------------------------------------------
diff --git 
a/service/src/java/org/apache/hive/service/cli/operation/Operation.java 
b/service/src/java/org/apache/hive/service/cli/operation/Operation.java
index 0c263cf..8340202 100644
--- a/service/src/java/org/apache/hive/service/cli/operation/Operation.java
+++ b/service/src/java/org/apache/hive/service/cli/operation/Operation.java
@@ -157,6 +157,7 @@ public abstract class Operation {
     state.validateTransition(newState);
     this.state = newState;
     setMetrics(state);
+    onNewState(state);
     this.lastAccessTime = System.currentTimeMillis();
     return this.state;
   }
@@ -420,4 +421,7 @@ public abstract class Operation {
   protected OperationState getState() {
     return state;
   }
+
+  protected void onNewState(OperationState state) {
+  }
 }

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/service/src/java/org/apache/hive/service/cli/operation/OperationManager.java
----------------------------------------------------------------------
diff --git 
a/service/src/java/org/apache/hive/service/cli/operation/OperationManager.java 
b/service/src/java/org/apache/hive/service/cli/operation/OperationManager.java
index f1ce6f6..38f27ef 100644
--- 
a/service/src/java/org/apache/hive/service/cli/operation/OperationManager.java
+++ 
b/service/src/java/org/apache/hive/service/cli/operation/OperationManager.java
@@ -22,7 +22,9 @@ import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -65,8 +67,10 @@ public class OperationManager extends AbstractService {
   private final ConcurrentHashMap<OperationHandle, Operation> 
handleToOperation =
       new ConcurrentHashMap<OperationHandle, Operation>();
 
-  //for displaying historical queries on WebUI
-  private Queue<SQLOperationInfo> historicSqlOperations;
+  //Following fields for displaying queries on WebUI
+  private Object webuiLock = new Object();
+  private SQLOperationDisplayCache historicSqlOperations;
+  private Map<String, SQLOperationDisplay> liveSqlOperations = new 
LinkedHashMap<String, SQLOperationDisplay>();
 
   public OperationManager() {
     super(OperationManager.class.getSimpleName());
@@ -80,9 +84,8 @@ public class OperationManager extends AbstractService {
     } else {
       LOG.debug("Operation level logging is turned off");
     }
-    if ((hiveConf.getIntVar(HiveConf.ConfVars.HIVE_SERVER2_WEBUI_PORT) != 0) &&
-      
hiveConf.getIntVar(HiveConf.ConfVars.HIVE_SERVER2_WEBUI_MAX_HISTORIC_QUERIES) > 
0) {
-      historicSqlOperations = EvictingQueue.create(
+    if (hiveConf.isWebUiQueryInfoCacheEnabled()) {
+      historicSqlOperations = new SQLOperationDisplayCache(
         hiveConf.getIntVar(ConfVars.HIVE_SERVER2_WEBUI_MAX_HISTORIC_QUERIES));
     }
     super.init(hiveConf);
@@ -186,7 +189,11 @@ public class OperationManager extends AbstractService {
     Operation operation = handleToOperation.get(operationHandle);
     if (operation != null && operation.isTimedOut(System.currentTimeMillis())) 
{
       handleToOperation.remove(operationHandle, operation);
-      cacheOldOperationInfo(operation);
+      synchronized (webuiLock) {
+        String opKey = operationHandle.getHandleIdentifier().toString();
+        SQLOperationDisplay display = liveSqlOperations.remove(opKey);
+        historicSqlOperations.put(opKey, display);
+      }
       return operation;
     }
     return null;
@@ -194,11 +201,21 @@ public class OperationManager extends AbstractService {
 
   private void addOperation(Operation operation) {
     handleToOperation.put(operation.getHandle(), operation);
+    if (operation instanceof SQLOperation) {
+      synchronized (webuiLock) {
+        
liveSqlOperations.put(operation.getHandle().getHandleIdentifier().toString(),
+          ((SQLOperation) operation).getSQLOperationDisplay());
+      }
+    }
   }
 
   private Operation removeOperation(OperationHandle opHandle) {
     Operation result = handleToOperation.remove(opHandle);
-    cacheOldOperationInfo(result);
+    synchronized (webuiLock) {
+      String opKey = opHandle.getHandleIdentifier().toString();
+      SQLOperationDisplay display = liveSqlOperations.remove(opKey);
+      historicSqlOperations.put(opKey, display);
+    }
     return result;
   }
 
@@ -329,35 +346,40 @@ public class OperationManager extends AbstractService {
     return removed;
   }
 
-  //Cache a number of historical operation info, at max number of
-  //hive.server2.webui.max.historic.queries.
-  private void cacheOldOperationInfo(Operation oldOperation) {
-    if ((getHiveConf().getIntVar(HiveConf.ConfVars.HIVE_SERVER2_WEBUI_PORT) != 
0) &&
-      
getHiveConf().getIntVar(HiveConf.ConfVars.HIVE_SERVER2_WEBUI_MAX_HISTORIC_QUERIES)
 > 0) {
-      if (oldOperation instanceof SQLOperation) {
-        SQLOperation query = (SQLOperation) oldOperation;
-        SQLOperationInfo queryInfo = query.getSQLOperationInfo();
-        if (queryInfo != null) {
-          synchronized (historicSqlOperations) {
-            historicSqlOperations.add(queryInfo);
-          }
-        }
-      }
+  /**
+   * @return displays representing a number of historical SQLOperations, at 
max number of
+   * hive.server2.webui.max.historic.queries
+   */
+  public List<SQLOperationDisplay> getHistoricalSQLOperations() {
+    List<SQLOperationDisplay> result = new LinkedList<>();
+    synchronized (webuiLock) {
+      result.addAll(historicSqlOperations.values());
     }
+    return result;
   }
 
   /**
-   * @return a number of historical SQLOperation info, at max number of
-   * hive.server2.webui.max.historic.queries
+   * @return displays representing live SQLOperations
    */
-  public List<SQLOperationInfo> getHistoricalSQLOpInfo() {
-    List<SQLOperationInfo> result = new LinkedList<>();
-    synchronized (historicSqlOperations) {
-      Iterator<SQLOperationInfo> opIterator = historicSqlOperations.iterator();
-      while (opIterator.hasNext()) {
-        result.add(opIterator.next());
-      }
+  public List<SQLOperationDisplay> getLiveSqlOperations() {
+    List<SQLOperationDisplay> result = new LinkedList<>();
+    synchronized (webuiLock) {
+      result.addAll(liveSqlOperations.values());
     }
     return result;
   }
+
+  /**
+   * @param handle handle of SQLOperation.
+   * @return display representing a particular SQLOperation.
+   */
+  public SQLOperationDisplay getSQLOperationDisplay(String handle) {
+    synchronized (webuiLock) {
+      SQLOperationDisplay result = liveSqlOperations.get(handle);
+      if (result != null) {
+        return result;
+      }
+      return historicSqlOperations.get(handle);
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java
----------------------------------------------------------------------
diff --git 
a/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java 
b/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java
index 01b1d3d..3fbbb70 100644
--- a/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java
+++ b/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java
@@ -34,8 +34,6 @@ import java.util.concurrent.RejectedExecutionException;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.lang3.CharEncoding;
 import org.apache.hadoop.hive.conf.HiveConf;
-import org.apache.hadoop.hive.conf.HiveVariableSource;
-import org.apache.hadoop.hive.conf.VariableSubstitution;
 import org.apache.hadoop.hive.metastore.api.FieldSchema;
 import org.apache.hadoop.hive.metastore.api.Schema;
 import org.apache.hadoop.hive.ql.CommandNeedRetryException;
@@ -79,11 +77,20 @@ public class SQLOperation extends ExecuteStatementOperation 
{
   private SerDe serde = null;
   private boolean fetchStarted = false;
 
+  //Display for WebUI.
+  private SQLOperationDisplay sqlOpDisplay;
+
+
   public SQLOperation(HiveSession parentSession, String statement, Map<String,
       String> confOverlay, boolean runInBackground) {
     // TODO: call setRemoteUser in ExecuteStatementOperation or higher.
     super(parentSession, statement, confOverlay, runInBackground);
     setupSessionIO(parentSession.getSessionState());
+    try {
+      sqlOpDisplay = new SQLOperationDisplay(this);
+    } catch (HiveSQLException e) {
+      LOG.warn("Error calcluating SQL Operation Display for webui", e);
+    }
   }
 
   private void setupSessionIO(SessionState sessionState) {
@@ -111,6 +118,7 @@ public class SQLOperation extends ExecuteStatementOperation 
{
 
     try {
       driver = new Driver(sqlOperationConf, getParentSession().getUserName());
+      sqlOpDisplay.setQueryDisplay(driver.getQueryDisplay());
 
       // set the operation handle information in Driver, so that thrift API 
users
       // can use the operation handle they receive, to lookup query 
information in
@@ -162,10 +170,6 @@ public class SQLOperation extends 
ExecuteStatementOperation {
     }
   }
 
-  public String getQueryStr() {
-    return driver == null ? "Unknown" : driver.getQueryString();
-  }
-
   private void runQuery(HiveConf sqlOperationConf) throws HiveSQLException {
     try {
       // In Hive server mode, we are not able to retry in the FetchTask
@@ -485,18 +489,17 @@ public class SQLOperation extends 
ExecuteStatementOperation {
   /**
    * Get summary information of this SQLOperation for display in WebUI.
    */
-  public SQLOperationInfo getSQLOperationInfo() {
-    try {
-      return new SQLOperationInfo(
-        getParentSession().getUserName(),
-        driver.getQueryString(),
-        
getConfigForOperation().getVar(HiveConf.ConfVars.HIVE_EXECUTION_ENGINE),
-        getState(),
-        (int) (System.currentTimeMillis() - getBeginTime()) / 1000,
-        System.currentTimeMillis());
-    } catch (HiveSQLException e) {
-      LOG.warn("Error calcluating SQL Operation Info for webui", e);
+  public SQLOperationDisplay getSQLOperationDisplay() {
+    return sqlOpDisplay;
+  }
+
+  @Override
+  protected void onNewState(OperationState state) {
+    if (state == OperationState.CLOSED) {
+      sqlOpDisplay.closed();
+    } else {
+      //CLOSED state not interesting, state before (FINISHED, ERROR) is.
+      sqlOpDisplay.updateState(state);
     }
-    return null;
   }
 }

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/service/src/java/org/apache/hive/service/cli/operation/SQLOperationDisplay.java
----------------------------------------------------------------------
diff --git 
a/service/src/java/org/apache/hive/service/cli/operation/SQLOperationDisplay.java
 
b/service/src/java/org/apache/hive/service/cli/operation/SQLOperationDisplay.java
new file mode 100644
index 0000000..d2ca1e7
--- /dev/null
+++ 
b/service/src/java/org/apache/hive/service/cli/operation/SQLOperationDisplay.java
@@ -0,0 +1,99 @@
+/**
+ * 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.service.cli.operation;
+
+import org.apache.hadoop.hive.conf.HiveConf;
+import org.apache.hadoop.hive.ql.QueryDisplay;
+import org.apache.hive.service.cli.HiveSQLException;
+import org.apache.hive.service.cli.OperationState;
+
+/**
+ * Used to display some info in the HS2 WebUI.
+ *
+ * The class is synchronized, as WebUI may access information about a running 
query.
+ */
+public class SQLOperationDisplay {
+  public final String userName;
+  public final String executionEngine;
+  public final long beginTime;
+  public final String operationId;
+
+  public Long endTime;
+  public OperationState state;
+  public QueryDisplay queryDisplay;
+
+  public SQLOperationDisplay(SQLOperation sqlOperation) throws 
HiveSQLException {
+    this.state = sqlOperation.getState();
+    this.userName = sqlOperation.getParentSession().getUserName();
+    this.executionEngine = 
sqlOperation.getConfigForOperation().getVar(HiveConf.ConfVars.HIVE_EXECUTION_ENGINE);
+    this.beginTime = System.currentTimeMillis();
+    this.operationId = 
sqlOperation.getHandle().getHandleIdentifier().toString();
+  }
+
+  public synchronized long getElapsedTime() {
+    if (isRunning()) {
+      return System.currentTimeMillis() - beginTime;
+    } else {
+      return endTime - beginTime;
+    }
+  }
+
+  public synchronized boolean isRunning() {
+    return endTime == null;
+  }
+
+  public synchronized QueryDisplay getQueryDisplay() {
+    return queryDisplay;
+  }
+
+  public synchronized void setQueryDisplay(QueryDisplay queryDisplay) {
+    this.queryDisplay = queryDisplay;
+  }
+
+  public String getUserName() {
+    return userName;
+  }
+
+  public String getExecutionEngine() {
+    return executionEngine;
+  }
+
+  public synchronized OperationState getState() {
+    return state;
+  }
+
+  public long getBeginTime() {
+    return beginTime;
+  }
+
+  public synchronized Long getEndTime() {
+    return endTime;
+  }
+
+  public synchronized void updateState(OperationState state) {
+    this.state = state;
+  }
+
+  public String getOperationId() {
+    return operationId;
+  }
+
+  public synchronized void closed() {
+    this.endTime = System.currentTimeMillis();
+  }
+}

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/service/src/java/org/apache/hive/service/cli/operation/SQLOperationDisplayCache.java
----------------------------------------------------------------------
diff --git 
a/service/src/java/org/apache/hive/service/cli/operation/SQLOperationDisplayCache.java
 
b/service/src/java/org/apache/hive/service/cli/operation/SQLOperationDisplayCache.java
new file mode 100644
index 0000000..4a33d37
--- /dev/null
+++ 
b/service/src/java/org/apache/hive/service/cli/operation/SQLOperationDisplayCache.java
@@ -0,0 +1,39 @@
+/**
+ * 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.service.cli.operation;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Cache some SQLOperation information for WebUI
+ */
+public class SQLOperationDisplayCache extends LinkedHashMap<String, 
SQLOperationDisplay> {
+
+  private final int capacity;
+
+  public SQLOperationDisplayCache(int capacity) {
+      super(capacity + 1, 1.1f, false);
+      this.capacity = capacity;
+  }
+
+  @Override
+  protected boolean removeEldestEntry(Map.Entry eldest) {
+    return size() > capacity;
+  }
+}

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/service/src/java/org/apache/hive/service/cli/operation/SQLOperationInfo.java
----------------------------------------------------------------------
diff --git 
a/service/src/java/org/apache/hive/service/cli/operation/SQLOperationInfo.java 
b/service/src/java/org/apache/hive/service/cli/operation/SQLOperationInfo.java
deleted file mode 100644
index 179f6dd..0000000
--- 
a/service/src/java/org/apache/hive/service/cli/operation/SQLOperationInfo.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * 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.service.cli.operation;
-
-import org.apache.hive.service.cli.OperationState;
-
-/**
- * Used to display some info in the HS2 WebUI.
- */
-public class SQLOperationInfo {
-  public String userName;
-  public String queryStr;
-  public String executionEngine;
-  public OperationState endState; //state before CLOSED (one of CANCELLED, 
FINISHED, ERROR)
-  public int elapsedTime;
-  public long endTime;
-
-  public SQLOperationInfo(
-    String userName,
-    String queryStr,
-    String executionEngine,
-    OperationState endState,
-    int elapsedTime,
-    long endTime
-  ) {
-    this.userName = userName;
-    this.queryStr = queryStr;
-    this.executionEngine = executionEngine;
-    this.endState = endState;
-    this.elapsedTime = elapsedTime;
-    this.endTime = endTime;
-  }
-}

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/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 958458f..c601614 100644
--- a/service/src/java/org/apache/hive/service/server/HiveServer2.java
+++ b/service/src/java/org/apache/hive/service/server/HiveServer2.java
@@ -64,6 +64,7 @@ import org.apache.hive.service.cli.CLIService;
 import org.apache.hive.service.cli.thrift.ThriftBinaryCLIService;
 import org.apache.hive.service.cli.thrift.ThriftCLIService;
 import org.apache.hive.service.cli.thrift.ThriftHttpCLIService;
+import org.apache.hive.service.servlet.QueryProfileServlet;
 import org.apache.logging.log4j.util.Strings;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
@@ -180,6 +181,7 @@ public class HiveServer2 extends CompositeService {
             builder.setUseSPNEGO(true);
           }
           webServer = builder.build();
+          webServer.addServlet("query_page", "/query_page", 
QueryProfileServlet.class);
         }
       }
     } catch (IOException ie) {

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/service/src/java/org/apache/hive/service/servlet/QueryProfileServlet.java
----------------------------------------------------------------------
diff --git 
a/service/src/java/org/apache/hive/service/servlet/QueryProfileServlet.java 
b/service/src/java/org/apache/hive/service/servlet/QueryProfileServlet.java
new file mode 100644
index 0000000..74a374d
--- /dev/null
+++ b/service/src/java/org/apache/hive/service/servlet/QueryProfileServlet.java
@@ -0,0 +1,49 @@
+/**
+ * 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.service.servlet;
+
+import org.apache.hive.service.cli.operation.OperationManager;
+import org.apache.hive.service.cli.operation.SQLOperationDisplay;
+import org.apache.hive.service.cli.session.SessionManager;
+import org.apache.hive.tmpl.QueryProfileTmpl;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Renders a query page
+ */
+public class QueryProfileServlet extends HttpServlet {
+  @Override
+  public void doGet(HttpServletRequest request, HttpServletResponse response)
+    throws ServletException, IOException {
+    String opId = (String) request.getParameter("operationId");
+    ServletContext ctx = getServletContext();
+    SessionManager sessionManager =
+      (SessionManager)ctx.getAttribute("hive.sm");
+    OperationManager opManager = sessionManager.getOperationManager();
+    SQLOperationDisplay sod = opManager.getSQLOperationDisplay(opId);
+
+    new QueryProfileTmpl().render(response.getWriter(), sod);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/hive/blob/2d94c0b0/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 a0b5d2e..8b46550 100644
--- a/service/src/resources/hive-webapps/hiveserver2/hiveserver2.jsp
+++ b/service/src/resources/hive-webapps/hiveserver2/hiveserver2.jsp
@@ -24,7 +24,7 @@
   import="org.apache.hive.common.util.HiveVersionInfo"
   import="org.apache.hive.service.cli.operation.Operation"
   import="org.apache.hive.service.cli.operation.SQLOperation"
-  import="org.apache.hive.service.cli.operation.SQLOperationInfo"
+  import="org.apache.hive.service.cli.operation.SQLOperationDisplay"
   import="org.apache.hive.service.cli.session.SessionManager"
   import="org.apache.hive.service.cli.session.HiveSession"
   import="javax.servlet.ServletContext"
@@ -132,29 +132,32 @@ for (HiveSession hiveSession: hiveSessions) {
         <th>Query</th>
         <th>Execution Engine</th>
         <th>State</th>
+        <th>Begin Time</th>
         <th>Elapsed Time (s)</th>
+        <th>Drilldown Link</th>
     </tr>
-<%
-int queries = 0;
-Collection<Operation> operations = sessionManager.getOperations();
-for (Operation operation: operations) {
-  if (operation instanceof SQLOperation) {
-    SQLOperation query = (SQLOperation) operation;
-    queries++;
-%>
+    <%
+      int queries = 0;
+      Collection<SQLOperationDisplay> operations = 
sessionManager.getOperationManager().getLiveSqlOperations();
+      for (SQLOperationDisplay operation : operations) {
+          queries++;
+    %>
     <tr>
-        <td><%= query.getParentSession().getUserName() %></td>
-        <td><%= query.getQueryStr() %></td>
-        <td><%= 
query.getConfigForOperation().getVar(ConfVars.HIVE_EXECUTION_ENGINE) %>
-        <td><%= query.getStatus().getState() %></td>
-        <td><%= (currentTime - query.getBeginTime())/1000 %></td>
+        <td><%= operation.getUserName() %></td>
+        <td><%= operation.getQueryDisplay() == null ? "Unknown" : 
operation.getQueryDisplay().getQueryString() %></td>
+        <td><%= operation.getExecutionEngine() %>
+        <td><%= operation.getState() %></td>
+        <td><%= new Date(operation.getBeginTime()) %></td>
+        <td><%= operation.getElapsedTime()/1000 %></td>
+        <% String link = "/query_page?operationId=" + 
operation.getOperationId(); %>
+        <td>  <a href= <%= link %>>Query Drilldown</a> </td>
     </tr>
+
 <%
   }
-}
 %>
 <tr>
-  <td colspan="5">Total number of queries: <%= queries %></td>
+  <td colspan="7">Total number of queries: <%= queries %></td>
 </tr>
 </table>
 </section>
@@ -169,33 +172,36 @@ for (Operation operation: operations) {
         <th>Execution Engine</th>
         <th>State</th>
         <th>Elapsed Time (s)</th>
-        <th>End Time </th>
+        <th>End Time</th>
+        <th>Drilldown Link</th>
     </tr>
-<%
-queries = 0;
-List<SQLOperationInfo> sqlOperations = 
sessionManager.getOperationManager().getHistoricalSQLOpInfo();
-for (SQLOperationInfo sqlOperation: sqlOperations) {
-  queries++;
-%>
+    <%
+      queries = 0;
+      operations = 
sessionManager.getOperationManager().getHistoricalSQLOperations();
+      for (SQLOperationDisplay operation : operations) {
+          queries++;
+    %>
     <tr>
-        <td><%= sqlOperation.userName %></td>
-        <td><%= sqlOperation.queryStr %></td>
-        <td><%= sqlOperation.executionEngine %></td>
-        <td><%= sqlOperation.endState %></td>
-        <td><%= sqlOperation.elapsedTime %></td>
-        <td><%= new Date(sqlOperation.endTime) %></td>
+        <td><%= operation.getUserName() %></td>
+        <td><%= operation.getQueryDisplay() == null ? "Unknown" : 
operation.getQueryDisplay().getQueryString() %></td>
+        <td><%= operation.getExecutionEngine() %>
+        <td><%= operation.getState() %></td>
+        <td><%= operation.getElapsedTime()/1000 %></td>
+        <td><%= operation.getEndTime() == null ? "In Progress" : new 
Date(operation.getEndTime()) %></td>
+        <% String link = "/query_page?operationId=" + 
operation.getOperationId(); %>
+        <td>  <a href= <%= link %>>Query Drilldown</a> </td>
     </tr>
-<%
-}
 
+<%
+  }
 %>
 <tr>
-  <td colspan="6">Total number of queries: <%= queries %></td>
+  <td colspan="8">Total number of queries: <%= queries %></td>
 </tr>
 </table>
 </section>
 
-<% 
+<%
  }
 %>
 

Reply via email to