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.hadoopbackport;
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(InputStream is, ZipEntry entry,
50                                ZipOutputStream zos) throws IOException {
51      zos.putNextEntry(entry);
52      byte[] arr = new byte[4096];
53      int read = is.read(arr);
54      while (read > -1) {
55        zos.write(arr, 0, read);
56        read = is.read(arr);
57      }
58      is.close();
59      zos.closeEntry();
60    }
61  
62    public static void jarDir(File dir, String relativePath, ZipOutputStream zos)
63      throws IOException {
64      Preconditions.checkNotNull(relativePath, "relativePath");
65      Preconditions.checkNotNull(zos, "zos");
66  
67      // by JAR spec, if there is a manifest, it must be the first entry in the
68      // ZIP.
69      File manifestFile = new File(dir, JarFile.MANIFEST_NAME);
70      ZipEntry manifestEntry = new ZipEntry(JarFile.MANIFEST_NAME);
71      if (!manifestFile.exists()) {
72        zos.putNextEntry(manifestEntry);
73        new Manifest().write(new BufferedOutputStream(zos));
74        zos.closeEntry();
75      } else {
76        InputStream is = new FileInputStream(manifestFile);
77        copyToZipStream(is, manifestEntry, zos);
78      }
79      zos.closeEntry();
80      zipDir(dir, relativePath, zos, true);
81      zos.close();
82    }
83  
84    private static void zipDir(File dir, String relativePath, ZipOutputStream zos,
85                               boolean start) throws IOException {
86      String[] dirList = dir.list();
87      for (String aDirList : dirList) {
88        File f = new File(dir, aDirList);
89        if (!f.isHidden()) {
90          if (f.isDirectory()) {
91            if (!start) {
92              ZipEntry dirEntry = new ZipEntry(relativePath + f.getName() + "/");
93              zos.putNextEntry(dirEntry);
94              zos.closeEntry();
95            }
96            String filePath = f.getPath();
97            File file = new File(filePath);
98            zipDir(file, relativePath + f.getName() + "/", zos, false);
99          }
100         else {
101           String path = relativePath + f.getName();
102           if (!path.equals(JarFile.MANIFEST_NAME)) {
103             ZipEntry anEntry = new ZipEntry(path);
104             InputStream is = new FileInputStream(f);
105             copyToZipStream(is, anEntry, zos);
106           }
107         }
108       }
109     }
110   }
111 
112   private static void createJar(File dir, File jarFile) throws IOException {
113     Preconditions.checkNotNull(dir, "dir");
114     Preconditions.checkNotNull(jarFile, "jarFile");
115     File jarDir = jarFile.getParentFile();
116     if (!jarDir.exists()) {
117       if (!jarDir.mkdirs()) {
118         throw new IOException(MessageFormat.format("could not create dir [{0}]",
119                                                    jarDir));
120       }
121     }
122     JarOutputStream zos = new JarOutputStream(new FileOutputStream(jarFile));
123     jarDir(dir, "", zos);
124   }
125 
126   /**
127    * Returns the full path to the Jar containing the class. It always return a
128    * JAR.
129    *
130    * @param klass class.
131    *
132    * @return path to the Jar containing the class.
133    */
134   public static String getJar(Class klass) {
135     Preconditions.checkNotNull(klass, "klass");
136     ClassLoader loader = klass.getClassLoader();
137     if (loader != null) {
138       String class_file = klass.getName().replaceAll("\\.", "/") + ".class";
139       try {
140         for (Enumeration itr = loader.getResources(class_file);
141              itr.hasMoreElements(); ) {
142           URL url = (URL) itr.nextElement();
143           String path = url.getPath();
144           if (path.startsWith("file:")) {
145             path = path.substring("file:".length());
146           }
147           path = URLDecoder.decode(path, "UTF-8");
148           if ("jar".equals(url.getProtocol())) {
149             path = URLDecoder.decode(path, "UTF-8");
150             return path.replaceAll("!.*$", "");
151           }
152           else if ("file".equals(url.getProtocol())) {
153             String klassName = klass.getName();
154             klassName = klassName.replace(".", "/") + ".class";
155             path = path.substring(0, path.length() - klassName.length());
156             File baseDir = new File(path);
157             File testDir = new File(System.getProperty("test.build.dir", "target/test-dir"));
158             testDir = testDir.getAbsoluteFile();
159             if (!testDir.exists()) {
160               testDir.mkdirs();
161             }
162             File tempJar = File.createTempFile("hadoop-", "", testDir);
163             tempJar = new File(tempJar.getAbsolutePath() + ".jar");
164             createJar(baseDir, tempJar);
165             return tempJar.getAbsolutePath();
166           }
167         }
168       }
169       catch (IOException e) {
170         throw new RuntimeException(e);
171       }
172     }
173     return null;
174   }
175 }