/**
* 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 io.dstream.tez.utils;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.yarn.api.records.LocalResource;
import org.apache.hadoop.yarn.api.records.LocalResourceType;
import org.apache.hadoop.yarn.api.records.LocalResourceVisibility;
import org.apache.hadoop.yarn.util.ConverterUtils;
import org.apache.tez.dag.api.TezConfiguration;
import io.dstream.tez.TezConstants;
/**
* Utility functions related to variety of tasks to be performed in HADOOP
* such as setting up LocalResource, provisioning classpath etc.
*
*/
public class HadoopUtils {
private static final Log logger = LogFactory.getLog(HadoopUtils.class);
/**
*
* @param configuration
* @return
*/
public static FileSystem getFileSystem(Configuration configuration){
try {
return FileSystem.get(configuration);
}
catch (Exception e) {
throw new IllegalStateException("Failed to access FileSystem", e);
}
}
/**
* Creates {@link LocalResource}s based on the current user's classpath
*
* @param fs
* @param classPathDir
* @return
*/
public static Map<String, LocalResource> createLocalResources(FileSystem fs, String classPathDir) {
Map<String, LocalResource> localResources = provisionAndLocalizeCurrentClasspath(fs, classPathDir);
return localResources;
}
/**
* Provisions resource represented as {@link File} to the {@link FileSystem} for a given application
*
* @param localResource
* @param fs
* @param applicationName
* @return
*/
public static Path provisionResourceToFs(File localResource, FileSystem fs, String applicationName) throws Exception {
String destinationFilePath = applicationName + "/" + localResource.getName();
Path provisionedPath = new Path(fs.getHomeDirectory(), destinationFilePath);
provisioinResourceToFs(fs, new Path(localResource.getAbsolutePath()), provisionedPath);
return provisionedPath;
}
/**
* Creates a single {@link LocalResource} for the provisioned resource identified with {@link Path}
*
* @param fs
* @param provisionedResourcePath
* @return
*/
public static LocalResource createLocalResource(FileSystem fs, Path provisionedResourcePath){
try {
FileStatus scFileStatus = fs.getFileStatus(provisionedResourcePath);
LocalResource localResource = LocalResource.newInstance(
ConverterUtils.getYarnUrlFromURI(provisionedResourcePath.toUri()),
LocalResourceType.FILE,
LocalResourceVisibility.APPLICATION, scFileStatus.getLen(),
scFileStatus.getModificationTime());
return localResource;
}
catch (Exception e) {
throw new IllegalStateException("Failed to communicate with FileSystem while creating LocalResource: " + fs, e);
}
}
/**
* Will provision current classpath to YARN and return an array of
* {@link Path}s representing provisioned resources
* If 'generate-jar' system property is set it will also generate the JAR for the current
* working directory (mainly used when executing from IDE)
*/
private static Path[] provisionClassPath(FileSystem fs, String applicationName, String[] classPathExclusions){
String genJarProperty = System.getProperty(TezConstants.GENERATE_JAR);
boolean generateJar = genJarProperty != null && Boolean.parseBoolean(genJarProperty);
List<Path> provisionedPaths = new ArrayList<Path>();
List<File> generatedJars = new ArrayList<File>();
boolean confFromHadoopConfDir = generateConfigJarFromHadoopConfDir(fs, applicationName, provisionedPaths, generatedJars);
TezConfiguration tezConf = new TezConfiguration(fs.getConf());
boolean provisionTez = true;
if (tezConf.get("tez.lib.uris") != null){
provisionTez = false;
}
URL[] classpath = ((URLClassLoader) ClassLoader.getSystemClassLoader()).getURLs();
for (URL classpathUrl : classpath) {
File f = new File(classpathUrl.getFile());
if (f.isDirectory()) {
if (generateJar){
String jarFileName = ClassPathUtils.generateJarFileName("application");
f = doGenerateJar(f, jarFileName, generatedJars, "application");
}
else if (f.getName().equals("conf") && !confFromHadoopConfDir){
String jarFileName = ClassPathUtils.generateJarFileName("conf_application");
f = doGenerateJar(f, jarFileName, generatedJars, "configuration");
}
else {
f = null;
}
}
if (f != null){
if (f.getName().startsWith("tez-") && !provisionTez){
logger.info("Skipping provisioning of " + f.getName() + " since Tez libraries are already provisioned");
continue;
}
String destinationFilePath = applicationName + "/" + f.getName();
Path provisionedPath = new Path(fs.getHomeDirectory(), destinationFilePath);
if (shouldProvision(provisionedPath.getName(), classPathExclusions)){
try {
provisioinResourceToFs(fs, new Path(f.getAbsolutePath()), provisionedPath);
provisionedPaths.add(provisionedPath);
} catch (Exception e) {
logger.warn("Failed to provision " + provisionedPath + "; " + e.getMessage());
if (logger.isDebugEnabled()){
logger.trace("Failed to provision " + provisionedPath, e);
}
}
}
}
}
for (File generatedJar : generatedJars) {
try {
generatedJar.delete();
} catch (Exception e) {
logger.warn("Failed to delete generated jars", e);
}
}
return provisionedPaths.toArray(new Path[]{});
}
/**
*
*/
private static File doGenerateJar(File f, String jarFileName, List<File> generatedJars, String subMessage) {
if (logger.isDebugEnabled()){
logger.debug("Generating " + subMessage + " JAR: " + jarFileName);
}
File jarFile = ClassPathUtils.toJar(f, jarFileName);
generatedJars.add(jarFile);
return jarFile;
}
/**
*
*/
private static boolean generateConfigJarFromHadoopConfDir(FileSystem fs, String applicationName, List<Path> provisionedPaths, List<File> generatedJars){
boolean generated = false;
String hadoopConfDir = System.getenv().get("HADOOP_CONF_DIR");
if (hadoopConfDir != null && hadoopConfDir.trim().length() > 0){
String jarFileName = ClassPathUtils.generateJarFileName("conf_");
File confDir = new File(hadoopConfDir.trim());
File jarFile = doGenerateJar(confDir, jarFileName, generatedJars, "configuration (HADOOP_CONF_DIR)");
String destinationFilePath = applicationName + "/" + jarFile.getName();
Path provisionedPath = new Path(fs.getHomeDirectory(), destinationFilePath);
try {
provisioinResourceToFs(fs, new Path(jarFile.getAbsolutePath()), provisionedPath);
provisionedPaths.add(provisionedPath);
generated = true;
} catch (Exception e) {
logger.warn("Failed to provision " + provisionedPath + "; " + e.getMessage());
if (logger.isDebugEnabled()){
logger.warn("Failed to provision " + provisionedPath, e);
}
throw new IllegalStateException(e);
}
}
String tezConfDir = System.getenv().get("TEZ_CONF_DIR");
if (tezConfDir != null && tezConfDir.trim().length() > 0){
String jarFileName = ClassPathUtils.generateJarFileName("conf_tez");
File confDir = new File(tezConfDir.trim());
File jarFile = doGenerateJar(confDir, jarFileName, generatedJars, "configuration (TEZ_CONF_DIR)");
try {
URLClassLoader cl = (URLClassLoader) ClassLoader.getSystemClassLoader();
Method m = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
m.setAccessible(true);
m.invoke(cl, jarFile.toURI().toURL());
} catch (Exception e) {
throw new IllegalStateException(e);
}
String destinationFilePath = applicationName + "/" + jarFile.getName();
Path provisionedPath = new Path(fs.getHomeDirectory(), destinationFilePath);
try {
provisioinResourceToFs(fs, new Path(jarFile.getAbsolutePath()), provisionedPath);
provisionedPaths.add(provisionedPath);
generated = true;
} catch (Exception e) {
logger.warn("Failed to provision " + provisionedPath + "; " + e.getMessage());
if (logger.isDebugEnabled()){
logger.warn("Failed to provision " + provisionedPath, e);
}
throw new IllegalStateException(e);
}
}
return generated;
}
/**
*
*/
private static boolean shouldProvision(String path, String[] classPathExclusions){
if (classPathExclusions != null){
for (String exclusion : classPathExclusions) {
if (path.contains(exclusion) || !path.endsWith(".jar")){
if (logger.isDebugEnabled()){
logger.debug("Excluding resource: " + path);
}
return false;
}
}
}
return true;
}
/**
*
*/
private static Map<String, LocalResource> createLocalResources(FileSystem fs, Path[] provisionedResourcesPaths) {
Map<String, LocalResource> localResources = new LinkedHashMap<String, LocalResource>();
for (Path provisionedResourcesPath : provisionedResourcesPaths) {
LocalResource localResource = createLocalResource(fs, provisionedResourcesPath);
localResources.put(provisionedResourcesPath.getName(), localResource);
}
return localResources;
}
/**
*
*/
private static synchronized void provisioinResourceToFs(FileSystem fs, Path sourcePath, Path destPath) throws Exception {
if (logger.isDebugEnabled()){
logger.debug("Provisioning '" + sourcePath + "' to " + destPath);
}
if (!fs.exists(destPath)){
fs.copyFromLocalFile(sourcePath, destPath);
}
else {
logger.debug("Skipping provisioning of " + destPath + " since it already exists.");
}
}
/**
*
*/
private static Map<String, LocalResource> provisionAndLocalizeCurrentClasspath(FileSystem fs, String appName) {
Path[] provisionedResourcesPaths = HadoopUtils.provisionClassPath(fs, appName, ClassPathUtils.initClasspathExclusions(TezConstants.CLASSPATH_EXCLUSIONS));
Map<String, LocalResource> localResources = HadoopUtils.createLocalResources(fs, provisionedResourcesPaths);
return localResources;
}
}