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