/*
* Copyright © 2014 Cask Data, Inc.
*
* Licensed 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 co.cask.cdap.explore.guice;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.io.Files;
import org.apache.hadoop.hive.conf.HiveConf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
/**
* This class hijacks Hadoop bin, and adds hbase-protocol jar to HADOOP_CLASSPATH.
* This hack should go away when Twill supports setting of environmental variables for a TwillRunnable.
* Addition info on why this is needed is in CDAP-9
*/
public class LocalMapreduceClasspathSetter {
private static final Logger LOG = LoggerFactory.getLogger(LocalMapreduceClasspathSetter.class);
private static final Pattern HBASE_PROTOCOL_JAR_NAME_PATTERN =
Pattern.compile(".*" + File.separatorChar + "hbase-protocol-.+\\.jar$");
private final HiveConf hiveConf;
private final String directory;
private final List<String> hiveAuxJars;
private final Set<String> hbaseProtocolJarPaths = new LinkedHashSet<>();
public LocalMapreduceClasspathSetter(HiveConf hiveConf, String directory, List<String> hiveAuxJars) {
this.hiveConf = hiveConf;
this.directory = directory;
this.hiveAuxJars = hiveAuxJars;
}
public void accept(String jar) {
if (HBASE_PROTOCOL_JAR_NAME_PATTERN.matcher(jar).matches()) {
hbaseProtocolJarPaths.add(jar);
}
}
public void setupClasspathScript() throws IOException {
if (hbaseProtocolJarPaths.isEmpty()) {
LOG.info("No HBase Protocol jar found. Not setting up HADOOP_CLASSPATH");
return;
}
File exploreHadoopBin = new File(directory, "explore_hadoop");
LOG.info("Adding {} to HADOOP_CLASSPATH", hbaseProtocolJarPaths);
String hadoopBin = hiveConf.get(HiveConf.ConfVars.HADOOPBIN.toString());
// We over-ride HADOOPBIN setting in HiveConf to the script below, so that Hive uses this script to execute
// map reduce jobs.
// The below script updates HADOOP_CLASSPATH to contain hbase-protocol jar for RunJar commands,
// so that the right version of protocol buffer jar gets loaded for HBase.
// It also puts all the user jars, ie hive aux jars, in this classpath and in first position, so that
// the right version of ASM jar gets loaded for Twill.
// It then calls the real Hadoop bin with the same arguments.
StringBuilder fileBuilder = new StringBuilder();
fileBuilder.append("#!/usr/bin/env bash\n");
fileBuilder.append("# This file is a hack to set HADOOP_CLASSPATH for Hive local mapreduce tasks.\n");
fileBuilder.append("# This hack should go away when Twill supports setting of environmental variables for a ");
fileBuilder.append("TwillRunnable.\n");
fileBuilder.append("\n");
fileBuilder.append("function join { local IFS=\"$1\"; shift; echo \"$*\"; }\n");
fileBuilder.append("if [ $# -ge 1 -a \"$1\" = \"jar\" ]; then\n");
fileBuilder.append(" HADOOP_CLASSPATH=$(join ").append(File.pathSeparatorChar)
.append(" ").append(Joiner.on(' ').join(hiveAuxJars))
.append(" ${HADOOP_CLASSPATH} ")
.append(Joiner.on(' ').join(hbaseProtocolJarPaths)).append(')').append("\n");
fileBuilder.append(" # Put user jars first in Hadoop classpath so that the ASM jar needed by Twill has\n");
fileBuilder.append(" # the right version, and not the one provided with the Hadoop libs.\n");
fileBuilder.append(" export HADOOP_USER_CLASSPATH_FIRST=true\n");
fileBuilder.append(" export HADOOP_CLASSPATH\n");
fileBuilder.append(" echo \"Explore modified HADOOP_CLASSPATH = $HADOOP_CLASSPATH\" 1>&2\n");
fileBuilder.append("fi\n");
fileBuilder.append("\n");
fileBuilder.append("exec ").append(hadoopBin).append(" \"$@\"\n");
Files.write(fileBuilder.toString(), exploreHadoopBin, Charsets.UTF_8);
if (!exploreHadoopBin.setExecutable(true, false)) {
throw new RuntimeException("Cannot set executable permission on " + exploreHadoopBin.getAbsolutePath());
}
LOG.info("Setting Hadoop bin to Explore Hadoop bin {}", exploreHadoopBin.getAbsolutePath());
System.setProperty(HiveConf.ConfVars.HADOOPBIN.toString(), exploreHadoopBin.getAbsolutePath());
}
Set<String> getHbaseProtocolJarPaths() {
return hbaseProtocolJarPaths;
}
}