/**
* 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.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.google.common.base.Preconditions;
/**
* Utility class which contains methods related to generating JAR file
* and/or byte stream from passed directory.
* Currently used as a dev feature allowing auto-generation of the JAR filr from
* local dev workspace when submitting Tez jobs directly from the IDE.
*/
public class ClassPathUtils {
private final static Log logger = LogFactory.getLog(ClassPathUtils.class);
/**
*
* @param resource
*/
public static void addResourceToClassPath(File resource) {
try {
Preconditions.checkState(resource != null && resource.exists(), "'resource' must not be null and it must exist: " + resource);
URLClassLoader cl = (URLClassLoader) Thread.currentThread().getContextClassLoader();
Method addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addUrlMethod.setAccessible(true);
addUrlMethod.invoke(cl, resource.toURI().toURL());
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
/**
* Will create a JAR file from base dir
*
* @param sourceDir
* @param jarName
* @return
*/
public static File toJar(File sourceDir, String jarName) {
if (!sourceDir.isAbsolute()) {
throw new IllegalArgumentException("Source must be expressed through absolute path");
}
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
File jarFile = new File(jarName);
try {
JarOutputStream target = new JarOutputStream(new FileOutputStream(jarFile), manifest);
add(sourceDir, sourceDir.getAbsolutePath().length(), target);
target.close();
}
catch (Exception e) {
throw new IllegalStateException("Failed to create JAR file '"
+ jarName + "' from " + sourceDir.getAbsolutePath(), e);
}
return jarFile;
}
/**
*
* @param applicationName
* @return
*/
public static String generateJarFileName(String applicationName){
StringBuffer nameBuffer = new StringBuffer();
nameBuffer.append(applicationName);
nameBuffer.append("_");
nameBuffer.append(UUID.randomUUID().toString());
nameBuffer.append(".jar");
return nameBuffer.toString();
}
/**
*
* @param source
* @param lengthOfOriginalPath
* @param target
* @throws IOException
*/
private static void add(File source, int lengthOfOriginalPath, JarOutputStream target) throws IOException {
BufferedInputStream in = null;
try {
String path = source.getAbsolutePath();
path = path.substring(lengthOfOriginalPath);
if (source.isDirectory()) {
String name = path.replace("\\", "/");
if (!name.isEmpty()) {
if (!name.endsWith("/")) {
name += "/";
}
JarEntry entry = new JarEntry(name.substring(1)); // avoiding absolute path warning
target.putNextEntry(entry);
target.closeEntry();
}
for (File nestedFile : source.listFiles()) {
add(nestedFile, lengthOfOriginalPath, target);
}
return;
}
JarEntry entry = new JarEntry(path.replace("\\", "/").substring(1)); // avoiding absolute path warning
entry.setTime(source.lastModified());
try {
target.putNextEntry(entry);
in = new BufferedInputStream(new FileInputStream(source));
byte[] buffer = new byte[1024];
while (true) {
int count = in.read(buffer);
if (count == -1) {
break;
}
target.write(buffer, 0, count);
}
target.closeEntry();
}
catch (Exception e) {
String message = e.getMessage();
if (message != null){
if (!message.toLowerCase().contains("duplicate")){
throw new IllegalStateException(e);
}
logger.warn(message);
}
else {
throw new IllegalStateException(e);
}
}
}
finally {
if (in != null)
in.close();
}
}
/**
*
* @param exclusionFile
* @return
*/
public static String[] initClasspathExclusions(String exclusionFile){
String[] classpathExclusions = null;
try {
InputStream excInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(exclusionFile);
if (excInputStream != null){
List<String> exclusionPatterns = new ArrayList<String>();
BufferedReader reader = new BufferedReader(new InputStreamReader(excInputStream));
String line;
while ((line = reader.readLine()) != null){
exclusionPatterns.add(line.trim());
}
classpathExclusions = exclusionPatterns.toArray(new String[]{});
reader.close();
}
} catch (Exception e) {
logger.warn("Failed to build the list of classpath exclusion. ", e);
}
return classpathExclusions;
}
}