All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hadoop.hive.hwi.HWISessionItem Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
/**
 * 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.hwi;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.sql.SQLException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hive.cli.CliSessionState;
import org.apache.hadoop.hive.cli.OptionsProcessor;
import org.apache.hadoop.hive.common.LogUtils;
import org.apache.hadoop.hive.common.LogUtils.LogInitializationException;
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.ql.CommandNeedRetryException;
import org.apache.hadoop.hive.ql.Driver;
import org.apache.hadoop.hive.ql.history.HiveHistoryViewer;
import org.apache.hadoop.hive.ql.processors.CommandProcessor;
import org.apache.hadoop.hive.ql.processors.CommandProcessorFactory;
import org.apache.hadoop.hive.ql.session.SessionState;

/**
 * HWISessionItem can be viewed as a wrapper for a Hive shell. With it the user
 * has a session on the web server rather then in a console window.
 *
 */
public class HWISessionItem implements Runnable, Comparable {

  protected static final Log l4j = LogFactory.getLog(HWISessionItem.class
      .getName());

  /** Represents the state a session item can be in. */
  public enum WebSessionItemStatus {
    NEW, READY, QUERY_SET, QUERY_RUNNING, DESTROY, KILL_QUERY
  };

  /** The Web Interface sessionName this is used to identify the session. */
  private final String sessionName;

  /**
   * Respresents the current status of the session. Used by components to
   * determine state. Operations will throw exceptions if the item is not in the
   * correct state.
   */
  private HWISessionItem.WebSessionItemStatus status;

  private CliSessionState ss;

  /** Standard out from the session will be written to this local file. */
  private String resultFile;

  /** Standard error from the session will be written to this local file. */
  private String errorFile;

  /**
   * The results from the Driver. This is used for storing the most result
   * results from the driver in memory.
   */
  private ArrayList> resultBucket;

  /** Limits the resultBucket to be no greater then this size. */
  private int resultBucketMaxSize;

  /** List of queries that this item should/has operated on. */
  private List queries;

  /** status code results of queries. */
  private List queryRet;

  /** Reference to the configuration. */
  private HiveConf conf;

  /** User privileges. */
  private HWIAuth auth;

  public Thread runnable;

  /**
   * Threading SessionState issues require us to capture a reference to the hive
   * history file and store it.
   */
  private String historyFile;

  /**
   * Creates an instance of WebSessionItem, sets status to NEW.
   */
  public HWISessionItem(HWIAuth auth, String sessionName) {
    this.auth = auth;
    this.sessionName = sessionName;
    l4j.debug("HWISessionItem created");
    status = WebSessionItemStatus.NEW;
    queries = new ArrayList();
    queryRet = new ArrayList();
    resultBucket = new ArrayList>();
    resultBucketMaxSize = 1000;
    runnable = new Thread(this);
    runnable.start();

    l4j.debug("Wait for NEW->READY transition");
    synchronized (runnable) {
      if (status != WebSessionItemStatus.READY) {
        try {
          runnable.wait();
        } catch (Exception ex) {
        }
      }
    }
    l4j.debug("NEW->READY transition complete");
  }

  /**
   * This is the initialization process that is carried out for each
   * SessionItem. The goal is to emulate the startup of CLIDriver.
   */
  private void itemInit() {
    l4j.debug("HWISessionItem itemInit start " + getSessionName());
    OptionsProcessor oproc = new OptionsProcessor();

    if (System.getProperty("hwi-args") != null) {
      String[] parts = System.getProperty("hwi-args").split("\\s+");
      if (!oproc.process_stage1(parts)) {
      }
    }

    try {
      LogUtils.initHiveLog4j();
    } catch (LogInitializationException e) {
      l4j.warn(e);
    }
    conf = new HiveConf(SessionState.class);
    ss = new CliSessionState(conf);
    SessionState.start(ss);
    queries.add("set hadoop.job.ugi=" + auth.getUser() + ","
        + auth.getGroups()[0]);
    queries.add("set user.name=" + auth.getUser());
    /*
     * HiveHistoryFileName will not be accessible outside this thread. We must
     * capture this now.
     */
    historyFile = SessionState.get().getHiveHistory().getHistFileName();
    l4j.debug("HWISessionItem itemInit Complete " + getSessionName());
    status = WebSessionItemStatus.READY;

    synchronized (runnable) {
      runnable.notifyAll();
    }
  }

