org.apache.hadoop.hive.ql.exec.tez.TezSessionState.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hive.ql.exec.tez.TezSessionState.java

Source

/**
 * 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.exec.tez;

import java.util.Collection;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import javax.security.auth.login.LoginException;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
import org.apache.hadoop.hive.llap.coordinator.LlapCoordinator;
import org.apache.hadoop.hive.llap.impl.LlapProtocolClientImpl;
import org.apache.hadoop.hive.llap.security.LlapTokenClient;
import org.apache.hadoop.hive.llap.security.LlapTokenIdentifier;
import org.apache.hadoop.hive.llap.tez.LlapProtocolClientProxy;
import org.apache.hadoop.hive.llap.tezplugins.LlapContainerLauncher;
import org.apache.hadoop.hive.llap.tezplugins.LlapTaskCommunicator;
import org.apache.hadoop.hive.llap.tezplugins.LlapTaskSchedulerService;
import org.apache.hadoop.hive.ql.exec.Utilities;
import org.apache.hadoop.hive.ql.session.SessionState;
import org.apache.hadoop.hive.ql.session.SessionState.LogHelper;
import org.apache.hadoop.hive.shims.Utils;
import org.apache.hadoop.security.Credentials;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.yarn.api.records.LocalResource;
import org.apache.hadoop.yarn.api.records.LocalResourceType;
import org.apache.tez.client.TezClient;
import org.apache.tez.common.TezUtils;
import org.apache.tez.dag.api.PreWarmVertex;
import org.apache.tez.dag.api.SessionNotRunning;
import org.apache.tez.dag.api.TezConfiguration;
import org.apache.tez.dag.api.TezException;
import org.apache.tez.dag.api.UserPayload;
import org.apache.tez.mapreduce.hadoop.MRHelpers;
import org.apache.tez.serviceplugins.api.ContainerLauncherDescriptor;
import org.apache.tez.serviceplugins.api.ServicePluginsDescriptor;
import org.apache.tez.serviceplugins.api.TaskCommunicatorDescriptor;
import org.apache.tez.serviceplugins.api.TaskSchedulerDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hadoop.hive.ql.exec.tez.monitoring.TezJobMonitor;

/**
 * Holds session state related to Tez
 */
public class TezSessionState {

    private static final Logger LOG = LoggerFactory.getLogger(TezSessionState.class.getName());
    private static final String TEZ_DIR = "_tez_session_dir";
    public static final String LLAP_SERVICE = "LLAP";
    private static final String LLAP_SCHEDULER = LlapTaskSchedulerService.class.getName();
    private static final String LLAP_LAUNCHER = LlapContainerLauncher.class.getName();
    private static final String LLAP_TASK_COMMUNICATOR = LlapTaskCommunicator.class.getName();

    private HiveConf conf;
    private Path tezScratchDir;
    private LocalResource appJarLr;
    private TezClient session;
    private Future<TezClient> sessionFuture;
    /** Console used for user feedback during async session opening. */
    private LogHelper console;
    private String sessionId;
    private final DagUtils utils;
    private String queueName;
    private boolean defaultQueue = false;
    private String user;

    private AtomicReference<String> ownerThread = new AtomicReference<>(null);

    private final Set<String> additionalFilesNotFromConf = new HashSet<String>();
    private final Set<LocalResource> localizedResources = new HashSet<LocalResource>();
    private boolean doAsEnabled;

    /**
     * Constructor. We do not automatically connect, because we only want to
     * load tez classes when the user has tez installed.
     */
    public TezSessionState(DagUtils utils) {
        this.utils = utils;
    }

    public String toString() {
        return "sessionId=" + sessionId + ", queueName=" + queueName + ", user=" + user + ", doAs=" + doAsEnabled
                + ", isOpen=" + isOpen() + ", isDefault=" + defaultQueue;
    }

    /**
     * Constructor. We do not automatically connect, because we only want to
     * load tez classes when the user has tez installed.
     */
    public TezSessionState(String sessionId) {
        this(DagUtils.getInstance());
        this.sessionId = sessionId;
    }

