/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase.util; import static org.junit.Assert.assertTrue; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.util.ArrayList; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; /** * Some utilities to help class loader testing */ public class ClassLoaderTestHelper { private static final Log LOG = LogFactory.getLog(ClassLoaderTestHelper.class); private static final int BUFFER_SIZE = 4096; /** * Jar a list of files into a jar archive. * * @param archiveFile the target jar archive * @param tobejared a list of files to be jared */ private static boolean createJarArchive(File archiveFile, File[] tobeJared) { try { byte buffer[] = new byte[BUFFER_SIZE]; // Open archive file FileOutputStream stream = new FileOutputStream(archiveFile); JarOutputStream out = new JarOutputStream(stream, new Manifest()); for (int i = 0; i < tobeJared.length; i++) { if (tobeJared[i] == null || !tobeJared[i].exists() || tobeJared[i].isDirectory()) { continue; } // Add archive entry JarEntry jarAdd = new JarEntry(tobeJared[i].getName()); jarAdd.setTime(tobeJared[i].lastModified()); out.putNextEntry(jarAdd); // Write file to archive FileInputStream in = new FileInputStream(tobeJared[i]); while (true) { int nRead = in.read(buffer, 0, buffer.length); if (nRead <= 0) break; out.write(buffer, 0, nRead); } in.close(); } out.close(); stream.close(); LOG.info("Adding classes to jar file completed"); return true; } catch (Exception ex) { LOG.error("Error: " + ex.getMessage()); return false; } } /** * Create a test jar for testing purpose for a given class * name with specified code string: save the class to a file, * compile it, and jar it up. If the code string passed in is * null, a bare empty class will be created and used. * * @param testDir the folder under which to store the test class and jar * @param className the test class name * @param code the optional test class code, which can be null. * If null, a bare empty class will be used * @return the test jar file generated */ public static File buildJar(String testDir, String className, String code) throws Exception { return buildJar(testDir, className, code, testDir); } /** * Create a test jar for testing purpose for a given class * name with specified code string. * * @param testDir the folder under which to store the test class * @param className the test class name * @param code the optional test class code, which can be null. * If null, an empty class will be used * @param folder the folder under which to store the generated jar * @return the test jar file generated */ public static File buildJar(String testDir, String className, String code, String folder) throws Exception { String javaCode = code != null ? code : "public class " + className + " {}"; Path srcDir = new Path(testDir, "src"); File srcDirPath = new File(srcDir.toString()); srcDirPath.mkdirs(); File sourceCodeFile = new File(srcDir.toString(), className + ".java"); BufferedWriter bw = new BufferedWriter(new FileWriter(sourceCodeFile)); bw.write(javaCode); bw.close(); // compile it by JavaCompiler JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); ArrayList<String> srcFileNames = new ArrayList<>(1); srcFileNames.add(sourceCodeFile.toString()); StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null); Iterable<? extends JavaFileObject> cu = fm.getJavaFileObjects(sourceCodeFile); List<String> options = new ArrayList<>(2); options.add("-classpath"); // only add hbase classes to classpath. This is a little bit tricky: assume // the classpath is {hbaseSrc}/target/classes. String currentDir = new File(".").getAbsolutePath(); String classpath = currentDir + File.separator + "target"+ File.separator + "classes" + System.getProperty("path.separator") + System.getProperty("java.class.path") + System.getProperty("path.separator") + System.getProperty("surefire.test.class.path"); options.add(classpath); LOG.debug("Setting classpath to: " + classpath); JavaCompiler.CompilationTask task = compiler.getTask(null, fm, null, options, null, cu); assertTrue("Compile file " + sourceCodeFile + " failed.", task.call()); // build a jar file by the classes files String jarFileName = className + ".jar"; File jarFile = new File(folder, jarFileName); jarFile.getParentFile().mkdirs(); if (!createJarArchive(jarFile, new File[]{new File(srcDir.toString(), className + ".class")})){ assertTrue("Build jar file failed.", false); } return jarFile; } /** * Add a list of jar files to another jar file under a specific folder. * It is used to generated coprocessor jar files which can be loaded by * the coprocessor class loader. It is for testing usage only so we * don't be so careful about stream closing in case any exception. * * @param targetJar the target jar file * @param libPrefix the folder where to put inner jar files * @param srcJars the source inner jar files to be added * @throws Exception if anything doesn't work as expected */ public static void addJarFilesToJar(File targetJar, String libPrefix, File... srcJars) throws Exception { FileOutputStream stream = new FileOutputStream(targetJar); JarOutputStream out = new JarOutputStream(stream, new Manifest()); byte buffer[] = new byte[BUFFER_SIZE]; for (File jarFile: srcJars) { // Add archive entry JarEntry jarAdd = new JarEntry(libPrefix + jarFile.getName()); jarAdd.setTime(jarFile.lastModified()); out.putNextEntry(jarAdd); // Write file to archive FileInputStream in = new FileInputStream(jarFile); while (true) { int nRead = in.read(buffer, 0, buffer.length); if (nRead <= 0) break; out.write(buffer, 0, nRead); } in.close(); } out.close(); stream.close(); LOG.info("Adding jar file to outer jar file completed"); } static String localDirPath(Configuration conf) { return conf.get(ClassLoaderBase.LOCAL_DIR_KEY) + File.separator + "jars" + File.separator; } }