  /**
   * HWISessionItem is a Runnable instance. Calling this method will change the
   * status to QUERY_SET and notify(). The run method detects this and then
   * continues processing.
   */
  public void clientStart() throws HWIException {
    if (status == WebSessionItemStatus.QUERY_RUNNING) {
      throw new HWIException("Query already running");
    }
    status = WebSessionItemStatus.QUERY_SET;
    synchronized (runnable) {
      runnable.notifyAll();
    }
    l4j.debug(getSessionName() + " Query is set to start");
  }

  public void clientKill() throws HWIException {
    if (status != WebSessionItemStatus.QUERY_RUNNING) {
      throw new HWIException("Can not kill that which is not running.");
    }
    status = WebSessionItemStatus.KILL_QUERY;
    l4j.debug(getSessionName() + " Query is set to KILL_QUERY");
  }

  /** This method clears the private member variables. */
  public void clientRenew() throws HWIException {
    throwIfRunning();
    queries = new ArrayList();
    queryRet = new ArrayList();
    resultBucket = new ArrayList>();
    resultFile = null;
    errorFile = null;
    // this.conf = null;
    // this.ss = null;
    status = WebSessionItemStatus.NEW;
    l4j.debug(getSessionName() + " Query is renewed to start");
  }

  /**
   * This is a callback style function used by the HiveSessionManager. The
   * HiveSessionManager notices this and attempts to stop the query.
   */
  protected void killIt() {
    l4j.debug(getSessionName() + " Attempting kill.");
    if (runnable != null) {
      try {
        runnable.join(1000);
        l4j.debug(getSessionName() + " Thread join complete");
      } catch (InterruptedException e) {
        l4j.error(getSessionName() + " killing session caused exception ", e);
      }
    }
  }

  /**
   * Helper function to get configuration variables.
   *
   * @param wanted
   *          a ConfVar
   * @return Value of the configuration variable.
   */
  public String getHiveConfVar(HiveConf.ConfVars wanted) throws HWIException {
    String result = null;
    try {
      result = ss.getConf().getVar(wanted);
    } catch (Exception ex) {
      throw new HWIException(ex);
    }
    return result;
  }

  public String getHiveConfVar(String s) throws HWIException {
    String result = null;
    try {
      result = conf.get(s);
    } catch (Exception ex) {
      throw new HWIException(ex);
    }
    return result;
  }

  /*
   * mapred.job.tracker could be host:port or just local
   * mapred.job.tracker.http.address could be host:port or just host In some
   * configurations http.address is set to 0.0.0.0 we are combining the two
   * variables to provide a url to the job tracker WUI if it exists. If hadoop
   * chose the first available port for the JobTracker HTTP port will can not
   * determine it.
   */
  public String getJobTrackerURL(String jobid) throws HWIException {
    String jt = this.getHiveConfVar("mapred.job.tracker");
    String jth = this.getHiveConfVar("mapred.job.tracker.http.address");
    String[] jtparts = null;
    String[] jthttpParts = null;
    if (jt.equalsIgnoreCase("local")) {
      jtparts = new String[2];
      jtparts[0] = "local";
      jtparts[1] = "";
    } else {
      jtparts = jt.split(":");
    }
    if (jth.contains(":")) {
      jthttpParts = jth.split(":");
    } else {
      jthttpParts = new String[2];
      jthttpParts[0] = jth;
      jthttpParts[1] = "";
    }
    return jtparts[0] + ":" + jthttpParts[1] + "/jobdetails.jsp?jobid=" + jobid
        + "&refresh=30";
  }