    public boolean isOpening() {
        if (session != null || sessionFuture == null)
            return false;
        try {
            session = sessionFuture.get(0, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (CancellationException e) {
            return false;
        } catch (TimeoutException e) {
            return true;
        }
        return false;
    }

    public boolean isOpen() {
        if (session != null)
            return true;
        if (sessionFuture == null)
            return false;
        try {
            session = sessionFuture.get(0, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (TimeoutException | CancellationException e) {
            return false;
        }
        return true;
    }

    /**
     * Get all open sessions. Only used to clean up at shutdown.
     * @return List<TezSessionState>
     */
    public static String makeSessionId() {
        return UUID.randomUUID().toString();
    }

    public void open(HiveConf conf) throws IOException, LoginException, URISyntaxException, TezException {
        Set<String> noFiles = null;
        open(conf, noFiles, null);
    }

    /**
     * Creates a tez session. A session is tied to either a cli/hs2 session. You can
     * submit multiple DAGs against a session (as long as they are executed serially).
     * @throws IOException
     * @throws URISyntaxException
     * @throws LoginException
     * @throws TezException
     * @throws InterruptedException
     */
    public void open(HiveConf conf, String[] additionalFiles)
            throws IOException, LoginException, IllegalArgumentException, URISyntaxException, TezException {
        openInternal(conf, setFromArray(additionalFiles), false, null, null);
    }

    private static Set<String> setFromArray(String[] additionalFiles) {
        if (additionalFiles == null)
            return null;
        Set<String> files = new HashSet<>();
        for (String originalFile : additionalFiles) {
            files.add(originalFile);
        }
        return files;
    }

    public void beginOpen(HiveConf conf, String[] additionalFiles, LogHelper console)
            throws IOException, LoginException, IllegalArgumentException, URISyntaxException, TezException {
        openInternal(conf, setFromArray(additionalFiles), true, console, null);
    }

    public void open(HiveConf conf, Collection<String> additionalFiles, Path scratchDir)
            throws LoginException, IOException, URISyntaxException, TezException {
        openInternal(conf, additionalFiles, false, null, scratchDir);
    }

    protected void openInternal(final HiveConf conf, Collection<String> additionalFiles, boolean isAsync,
            LogHelper console, Path scratchDir)
            throws IOException, LoginException, IllegalArgumentException, URISyntaxException, TezException {
        this.conf = conf;
        // TODO Why is the queue name set again. It has already been setup via setQueueName. Do only one of the two.
        String confQueueName = conf.get(TezConfiguration.TEZ_QUEUE_NAME);
        if (queueName != null && !queueName.equals(confQueueName)) {
            LOG.warn("Resetting a queue name that was already set: was " + queueName + ", now " + confQueueName);
        }
        this.queueName = confQueueName;
        this.doAsEnabled = conf.getBoolVar(HiveConf.ConfVars.HIVE_SERVER2_ENABLE_DOAS);

        final boolean llapMode = "llap"
                .equalsIgnoreCase(HiveConf.getVar(conf, HiveConf.ConfVars.HIVE_EXECUTION_MODE));

        // TODO This - at least for the session pool - will always be the hive user. How does doAs above this affect things ?
        UserGroupInformation ugi = Utils.getUGI();
        user = ugi.getShortUserName();
        LOG.info("User of session id " + sessionId + " is " + user);

        // create the tez tmp dir
        tezScratchDir = scratchDir == null ? createTezDir(sessionId) : scratchDir;

        additionalFilesNotFromConf.clear();
        if (additionalFiles != null) {
            additionalFilesNotFromConf.addAll(additionalFiles);
        }

        refreshLocalResourcesFromConf(conf);

        // unless already installed on all the cluster nodes, we'll have to
        // localize hive-exec.jar as well.
        appJarLr = createJarLocalResource(utils.getExecJarPathLocal());

        // configuration for the application master
        final Map<String, LocalResource> commonLocalResources = new HashMap<String, LocalResource>();
        commonLocalResources.put(utils.getBaseName(appJarLr), appJarLr);
        for (LocalResource lr : localizedResources) {
            commonLocalResources.put(utils.getBaseName(lr), lr);
        }

        if (llapMode) {
            // localize llap client jars
            addJarLRByClass(LlapTaskSchedulerService.class, commonLocalResources);
            addJarLRByClass(LlapProtocolClientImpl.class, commonLocalResources);
            addJarLRByClass(LlapProtocolClientProxy.class, commonLocalResources);
            addJarLRByClassName("org.apache.hadoop.registry.client.api.RegistryOperations", commonLocalResources);
        }

        // Create environment for AM.
        Map<String, String> amEnv = new HashMap<String, String>();
        MRHelpers.updateEnvBasedOnMRAMEnv(conf, amEnv);

        // and finally we're ready to create and start the session
        // generate basic tez config
        final TezConfiguration tezConfig = new TezConfiguration(conf);

        // set up the staging directory to use
        tezConfig.set(TezConfiguration.TEZ_AM_STAGING_DIR, tezScratchDir.toUri().toString());
        conf.stripHiddenConfigurations(tezConfig);

        ServicePluginsDescriptor servicePluginsDescriptor;

        Credentials llapCredentials = null;
        if (llapMode) {
            if (UserGroupInformation.isSecurityEnabled()) {
                llapCredentials = new Credentials();
                llapCredentials.addToken(LlapTokenIdentifier.KIND_NAME, getLlapToken(user, tezConfig));
            }
            // TODO Change this to not serialize the entire Configuration - minor.
            UserPayload servicePluginPayload = TezUtils.createUserPayloadFromConf(tezConfig);
            // we need plugins to handle llap and uber mode
            servicePluginsDescriptor = ServicePluginsDescriptor.create(true,
                    new TaskSchedulerDescriptor[] { TaskSchedulerDescriptor.create(LLAP_SERVICE, LLAP_SCHEDULER)
                            .setUserPayload(servicePluginPayload) },
                    new ContainerLauncherDescriptor[] {
                            ContainerLauncherDescriptor.create(LLAP_SERVICE, LLAP_LAUNCHER) },
                    new TaskCommunicatorDescriptor[] { TaskCommunicatorDescriptor
                            .create(LLAP_SERVICE, LLAP_TASK_COMMUNICATOR).setUserPayload(servicePluginPayload) });
        } else {
            servicePluginsDescriptor = ServicePluginsDescriptor.create(true);
        }

        // container prewarming. tell the am how many containers we need
        if (HiveConf.getBoolVar(conf, ConfVars.HIVE_PREWARM_ENABLED)) {
            int n = HiveConf.getIntVar(conf, ConfVars.HIVE_PREWARM_NUM_CONTAINERS);
            n = Math.max(tezConfig.getInt(TezConfiguration.TEZ_AM_SESSION_MIN_HELD_CONTAINERS,
                    TezConfiguration.TEZ_AM_SESSION_MIN_HELD_CONTAINERS_DEFAULT), n);
            tezConfig.setInt(TezConfiguration.TEZ_AM_SESSION_MIN_HELD_CONTAINERS, n);
        }

        setupSessionAcls(tezConfig, conf);

        final TezClient session = TezClient.newBuilder("HIVE-" + sessionId, tezConfig).setIsSession(true)
                .setLocalResources(commonLocalResources).setCredentials(llapCredentials)
                .setServicePluginDescriptor(servicePluginsDescriptor).build();

        LOG.info("Opening new Tez Session (id: " + sessionId + ", scratch dir: " + tezScratchDir + ")");

        TezJobMonitor.initShutdownHook();
        if (!isAsync) {
            startSessionAndContainers(session, conf, commonLocalResources, tezConfig, false);
            this.session = session;
        } else {
            FutureTask<TezClient> sessionFuture = new FutureTask<>(new Callable<TezClient>() {
                @Override
                public TezClient call() throws Exception {
                    try {
                        return startSessionAndContainers(session, conf, commonLocalResources, tezConfig, true);
                    } catch (Throwable t) {
                        LOG.error("Failed to start Tez session", t);
                        throw (t instanceof Exception) ? (Exception) t : new Exception(t);
                    }
                }
            });
            new Thread(sessionFuture, "Tez session start thread").start();
            // We assume here nobody will try to get session before open() returns.
            this.console = console;
            this.sessionFuture = sessionFuture;
        }
    }

    private static Token<LlapTokenIdentifier> getLlapToken(String user, final Configuration conf)
            throws IOException {
        // TODO: parts of this should be moved out of TezSession to reuse the clients, but there's
        //       no good place for that right now (HIVE-13698).
        // TODO: De-link from SessionState. A TezSession can be linked to different Hive Sessions via the pool.
        SessionState session = SessionState.get();
        boolean isInHs2 = session != null && session.isHiveServerQuery();
        Token<LlapTokenIdentifier> token = null;
        // For Tez, we don't use appId to distinguish the tokens.

        LlapCoordinator coordinator = null;
        if (isInHs2) {
            // We are in HS2, get the token locally.
            // TODO: coordinator should be passed in; HIVE-13698. Must be initialized for now.
            coordinator = LlapCoordinator.getInstance();
            if (coordinator == null) {
                throw new IOException("LLAP coordinator not initialized; cannot get LLAP tokens");
            }
            // Signing is not required for Tez.
            token = coordinator.getLocalTokenClient(conf, user).createToken(null, null, false);
        } else {
            // We are not in HS2; always create a new client for now.
            token = new LlapTokenClient(conf).getDelegationToken(null);
        }
        if (LOG.isInfoEnabled()) {
            LOG.info("Obtained a LLAP token: " + token);
        }
        return token;
    }

    private TezClient startSessionAndContainers(TezClient session, HiveConf conf,
            Map<String, LocalResource> commonLocalResources, TezConfiguration tezConfig, boolean isOnThread)
            throws TezException, IOException {
        session.start();
        boolean isSuccessful = false;
        try {
            if (HiveConf.getBoolVar(conf, ConfVars.HIVE_PREWARM_ENABLED)) {
                int n = HiveConf.getIntVar(conf, ConfVars.HIVE_PREWARM_NUM_CONTAINERS);
                LOG.info("Prewarming " + n + " containers  (id: " + sessionId + ", scratch dir: " + tezScratchDir
                        + ")");
                PreWarmVertex prewarmVertex = utils.createPreWarmVertex(tezConfig, n, commonLocalResources);
                try {
                    session.preWarm(prewarmVertex);
                } catch (IOException ie) {
                    if (!isOnThread && ie.getMessage().contains("Interrupted while waiting")) {
                        LOG.warn("Hive Prewarm threw an exception ", ie);
                    } else {
                        throw ie;
                    }
                }
            }
            try {
                session.waitTillReady();
            } catch (InterruptedException ie) {
                if (isOnThread)
                    throw new IOException(ie);
                //ignore
            }
            isSuccessful = true;
            // sessionState.getQueueName() comes from cluster wide configured queue names.
            // sessionState.getConf().get("tez.queue.name") is explicitly set by user in a session.
            // TezSessionPoolManager sets tez.queue.name if user has specified one or use the one from
            // cluster wide queue names.
            // There is no way to differentiate how this was set (user vs system).
            // Unset this after opening the session so that reopening of session uses the correct queue
            // names i.e, if client has not died and if the user has explicitly set a queue name
            // then reopened session will use user specified queue name else default cluster queue names.
            conf.unset(TezConfiguration.TEZ_QUEUE_NAME);
            return session;
        } finally {
            if (isOnThread && !isSuccessful) {
                closeAndIgnoreExceptions(session);
            }
        }
    }

    private static void closeAndIgnoreExceptions(TezClient session) {
        try {
            session.stop();
        } catch (SessionNotRunning nr) {
            // Ignore.
        } catch (IOException | TezException ex) {
            LOG.info("Failed to close Tez session after failure to initialize: " + ex.getMessage());
        }
    }

    public void endOpen() throws InterruptedException, CancellationException {
        if (this.session != null || this.sessionFuture == null)
            return;
        try {
            this.session = this.sessionFuture.get();
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    private void setupSessionAcls(Configuration tezConf, HiveConf hiveConf) throws IOException {

        // TODO: De-link from SessionState. A TezSession can be linked to different Hive Sessions via the pool.
        String user = SessionState.getUserFromAuthenticator();
        UserGroupInformation loginUserUgi = UserGroupInformation.getLoginUser();
        String loginUser = loginUserUgi == null ? null : loginUserUgi.getShortUserName();
        boolean addHs2User = HiveConf.getBoolVar(hiveConf, ConfVars.HIVETEZHS2USERACCESS);

        String viewStr = Utilities.getAclStringWithHiveModification(tezConf, TezConfiguration.TEZ_AM_VIEW_ACLS,
                addHs2User, user, loginUser);
        String modifyStr = Utilities.getAclStringWithHiveModification(tezConf, TezConfiguration.TEZ_AM_MODIFY_ACLS,
                addHs2User, user, loginUser);

        if (LOG.isDebugEnabled()) {
            // TODO: De-link from SessionState. A TezSession can be linked to different Hive Sessions via the pool.
            LOG.debug("Setting Tez Session access for sessionId={} with viewAclString={}, modifyStr={}",
                    SessionState.get().getSessionId(), viewStr, modifyStr);
        }

        tezConf.set(TezConfiguration.TEZ_AM_VIEW_ACLS, viewStr);
        tezConf.set(TezConfiguration.TEZ_AM_MODIFY_ACLS, modifyStr);
    }

    public void refreshLocalResourcesFromConf(HiveConf conf)
            throws IOException, LoginException, IllegalArgumentException, URISyntaxException, TezException {

        String dir = tezScratchDir.toString();

        localizedResources.clear();

        // these are local resources set through add file, jar, etc
        List<LocalResource> lrs = utils.localizeTempFilesFromConf(dir, conf);
        if (lrs != null) {
            localizedResources.addAll(lrs);
        }

        // these are local resources that are set through the mr "tmpjars" property; skip session files.
        List<LocalResource> handlerLr = utils.localizeTempFiles(dir, conf,
                additionalFilesNotFromConf.toArray(new String[additionalFilesNotFromConf.size()]),
                DagUtils.getTempFilesFromConf(conf));

        if (handlerLr != null) {
            localizedResources.addAll(handlerLr);
        }
    }

    public boolean hasResources(String[] localAmResources) {
        if (localAmResources == null || localAmResources.length == 0)
            return true;
        if (additionalFilesNotFromConf.isEmpty())
            return false;
        for (String s : localAmResources) {
            if (!additionalFilesNotFromConf.contains(s))
                return false;
        }
        return true;
    }

    /**
     * Close a tez session. Will cleanup any tez/am related resources. After closing a session no
     * further DAGs can be executed against it.
     *
     * @param keepTmpDir
     *          whether or not to remove the scratch dir at the same time.
     * @throws Exception
     */
    public void close(boolean keepTmpDir) throws Exception {
        if (session != null) {
            LOG.info("Closing Tez Session");
            closeClient(session);
        } else if (sessionFuture != null) {
            sessionFuture.cancel(true);
            TezClient asyncSession = null;
            try {
                asyncSession = sessionFuture.get(); // In case it was done and noone looked at it.
            } catch (ExecutionException | CancellationException e) {
                // ignore
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                // ignore
            }
            if (asyncSession != null) {
                LOG.info("Closing Tez Session");
                closeClient(asyncSession);
            }
        }

        if (!keepTmpDir) {
            cleanupScratchDir();
        }
        session = null;
        sessionFuture = null;
        console = null;
        tezScratchDir = null;
        conf = null;
        appJarLr = null;
        additionalFilesNotFromConf.clear();
        localizedResources.clear();
    }

    public Set<String> getAdditionalFilesNotFromConf() {
        return additionalFilesNotFromConf;
    }

    private void closeClient(TezClient client) throws TezException, IOException {
        try {
            client.stop();
        } catch (SessionNotRunning nr) {
            // ignore
        }
    }

    public void cleanupScratchDir() throws IOException {
        FileSystem fs = tezScratchDir.getFileSystem(conf);
        fs.delete(tezScratchDir, true);
        tezScratchDir = null;
    }

    public String getSessionId() {
        return sessionId;
    }

    public TezClient getSession() {
        if (session == null && sessionFuture != null) {
            if (!sessionFuture.isDone()) {
                console.printInfo("Waiting for Tez session and AM to be ready...");
            }
            try {
                session = sessionFuture.get();
            } catch (InterruptedException e) {
                console.printInfo("Interrupted while waiting for the session");
                Thread.currentThread().interrupt();
                return null;
            } catch (ExecutionException e) {
                console.printInfo("Failed to get session");
                throw new RuntimeException(e);
            } catch (CancellationException e) {
                console.printInfo("Cancelled while waiting for the session");
                return null;
            }
        }
        return session;
    }

    public Path getTezScratchDir() {
        return tezScratchDir;
    }

    public LocalResource getAppJarLr() {
        return appJarLr;
    }

    /**
     * createTezDir creates a temporary directory in the scratchDir folder to
     * be used with Tez. Assumes scratchDir exists.
     */
    private Path createTezDir(String sessionId) throws IOException {
        // tez needs its own scratch dir (per session)
        // TODO: De-link from SessionState. A TezSession can be linked to different Hive Sessions via the pool.
        Path tezDir = new Path(SessionState.get().getHdfsScratchDirURIString(), TEZ_DIR);
        tezDir = new Path(tezDir, sessionId);
        FileSystem fs = tezDir.getFileSystem(conf);
        FsPermission fsPermission = new FsPermission(HiveConf.getVar(conf, HiveConf.ConfVars.SCRATCHDIRPERMISSION));
        fs.mkdirs(tezDir, fsPermission);
        // Make sure the path is normalized (we expect validation to pass since we just created it).
        tezDir = DagUtils.validateTargetDir(tezDir, conf).getPath();

        // Directory removal will be handled by cleanup at the SessionState level.
        return tezDir;
    }

    /**
     * Returns a local resource representing a jar.
     * This resource will be used to execute the plan on the cluster.
     * @param localJarPath Local path to the jar to be localized.
     * @return LocalResource corresponding to the localized hive exec resource.
     * @throws IOException when any file system related call fails.
     * @throws LoginException when we are unable to determine the user.
     * @throws URISyntaxException when current jar location cannot be determined.
     */
    private LocalResource createJarLocalResource(String localJarPath)
            throws IOException, LoginException, IllegalArgumentException, FileNotFoundException {
        // TODO Reduce the number of lookups that happen here. This shouldn't go to HDFS for each call.
        // The hiveJarDir can be determined once per client.
        FileStatus destDirStatus = utils.getHiveJarDirectory(conf);
        assert destDirStatus != null;
        Path destDirPath = destDirStatus.getPath();

        Path localFile = new Path(localJarPath);
        String sha = getSha(localFile);

        String destFileName = localFile.getName();

        // Now, try to find the file based on SHA and name. Currently we require exact name match.
        // We could also allow cutting off versions and other stuff provided that SHA matches...
        destFileName = FilenameUtils.removeExtension(destFileName) + "-" + sha + FilenameUtils.EXTENSION_SEPARATOR
                + FilenameUtils.getExtension(destFileName);

        if (LOG.isDebugEnabled()) {
            LOG.debug("The destination file name for [" + localJarPath + "] is " + destFileName);
        }

        // TODO: if this method is ever called on more than one jar, getting the dir and the
        //       list need to be refactored out to be done only once.
        Path destFile = new Path(destDirPath.toString() + "/" + destFileName);
        return utils.localizeResource(localFile, destFile, LocalResourceType.FILE, conf);
    }

    private void addJarLRByClassName(String className, final Map<String, LocalResource> lrMap)
            throws IOException, LoginException {
        Class<?> clazz;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new IOException("Cannot find " + className + " in classpath", e);
        }
        addJarLRByClass(clazz, lrMap);
    }

    private void addJarLRByClass(Class<?> clazz, final Map<String, LocalResource> lrMap)
            throws IOException, LoginException {
        final File jar = new File(Utilities.jarFinderGetJar(clazz));
        final LocalResource jarLr = createJarLocalResource(jar.toURI().toURL().toExternalForm());
        lrMap.put(utils.getBaseName(jarLr), jarLr);
    }

    private String getSha(Path localFile) throws IOException, IllegalArgumentException {
        InputStream is = null;
        try {
            FileSystem localFs = FileSystem.getLocal(conf);
            is = localFs.open(localFile);
            return DigestUtils.sha256Hex(is);
        } finally {
            if (is != null) {
                is.close();
            }
        }
    }

    public void setQueueName(String queueName) {
        this.queueName = queueName;
    }

    public String getQueueName() {
        return queueName;
    }

    public void setDefault() {
        defaultQueue = true;
    }

    public boolean isDefault() {
        return defaultQueue;
    }

    public HiveConf getConf() {
        return conf;
    }

    public List<LocalResource> getLocalizedResources() {
        return new ArrayList<>(localizedResources);
    }

    public String getUser() {
        return user;
    }

    public boolean getDoAsEnabled() {
        return doAsEnabled;
    }

    /** Mark session as free for use from TezTask, for safety/debugging purposes. */
    public void markFree() {
        if (ownerThread.getAndSet(null) == null)
            throw new AssertionError("Not in use");
    }

    /** Mark session as being in use from TezTask, for safety/debugging purposes. */
    public void markInUse() {
        String newName = Thread.currentThread().getName();
        do {
            String oldName = ownerThread.get();
            if (oldName != null) {
                throw new AssertionError(
                        "Tez session is already in use from " + oldName + "; cannot use from " + newName);
            }
        } while (!ownerThread.compareAndSet(null, newName));
    }

}