001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.mapreduce; 019 020import java.io.BufferedOutputStream; 021import java.io.File; 022import java.io.FileInputStream; 023import java.io.FileOutputStream; 024import java.io.IOException; 025import java.io.InputStream; 026import java.net.URL; 027import java.net.URLDecoder; 028import java.text.MessageFormat; 029import java.util.Enumeration; 030import java.util.jar.JarFile; 031import java.util.jar.JarOutputStream; 032import java.util.jar.Manifest; 033import java.util.zip.ZipEntry; 034import java.util.zip.ZipOutputStream; 035import org.apache.yetus.audience.InterfaceAudience; 036 037import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 038 039/** 040 * Finds the Jar for a class. If the class is in a directory in the classpath, it creates a Jar on 041 * the fly with the contents of the directory and returns the path to that Jar. If a Jar is created, 042 * it is created in the system temporary directory. This file was forked from 043 * hadoop/common/branches/branch-2@1377176. 044 */ 045@InterfaceAudience.Private 046public final class JarFinder { 047 048 private static void copyToZipStream(File file, ZipEntry entry, ZipOutputStream zos) 049 throws IOException { 050 InputStream is = new FileInputStream(file); 051 try { 052 zos.putNextEntry(entry); 053 byte[] arr = new byte[4096]; 054 int read = is.read(arr); 055 while (read > -1) { 056 zos.write(arr, 0, read); 057 read = is.read(arr); 058 } 059 } finally { 060 try { 061 is.close(); 062 } finally { 063 zos.closeEntry(); 064 } 065 } 066 } 067 068 public static void jarDir(File dir, String relativePath, ZipOutputStream zos) throws IOException { 069 Preconditions.checkNotNull(relativePath, "relativePath"); 070 Preconditions.checkNotNull(zos, "zos"); 071 072 // by JAR spec, if there is a manifest, it must be the first entry in the 073 // ZIP. 074 File manifestFile = new File(dir, JarFile.MANIFEST_NAME); 075 ZipEntry manifestEntry = new ZipEntry(JarFile.MANIFEST_NAME); 076 if (!manifestFile.exists()) { 077 zos.putNextEntry(manifestEntry); 078 new Manifest().write(new BufferedOutputStream(zos)); 079 zos.closeEntry(); 080 } else { 081 copyToZipStream(manifestFile, manifestEntry, zos); 082 } 083 zos.closeEntry(); 084 zipDir(dir, relativePath, zos, true); 085 zos.close(); 086 } 087 088 private static void zipDir(File dir, String relativePath, ZipOutputStream zos, boolean start) 089 throws IOException { 090 String[] dirList = dir.list(); 091 if (dirList == null) { 092 return; 093 } 094 for (String aDirList : dirList) { 095 File f = new File(dir, aDirList); 096 if (!f.isHidden()) { 097 if (f.isDirectory()) { 098 if (!start) { 099 ZipEntry dirEntry = new ZipEntry(relativePath + f.getName() + "/"); 100 zos.putNextEntry(dirEntry); 101 zos.closeEntry(); 102 } 103 String filePath = f.getPath(); 104 File file = new File(filePath); 105 zipDir(file, relativePath + f.getName() + "/", zos, false); 106 } else { 107 String path = relativePath + f.getName(); 108 if (!path.equals(JarFile.MANIFEST_NAME)) { 109 ZipEntry anEntry = new ZipEntry(path); 110 copyToZipStream(f, anEntry, zos); 111 } 112 } 113 } 114 } 115 } 116 117 private static void createJar(File dir, File jarFile) throws IOException { 118 Preconditions.checkNotNull(dir, "dir"); 119 Preconditions.checkNotNull(jarFile, "jarFile"); 120 File jarDir = jarFile.getParentFile(); 121 if (!jarDir.exists()) { 122 if (!jarDir.mkdirs()) { 123 throw new IOException(MessageFormat.format("could not create dir [{0}]", jarDir)); 124 } 125 } 126 try (FileOutputStream fos = new FileOutputStream(jarFile); 127 JarOutputStream jos = new JarOutputStream(fos)) { 128 jarDir(dir, "", jos); 129 } 130 } 131 132 /** 133 * Returns the full path to the Jar containing the class. It always return a JAR. 134 * @param klass class. 135 * @return path to the Jar containing the class. 136 */ 137 public static String getJar(Class klass) { 138 Preconditions.checkNotNull(klass, "klass"); 139 ClassLoader loader = klass.getClassLoader(); 140 if (loader != null) { 141 String class_file = klass.getName().replaceAll("\\.", "/") + ".class"; 142 try { 143 for (Enumeration itr = loader.getResources(class_file); itr.hasMoreElements();) { 144 URL url = (URL) itr.nextElement(); 145 String path = url.getPath(); 146 if (path.startsWith("file:")) { 147 path = path.substring("file:".length()); 148 } 149 path = URLDecoder.decode(path, "UTF-8"); 150 if ("jar".equals(url.getProtocol())) { 151 path = URLDecoder.decode(path, "UTF-8"); 152 return path.replaceAll("!.*$", ""); 153 } else if ("file".equals(url.getProtocol())) { 154 String klassName = klass.getName(); 155 klassName = klassName.replace(".", "/") + ".class"; 156 path = path.substring(0, path.length() - klassName.length()); 157 File baseDir = new File(path); 158 File testDir = new File(System.getProperty("test.build.dir", "target/test-dir")); 159 testDir = testDir.getAbsoluteFile(); 160 if (!testDir.exists()) { 161 testDir.mkdirs(); 162 } 163 File tempJar = File.createTempFile("hadoop-", "", testDir); 164 tempJar = new File(tempJar.getAbsolutePath() + ".jar"); 165 tempJar.deleteOnExit(); 166 createJar(baseDir, tempJar); 167 return tempJar.getAbsolutePath(); 168 } 169 } 170 } catch (IOException e) { 171 throw new RuntimeException(e); 172 } 173 } 174 return null; 175 } 176 177 private JarFinder() { 178 } 179}