View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.hadoop.hbase.mapreduce;
20  
21  import com.google.common.base.Preconditions;
22  
23  import java.io.BufferedOutputStream;
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.FileOutputStream;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.net.URL;
30  import java.net.URLDecoder;
31  import java.text.MessageFormat;
32  import java.util.Enumeration;
33  import java.util.jar.JarFile;
34  import java.util.jar.JarOutputStream;
35  import java.util.jar.Manifest;
36  import java.util.zip.ZipEntry;
37  import java.util.zip.ZipOutputStream;
38  
39  /**
40   * Finds the Jar for a class. If the class is in a directory in the
41   * classpath, it creates a Jar on the fly with the contents of the directory
42   * and returns the path to that Jar. If a Jar is created, it is created in
43   * the system temporary directory.
44   *
45   * This file was forked from hadoop/common/branches/branch-2@1377176.
46   */
47  public class JarFinder {
48  
49    private static void copyToZipStream(File file, ZipEntry entry,
50                                ZipOutputStream zos) throws IOException {
51      InputStream is = new FileInputStream(file);
52      try {
53        zos.putNextEntry(entry);
54        byte[] arr = new byte[4096];
55        int read = is.read(arr);
56        while (read > -1) {
57          zos.write(arr, 0, read);
58          read = is.read(arr);
59        }
60      } finally {
61        try {
62          is.close();
63        } finally {
64          zos.closeEntry();
65        }
66      }
67    }
68  
69    public static void jarDir(File dir, String relativePath, ZipOutputStream zos)
70      throws IOException {
71      Preconditions.checkNotNull(relativePath, "relativePath");
72      Preconditions.checkNotNull(zos, "zos");
73  
74      // by JAR spec, if there is a manifest, it must be the first entry in the
75      // ZIP.
76      File manifestFile = new File(dir, JarFile.MANIFEST_NAME);
77      ZipEntry manifestEntry = new ZipEntry(JarFile.MANIFEST_NAME);
78      if (!manifestFile.exists()) {
79        zos.putNextEntry(manifestEntry);
80        new Manifest().write(new BufferedOutputStream(zos));
81        zos.closeEntry();
82      } else {
83        copyToZipStream(manifestFile, manifestEntry, zos);
84      }
85      zos.closeEntry();
86      zipDir(dir, relativePath, zos, true);
87      zos.close();
88    }
89  
90    private static void zipDir(File dir, String relativePath, ZipOutputStream zos,
91                               boolean start) throws IOException {
92      String[] dirList = dir.list();
93      if (dirList == null) {
94        return;
95      }
96      for (String aDirList : dirList) {
97        File f = new File(dir, aDirList);
98        if (!f.isHidden()) {
99          if (f.isDirectory()) {
100           if (!start) {
101             ZipEntry dirEntry = new ZipEntry(relativePath + f.getName() + "/");
102             zos.putNextEntry(dirEntry);
103             zos.closeEntry();
104           }
105           String filePath = f.getPath();
106           File file = new File(filePath);
107           zipDir(file, relativePath + f.getName() + "/", zos, false);
108         }
109         else {
110           String path = relativePath + f.getName();
111           if (!path.equals(JarFile.MANIFEST_NAME)) {
112             ZipEntry anEntry = new ZipEntry(path);
113             copyToZipStream(f, anEntry, zos);
114           }
115         }
116       }
117     }
118   }
119 
120   private static void createJar(File dir, File jarFile) throws IOException {
121     Preconditions.checkNotNull(dir, "dir");
122     Preconditions.checkNotNull(jarFile, "jarFile");
123     File jarDir = jarFile.getParentFile();
124     if (!jarDir.exists()) {
125       if (!jarDir.mkdirs()) {
126         throw new IOException(MessageFormat.format("could not create dir [{0}]",
127                                                    jarDir));
128       }
129     }
130     JarOutputStream zos = new JarOutputStream(new FileOutputStream(jarFile));
131     jarDir(dir, "", zos);
132   }
133 
134   /**
135    * Returns the full path to the Jar containing the class. It always return a
136    * JAR.
137    *
138    * @param klass class.
139    *
140    * @return path to the Jar containing the class.
141    */
142   public static String getJar(Class klass) {
143     Preconditions.checkNotNull(klass, "klass");
144     ClassLoader loader = klass.getClassLoader();
145     if (loader != null) {
146       String class_file = klass.getName().replaceAll("\\.", "/") + ".class";
147       try {
148         for (Enumeration itr = loader.getResources(class_file);
149              itr.hasMoreElements(); ) {
150           URL url = (URL) itr.nextElement();
151           String path = url.getPath();
152           if (path.startsWith("file:")) {
153             path = path.substring("file:".length());
154           }
155           path = URLDecoder.decode(path, "UTF-8");
156           if ("jar".equals(url.getProtocol())) {
157             path = URLDecoder.decode(path, "UTF-8");
158             return path.replaceAll("!.*$", "");
159           }
160           else if ("file".equals(url.getProtocol())) {
161             String klassName = klass.getName();
162             klassName = klassName.replace(".", "/") + ".class";
163             path = path.substring(0, path.length() - klassName.length());
164             File baseDir = new File(path);
165             File testDir = new File(System.getProperty("test.build.dir", "target/test-dir"));
166             testDir = testDir.getAbsoluteFile();
167             if (!testDir.exists()) {
168               testDir.mkdirs();
169             }
170             File tempJar = File.createTempFile("hadoop-", "", testDir);
171             tempJar = new File(tempJar.getAbsolutePath() + ".jar");
172             tempJar.deleteOnExit();
173             createJar(baseDir, tempJar);
174             return tempJar.getAbsolutePath();
175           }
176         }
177       }
178       catch (IOException e) {
179         throw new RuntimeException(e);
180       }
181     }
182     return null;
183   }
184 }