  @Override
  /*
   * HWISessionItem uses a wait() notify() system. If the thread detects conf to
   * be null, control is transfered to initItem(). A status of QUERY_SET causes
   * control to transfer to the runQuery() method. DESTROY will cause the run
   * loop to end permanently.
   */
  public void run() {
    synchronized (runnable) {
      while (status != HWISessionItem.WebSessionItemStatus.DESTROY) {
        if (status == WebSessionItemStatus.NEW) {
          itemInit();
        }

        if (status == WebSessionItemStatus.QUERY_SET) {
          runQuery();
        }

        try {
          runnable.wait();
        } catch (InterruptedException e) {
          l4j.error("in wait() state ", e);
        }
      } // end while
    } // end sync
  } // end run

  /**
   * runQuery iterates the list of queries executing each query.
   */
  public void runQuery() {
    FileOutputStream fos = null;
    if (getResultFile() != null) {
      try {
        fos = new FileOutputStream(new File(resultFile));
        ss.out = new PrintStream(fos, true, "UTF-8");
      } catch (java.io.FileNotFoundException fex) {
        l4j.error(getSessionName() + " opening resultfile " + resultFile, fex);
      } catch (java.io.UnsupportedEncodingException uex) {
        l4j.error(getSessionName() + " opening resultfile " + resultFile, uex);
      }
    } else {
      l4j.debug(getSessionName() + " Output file was not specified");
    }
    l4j.debug(getSessionName() + " state is now QUERY_RUNNING.");
    status = WebSessionItemStatus.QUERY_RUNNING;

    // expect one return per query
    queryRet = new ArrayList(queries.size());
    for (int i = 0; i < queries.size(); i++) {
      String cmd = queries.get(i);
      String cmd_trimmed = cmd.trim();
      String[] tokens = cmd_trimmed.split("\\s+");
      String cmd_1 = cmd_trimmed.substring(tokens[0].length()).trim();
      CommandProcessor proc = null;
      try {
        proc = CommandProcessorFactory.get(tokens[0]);
      } catch (SQLException e) {
        l4j.error(getSessionName() + " error processing " + cmd, e);
      }
      if (proc != null) {
        if (proc instanceof Driver) {
          Driver qp = (Driver) proc;
          qp.setTryCount(Integer.MAX_VALUE);
          try {
          queryRet.add(Integer.valueOf(qp.run(cmd).getResponseCode()));
          ArrayList res = new ArrayList();
          try {
            while (qp.getResults(res)) {
              ArrayList resCopy = new ArrayList();
              resCopy.addAll(res);
              resultBucket.add(resCopy);
              if (resultBucket.size() > resultBucketMaxSize) {
                resultBucket.remove(0);
              }
              for (String row : res) {
                if (ss != null) {
                  if (ss.out != null) {
                    ss.out.println(row);
                  }
                } else {
                  throw new RuntimeException("ss was null");
                }
              }
              res.clear();
            }

          } catch (IOException ex) {
            l4j.error(getSessionName() + " getting results " + getResultFile()
                + " caused exception.", ex);
          }
          } catch (CommandNeedRetryException e) {
            // this should never happen since we Driver.setTryCount(Integer.MAX_VALUE)
            l4j.error(getSessionName() + " Exception when executing", e);
          } finally {
            qp.close();
          }
        } else {
          try {
            queryRet.add(Integer.valueOf(proc.run(cmd_1).getResponseCode()));
          } catch (CommandNeedRetryException e) {
            // this should never happen if there is no bug
            l4j.error(getSessionName() + " Exception when executing", e);
          }
        }
      } else {
        // processor was null
        l4j.error(getSessionName()
            + " query processor was not found for query " + cmd);
      }
    } // end for

    // cleanup
    try {
      if (fos != null) {
        fos.close();
      }
    } catch (IOException ex) {
      l4j.error(getSessionName() + " closing result file " + getResultFile()
          + " caused exception.", ex);
    }
    status = WebSessionItemStatus.READY;
    l4j.debug(getSessionName() + " state is now READY");
    synchronized (runnable) {
      runnable.notifyAll();
    }
  }

  /**
   * This is a chained call to SessionState.setIsSilent(). Use this if you do
   * not want the result file to have information status
   */
  public void setSSIsSilent(boolean silent) throws HWIException {
    if (ss == null) {
      throw new HWIException("Session State is null");
    }
    ss.setIsSilent(silent);
  }

