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      for (String aDirList : dirList) {
94        File f = new File(dir, aDirList);
95        if (!f.isHidden()) {
96          if (f.isDirectory()) {
97            if (!start) {
98              ZipEntry dirEntry = new ZipEntry(relativePath + f.getName() + "/");
99              zos.putNextEntry(dirEntry);
100             zos.closeEntry();
101           }
102           String filePath = f.getPath();
103           File file = new File(filePath);
104           zipDir(file, relativePath + f.getName() + "/", zos, false);
105         }
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}]",
124                                                    jarDir));
125       }
126     }
127     JarOutputStream zos = new JarOutputStream(new FileOutputStream(jarFile));
128     jarDir(dir, "", zos);
129   }
130 
131   /**
132    * Returns the full path to the Jar containing the class. It always return a
133    * JAR.
134    *
135    * @param klass class.
136    *
137    * @return path to the Jar containing the class.
138    */
139   public static String getJar(Class klass) {
140     Preconditions.checkNotNull(klass, "klass");
141     ClassLoader loader = klass.getClassLoader();
142     if (loader != null) {
143       String class_file = klass.getName().replaceAll("\\.", "/") + ".class";
144       try {
145         for (Enumeration itr = loader.getResources(class_file);
146              itr.hasMoreElements(); ) {
147           URL url = (URL) itr.nextElement();
148           String path = url.getPath();
149           if (path.startsWith("file:")) {
150             path = path.substring("file:".length());
151           }
152           path = URLDecoder.decode(path, "UTF-8");
153           if ("jar".equals(url.getProtocol())) {
154             path = URLDecoder.decode(path, "UTF-8");
155             return path.replaceAll("!.*$", "");
156           }
157           else if ("file".equals(url.getProtocol())) {
158             String klassName = klass.getName();
159             klassName = klassName.replace(".", "/") + ".class";
160             path = path.substring(0, path.length() - klassName.length());
161             File baseDir = new File(path);
162             File testDir = new File(System.getProperty("test.build.dir", "target/test-dir"));
163             testDir = testDir.getAbsoluteFile();
164             if (!testDir.exists()) {
165               testDir.mkdirs();
166             }
167             File tempJar = File.createTempFile("hadoop-", "", testDir);
168             tempJar = new File(tempJar.getAbsolutePath() + ".jar");
169             tempJar.deleteOnExit();
170             createJar(baseDir, tempJar);
171             return tempJar.getAbsolutePath();
172           }
173         }
174       }
175       catch (IOException e) {
176         throw new RuntimeException(e);
177       }
178     }
179     return null;
180   }
181 }