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, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.util;
19  
20  import static org.junit.Assert.assertTrue;
21  
22  import java.io.BufferedWriter;
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.FileOutputStream;
26  import java.io.FileWriter;
27  import java.util.ArrayList;
28  import java.util.List;
29  import java.util.jar.JarEntry;
30  import java.util.jar.JarOutputStream;
31  import java.util.jar.Manifest;
32  
33  import javax.tools.JavaCompiler;
34  import javax.tools.JavaFileObject;
35  import javax.tools.StandardJavaFileManager;
36  import javax.tools.ToolProvider;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.apache.hadoop.conf.Configuration;
41  import org.apache.hadoop.fs.Path;
42  
43  /**
44   * Some utilities to help class loader testing
45   */
46  public class ClassLoaderTestHelper {
47    private static final Log LOG = LogFactory.getLog(ClassLoaderTestHelper.class);
48  
49    private static final int BUFFER_SIZE = 4096;
50  
51    /**
52     * Jar a list of files into a jar archive.
53     *
54     * @param archiveFile the target jar archive
55     * @param tobejared a list of files to be jared
56     */
57    private static boolean createJarArchive(File archiveFile, File[] tobeJared) {
58      try {
59        byte buffer[] = new byte[BUFFER_SIZE];
60        // Open archive file
61        FileOutputStream stream = new FileOutputStream(archiveFile);
62        JarOutputStream out = new JarOutputStream(stream, new Manifest());
63  
64        for (int i = 0; i < tobeJared.length; i++) {
65          if (tobeJared[i] == null || !tobeJared[i].exists()
66              || tobeJared[i].isDirectory()) {
67            continue;
68          }
69  
70          // Add archive entry
71          JarEntry jarAdd = new JarEntry(tobeJared[i].getName());
72          jarAdd.setTime(tobeJared[i].lastModified());
73          out.putNextEntry(jarAdd);
74  
75          // Write file to archive
76          FileInputStream in = new FileInputStream(tobeJared[i]);
77          while (true) {
78            int nRead = in.read(buffer, 0, buffer.length);
79            if (nRead <= 0)
80              break;
81            out.write(buffer, 0, nRead);
82          }
83          in.close();
84        }
85        out.close();
86        stream.close();
87        LOG.info("Adding classes to jar file completed");
88        return true;
89      } catch (Exception ex) {
90        LOG.error("Error: " + ex.getMessage());
91        return false;
92      }
93    }
94  
95    /**
96     * Create a test jar for testing purpose for a given class
97     * name with specified code string: save the class to a file,
98     * compile it, and jar it up. If the code string passed in is
99     * null, a bare empty class will be created and used.
100    *
101    * @param testDir the folder under which to store the test class and jar
102    * @param className the test class name
103    * @param code the optional test class code, which can be null.
104    * If null, a bare empty class will be used
105    * @return the test jar file generated
106    */
107   public static File buildJar(String testDir,
108       String className, String code) throws Exception {
109     return buildJar(testDir, className, code, testDir);
110   }
111 
112   /**
113    * Create a test jar for testing purpose for a given class
114    * name with specified code string.
115    *
116    * @param testDir the folder under which to store the test class
117    * @param className the test class name
118    * @param code the optional test class code, which can be null.
119    * If null, an empty class will be used
120    * @param folder the folder under which to store the generated jar
121    * @return the test jar file generated
122    */
123   public static File buildJar(String testDir,
124       String className, String code, String folder) throws Exception {
125     String javaCode = code != null ? code : "public class " + className + " {}";
126     Path srcDir = new Path(testDir, "src");
127     File srcDirPath = new File(srcDir.toString());
128     srcDirPath.mkdirs();
129     File sourceCodeFile = new File(srcDir.toString(), className + ".java");
130     BufferedWriter bw = new BufferedWriter(new FileWriter(sourceCodeFile));
131     bw.write(javaCode);
132     bw.close();
133 
134     // compile it by JavaCompiler
135     JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
136     ArrayList<String> srcFileNames = new ArrayList<String>();
137     srcFileNames.add(sourceCodeFile.toString());
138     StandardJavaFileManager fm = compiler.getStandardFileManager(null, null,
139       null);
140     Iterable<? extends JavaFileObject> cu =
141       fm.getJavaFileObjects(sourceCodeFile);
142     List<String> options = new ArrayList<String>();
143     options.add("-classpath");
144     // only add hbase classes to classpath. This is a little bit tricky: assume
145     // the classpath is {hbaseSrc}/target/classes.
146     String currentDir = new File(".").getAbsolutePath();
147     String classpath = currentDir + File.separator + "target"+ File.separator
148       + "classes" + System.getProperty("path.separator")
149       + System.getProperty("java.class.path") + System.getProperty("path.separator")
150       + System.getProperty("surefire.test.class.path");
151 
152     options.add(classpath);
153     LOG.debug("Setting classpath to: " + classpath);
154 
155     JavaCompiler.CompilationTask task = compiler.getTask(null, fm, null,
156       options, null, cu);
157     assertTrue("Compile file " + sourceCodeFile + " failed.", task.call());
158 
159     // build a jar file by the classes files
160     String jarFileName = className + ".jar";
161     File jarFile = new File(folder, jarFileName);
162     jarFile.getParentFile().mkdirs();
163     if (!createJarArchive(jarFile,
164         new File[]{new File(srcDir.toString(), className + ".class")})){
165       assertTrue("Build jar file failed.", false);
166     }
167     return jarFile;
168   }
169 
170   /**
171    * Add a list of jar files to another jar file under a specific folder.
172    * It is used to generated coprocessor jar files which can be loaded by
173    * the coprocessor class loader.  It is for testing usage only so we
174    * don't be so careful about stream closing in case any exception.
175    *
176    * @param targetJar the target jar file
177    * @param libPrefix the folder where to put inner jar files
178    * @param srcJars the source inner jar files to be added
179    * @throws Exception if anything doesn't work as expected
180    */
181   public static void addJarFilesToJar(File targetJar,
182       String libPrefix, File... srcJars) throws Exception {
183     FileOutputStream stream = new FileOutputStream(targetJar);
184     JarOutputStream out = new JarOutputStream(stream, new Manifest());
185     byte buffer[] = new byte[BUFFER_SIZE];
186 
187     for (File jarFile: srcJars) {
188       // Add archive entry
189       JarEntry jarAdd = new JarEntry(libPrefix + jarFile.getName());
190       jarAdd.setTime(jarFile.lastModified());
191       out.putNextEntry(jarAdd);
192 
193       // Write file to archive
194       FileInputStream in = new FileInputStream(jarFile);
195       while (true) {
196         int nRead = in.read(buffer, 0, buffer.length);
197         if (nRead <= 0)
198           break;
199         out.write(buffer, 0, nRead);
200       }
201       in.close();
202     }
203     out.close();
204     stream.close();
205     LOG.info("Adding jar file to outer jar file completed");
206   }
207 
208   static String localDirPath(Configuration conf) {
209     return conf.get(ClassLoaderBase.LOCAL_DIR_KEY)
210       + File.separator + "jars" + File.separator;
211   }
212 
213 }