  /**
   * This is a chained call to SessionState.getIsSilent().
   */
  public boolean getSSIsSilent() throws HWIException {
    if (ss == null) {
      throw new HWIException("Session State is null");
    }
    return ss.getIsSilent();
  }

  /** to support sorting/Set. */
  public int compareTo(HWISessionItem other) {
    if (other == null) {
      return -1;
    }
    return getSessionName().compareTo(other.getSessionName());
  }

  /**
   *
   * @return the HiveHistoryViewer for the session
   * @throws HWIException
   */
  public HiveHistoryViewer getHistoryViewer() throws HWIException {
    if (ss == null) {
      throw new HWIException("Session state was null");
    }
    /*
     * we can not call this.ss.get().getHiveHistory().getHistFileName() directly
     * as this call is made from a a Jetty thread and will return null
     */
    HiveHistoryViewer hv = new HiveHistoryViewer(historyFile);
    return hv;
  }

  /**
   * Uses the sessionName property to compare to sessions.
   *
   * @return true if sessionNames are equal false otherwise
   */
  @Override
  public boolean equals(Object other) {
    if (other == null) {
      return false;
    }
    if (!(other instanceof HWISessionItem)) {
      return false;
    }
    HWISessionItem o = (HWISessionItem) other;
    if (getSessionName().equals(o.getSessionName())) {
      return true;
    } else {
      return false;
    }
  }

  public String getResultFile() {
    return resultFile;
  }

  public void setResultFile(String resultFile) {
    this.resultFile = resultFile;
  }

  /**
   * The session name is an identifier to recognize the session.
   *
   * @return the session's name
   */
  public String getSessionName() {
    return sessionName;
  }

  /**
   * Used to represent to the user and other components what state the
   * HWISessionItem is in. Certain commands can only be run when the application
   * is in certain states.
   *
   * @return the current status of the session
   */
  public WebSessionItemStatus getStatus() {
    return status;
  }

  /**
   * Currently unused.
   *
   * @return a String with the full path to the error file.
   */
  public String getErrorFile() {
    return errorFile;
  }

  /**
   * Currently unused.
   *
   * @param errorFile
   *          the full path to the file for results.
   */
  public void setErrorFile(String errorFile) {
    this.errorFile = errorFile;
  }

  /**
   * @return the auth
   */
  public HWIAuth getAuth() {
    return auth;
  }

  /**
   * @param auth
   *          the auth to set
   */
  protected void setAuth(HWIAuth auth) {
    this.auth = auth;
  }

  /** Returns an unmodifiable list of queries. */
  public List getQueries() {
    return java.util.Collections.unmodifiableList(queries);
  }

  /**
   * Adds a new query to the execution list.
   *
   * @param query
   *          query to be added to the list
   */
  public void addQuery(String query) throws HWIException {
    throwIfRunning();
    queries.add(query);
  }

  /**
   * Removes a query from the execution list.
   *
   * @param item
   *          the 0 based index of the item to be removed
   */
  public void removeQuery(int item) throws HWIException {
    throwIfRunning();
    queries.remove(item);
  }

  public void clearQueries() throws HWIException {
    throwIfRunning();
    queries.clear();
  }

  /** returns the value for resultBucketMaxSize. */
  public int getResultBucketMaxSize() {
    return resultBucketMaxSize;
  }

  /**
   * sets the value for resultBucketMaxSize.
   *
   * @param size
   *          the new size
   */
  public void setResultBucketMaxSize(int size) {
    resultBucketMaxSize = size;
  }

  /** gets the value for resultBucket. */
  public ArrayList> getResultBucket() {
    return resultBucket;
  }

  /**
   * The HWISessionItem stores the result of each query in an array.
   *
   * @return unmodifiable list of return codes
   */
  public List getQueryRet() {
    return java.util.Collections.unmodifiableList(queryRet);
  }

  /**
   * If the ItemStatus is QueryRunning most of the configuration is in a read
   * only state.
   */
  private void throwIfRunning() throws HWIException {
    if (status == WebSessionItemStatus.QUERY_RUNNING) {
      throw new HWIException("Query already running");
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy