Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 41EC1200D24 for ; Tue, 10 Oct 2017 02:51:43 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 405DB1609E0; Tue, 10 Oct 2017 00:51:43 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id BA41A160BE3 for ; Tue, 10 Oct 2017 02:51:41 +0200 (CEST) Received: (qmail 78599 invoked by uid 500); 10 Oct 2017 00:51:39 -0000 Mailing-List: contact commits-help@hive.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: hive-dev@hive.apache.org Delivered-To: mailing list commits@hive.apache.org Received: (qmail 76854 invoked by uid 99); 10 Oct 2017 00:51:37 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 10 Oct 2017 00:51:37 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id E8F11F5D8A; Tue, 10 Oct 2017 00:51:35 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: sershe@apache.org To: commits@hive.apache.org Date: Tue, 10 Oct 2017 00:51:55 -0000 Message-Id: <7aaf3997506d441a97fe815ba1e7e1f5@git.apache.org> In-Reply-To: <97334c38fc1c41faafc6b13d11b4c2f2@git.apache.org> References: <97334c38fc1c41faafc6b13d11b4c2f2@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [21/61] [abbrv] hive git commit: HIVE-15053: Beeline#addlocaldriver - reduce classpath scanning (Zoltan Haindrich, reviewed by Ashutosh Chauhan) archived-at: Tue, 10 Oct 2017 00:51:43 -0000 HIVE-15053: Beeline#addlocaldriver - reduce classpath scanning (Zoltan Haindrich, reviewed by Ashutosh Chauhan) Signed-off-by: Zoltan Haindrich Project: http://git-wip-us.apache.org/repos/asf/hive/repo Commit: http://git-wip-us.apache.org/repos/asf/hive/commit/4cc0771d Tree: http://git-wip-us.apache.org/repos/asf/hive/tree/4cc0771d Diff: http://git-wip-us.apache.org/repos/asf/hive/diff/4cc0771d Branch: refs/heads/hive-14535 Commit: 4cc0771d180299c917bf2adc60493fba8c9c9efd Parents: 71004d2 Author: Zoltan Haindrich Authored: Fri Oct 6 11:11:28 2017 +0200 Committer: Zoltan Haindrich Committed: Fri Oct 6 11:11:28 2017 +0200 ---------------------------------------------------------------------- .../java/org/apache/hive/beeline/BeeLine.java | 110 +------- .../apache/hive/beeline/ClassNameCompleter.java | 254 ------------------- .../hive/beeline/TestBeelineArgParsing.java | 13 +- .../hive/beeline/TestClassNameCompleter.java | 76 ------ .../apache/hive/common/util/HiveTestUtils.java | 37 ++- 5 files changed, 47 insertions(+), 443 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/hive/blob/4cc0771d/beeline/src/java/org/apache/hive/beeline/BeeLine.java ---------------------------------------------------------------------- diff --git a/beeline/src/java/org/apache/hive/beeline/BeeLine.java b/beeline/src/java/org/apache/hive/beeline/BeeLine.java index f2c346d..649edc0 100644 --- a/beeline/src/java/org/apache/hive/beeline/BeeLine.java +++ b/beeline/src/java/org/apache/hive/beeline/BeeLine.java @@ -67,6 +67,7 @@ import java.util.ListIterator; import java.util.Map; import java.util.Properties; import java.util.ResourceBundle; +import java.util.ServiceLoader; import java.util.Set; import java.util.SortedSet; import java.util.StringTokenizer; @@ -2111,122 +2112,21 @@ public class BeeLine implements Closeable { return scanDrivers(false); } - Driver[] scanDrivers(boolean knownOnly) throws IOException { long start = System.currentTimeMillis(); - Set classNames = new HashSet(); - - if (!knownOnly) { - classNames.addAll(Arrays.asList( - ClassNameCompleter.getClassNames())); - } - - classNames.addAll(KNOWN_DRIVERS); - - Set driverClasses = new HashSet(); + ServiceLoader sqlDrivers = ServiceLoader.load(Driver.class); - for (Iterator i = classNames.iterator(); i.hasNext();) { - String className = i.next().toString(); + Set driverClasses = new HashSet<>(); - if (className.toLowerCase().indexOf("driver") == -1) { - continue; - } - - try { - Class c = Class.forName(className, false, - Thread.currentThread().getContextClassLoader()); - if (!Driver.class.isAssignableFrom(c)) { - continue; - } - - if (Modifier.isAbstract(c.getModifiers())) { - continue; - } - - // now instantiate and initialize it - driverClasses.add(c.newInstance()); - } catch (Throwable t) { - } + for (Driver driver : sqlDrivers) { + driverClasses.add(driver); } info("scan complete in " + (System.currentTimeMillis() - start) + "ms"); return (Driver[]) driverClasses.toArray(new Driver[0]); } - - private Driver[] scanDriversOLD(String line) { - long start = System.currentTimeMillis(); - - Set paths = new HashSet(); - Set driverClasses = new HashSet(); - - for (StringTokenizer tok = new StringTokenizer( - System.getProperty("java.ext.dirs"), - System.getProperty("path.separator")); tok.hasMoreTokens();) { - File[] files = new File(tok.nextToken()).listFiles(); - for (int i = 0; files != null && i < files.length; i++) { - paths.add(files[i].getAbsolutePath()); - } - } - - for (StringTokenizer tok = new StringTokenizer( - System.getProperty("java.class.path"), - System.getProperty("path.separator")); tok.hasMoreTokens();) { - paths.add(new File(tok.nextToken()).getAbsolutePath()); - } - - for (Iterator i = paths.iterator(); i.hasNext();) { - File f = new File(i.next()); - output(getColorBuffer().pad(loc("scanning", f.getAbsolutePath()), 60), - false); - - try (ZipFile zf = new ZipFile(f)) { - int total = zf.size(); - int index = 0; - - for (Enumeration zfEnum = zf.entries(); zfEnum.hasMoreElements();) { - ZipEntry entry = (ZipEntry) zfEnum.nextElement(); - String name = entry.getName(); - progress(index++, total); - - if (name.endsWith(".class")) { - name = name.replace('/', '.'); - name = name.substring(0, name.length() - 6); - - try { - // check for the string "driver" in the class - // to see if we should load it. Not perfect, but - // it is far too slow otherwise. - if (name.toLowerCase().indexOf("driver") != -1) { - Class c = Class.forName(name, false, - getClass().getClassLoader()); - if (Driver.class.isAssignableFrom(c) - && !(Modifier.isAbstract( - c.getModifiers()))) { - try { - // load and initialize - Class.forName(name); - } catch (Exception e) { - } - driverClasses.add(c.newInstance()); - } - } - } catch (Throwable t) { - } - } - } - progress(total, total); - } catch (Exception e) { - } - } - - info("scan complete in " - + (System.currentTimeMillis() - start) + "ms"); - return (Driver[]) driverClasses.toArray(new Driver[0]); - } - - // ///////////////////////////////////// // ResultSet output formatting classes // ///////////////////////////////////// http://git-wip-us.apache.org/repos/asf/hive/blob/4cc0771d/beeline/src/java/org/apache/hive/beeline/ClassNameCompleter.java ---------------------------------------------------------------------- diff --git a/beeline/src/java/org/apache/hive/beeline/ClassNameCompleter.java b/beeline/src/java/org/apache/hive/beeline/ClassNameCompleter.java deleted file mode 100644 index 65ac576..0000000 --- a/beeline/src/java/org/apache/hive/beeline/ClassNameCompleter.java +++ /dev/null @@ -1,254 +0,0 @@ -/** - * Copyright (c) 2002-2006, Marc Prud'hommeaux - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with - * the distribution. - * - * Neither the name of JLine nor the names of its contributors - * may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/** - * 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.hive.beeline; - -import jline.console.completer.StringsCompleter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.IOException; -import java.io.FileInputStream; -import java.net.JarURLConnection; -import java.net.URL; -import java.net.URLClassLoader; -import java.net.URLConnection; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.ArrayList; -import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.Enumeration; -import java.util.TreeSet; -import java.util.zip.ZipInputStream; -import java.util.zip.ZipEntry; - -/** - * the completer is original provided in JLine 0.9.94 and is being removed in 2.12. Add the - * previous implement for usage of the beeline. - */ -public class ClassNameCompleter extends StringsCompleter { - - private static final Logger LOG = LoggerFactory.getLogger(ClassNameCompleter.class.getName()); - public final static String clazzFileNameExtension = ".class"; - public final static String jarFileNameExtension = ".jar"; - - public ClassNameCompleter(String... candidates) { - super(candidates); - } - - public static String[] getClassNames() throws IOException { - Set urls = new HashSet(); - - for (ClassLoader loader = Thread.currentThread().getContextClassLoader(); loader != null; - loader = loader.getParent()) { - if (!(loader instanceof URLClassLoader)) { - continue; - } - - urls.addAll(Arrays.asList(((URLClassLoader) loader).getURLs())); - } - - // Now add the URL that holds java.lang.String. This is because - // some JVMs do not report the core classes jar in the list of - // class loaders. - Class[] systemClasses = new Class[]{String.class, javax.swing.JFrame.class}; - - for (int i = 0; i < systemClasses.length; i++) { - URL classURL = systemClasses[i] - .getResource("/" + systemClasses[i].getName().replace('.', '/') + clazzFileNameExtension); - - if (classURL != null) { - URLConnection uc = classURL.openConnection(); - - if (uc instanceof JarURLConnection) { - urls.add(((JarURLConnection) uc).getJarFileURL()); - } - } - } - - Set classes = new HashSet(); - - for (Iterator i = urls.iterator(); i.hasNext(); ) { - URL url = (URL) i.next(); - try { - File file = new File(url.getFile()); - - if (file.isDirectory()) { - Set files = getClassFiles(file.getAbsolutePath(), new HashSet(), file, new int[] { 200 }); - classes.addAll(files); - - continue; - } - - if (!isJarFile(file)) { - continue; - } - - JarFile jf = new JarFile(file); - - for (Enumeration e = jf.entries(); e.hasMoreElements();) { - JarEntry entry = (JarEntry) e.nextElement(); - - if (entry == null) { - continue; - } - - String name = entry.getName(); - - if (isClazzFile(name)) { - /* only use class file */ - classes.add(name); - } else if (isJarFile(name)) { - classes.addAll(getClassNamesFromJar(name)); - } else { - continue; - } - } - } catch (IOException e) { - throw new IOException(String.format("Error reading classpath entry: %s", url), e); - } - } - - // now filter classes by changing "/" to "." and trimming the - // trailing ".class" - Set classNames = new TreeSet(); - - for (Iterator i = classes.iterator(); i.hasNext(); ) { - String name = (String) i.next(); - classNames.add(name.replace('/', '.').substring(0, name.length() - 6)); - } - - return (String[]) classNames.toArray(new String[classNames.size()]); - } - - private static Set getClassFiles(String root, Set holder, File directory, int[] maxDirectories) { - // we have passed the maximum number of directories to scan - if (maxDirectories[0]-- < 0) { - return holder; - } - - File[] files = directory.listFiles(); - - for (int i = 0; (files != null) && (i < files.length); i++) { - String name = files[i].getAbsolutePath(); - - if (!(name.startsWith(root))) { - continue; - } else if (files[i].isDirectory()) { - getClassFiles(root, holder, files[i], maxDirectories); - } else if (files[i].getName().endsWith(clazzFileNameExtension)) { - holder.add(files[i].getAbsolutePath(). - substring(root.length() + 1)); - } - } - - return holder; - } - - /** - * Get clazz names from a jar file path - * @param path specifies the jar file's path - * @return - */ - private static List getClassNamesFromJar(String path) { - List classNames = new ArrayList(); - ZipInputStream zip = null; - try { - zip = new ZipInputStream(new FileInputStream(path)); - ZipEntry entry = zip.getNextEntry(); - while (entry != null) { - if (!entry.isDirectory() && entry.getName().endsWith(clazzFileNameExtension)) { - StringBuilder className = new StringBuilder(); - for (String part : entry.getName().split("/")) { - if (className.length() != 0) { - className.append("."); - } - className.append(part); - if (part.endsWith(clazzFileNameExtension)) { - className.setLength(className.length() - clazzFileNameExtension.length()); - } - } - classNames.add(className.toString()); - } - entry = zip.getNextEntry(); - } - } catch (IOException e) { - LOG.error("Fail to parse the class name from the Jar file due to the exception:" + e); - } finally { - if (zip != null) { - try { - zip.close(); - } catch (IOException e) { - LOG.error("Fail to close the file due to the exception:" + e); - } - } - } - - return classNames; - } - - private static boolean isJarFile(File file) { - return (file != null && file.isFile() && isJarFile(file.getName())); - } - - private static boolean isJarFile(String fileName) { - return fileName.endsWith(jarFileNameExtension); - } - - private static boolean isClazzFile(String clazzName) { - return clazzName.endsWith(clazzFileNameExtension); - } -} http://git-wip-us.apache.org/repos/asf/hive/blob/4cc0771d/beeline/src/test/org/apache/hive/beeline/TestBeelineArgParsing.java ---------------------------------------------------------------------- diff --git a/beeline/src/test/org/apache/hive/beeline/TestBeelineArgParsing.java b/beeline/src/test/org/apache/hive/beeline/TestBeelineArgParsing.java index 2884cc8..379dcfd 100644 --- a/beeline/src/test/org/apache/hive/beeline/TestBeelineArgParsing.java +++ b/beeline/src/test/org/apache/hive/beeline/TestBeelineArgParsing.java @@ -22,7 +22,12 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertTrue; + import java.io.File; import java.io.FileOutputStream; import java.io.PrintStream; @@ -92,10 +97,13 @@ public class TestBeelineArgParsing { } } - @Parameters public static Collection data() throws IOException, InterruptedException { + @Parameters(name="{1}") + public static Collection data() throws IOException, InterruptedException { // generate the dummy driver by using txt file String u = HiveTestUtils.getFileFromClasspath("DummyDriver.txt"); - File jarFile = HiveTestUtils.genLocalJarForTest(u, dummyDriverClazzName); + Map extraContent=new HashMap<>(); + extraContent.put(new File("META-INF/services/java.sql.Driver"), dummyDriverClazzName); + File jarFile = HiveTestUtils.genLocalJarForTest(u, dummyDriverClazzName, extraContent); String pathToDummyDriver = jarFile.getAbsolutePath(); return Arrays.asList(new Object[][] { { "jdbc:postgresql://host:5432/testdb", "org.postgresql.Driver", @@ -284,6 +292,7 @@ public class TestBeelineArgParsing { TestBeeline bl = new TestBeeline(); LOG.info("Add " + driverJarFileName + " for the driver class " + driverClazzName); + assertTrue("expected to exists: "+driverJarFileName,new File(driverJarFileName).exists()); bl.addLocalJar(driverJarFileName); if (!defaultSupported) { Assert.assertNull(bl.findLocalDriver(connectionString)); http://git-wip-us.apache.org/repos/asf/hive/blob/4cc0771d/beeline/src/test/org/apache/hive/beeline/TestClassNameCompleter.java ---------------------------------------------------------------------- diff --git a/beeline/src/test/org/apache/hive/beeline/TestClassNameCompleter.java b/beeline/src/test/org/apache/hive/beeline/TestClassNameCompleter.java deleted file mode 100644 index 1999937..0000000 --- a/beeline/src/test/org/apache/hive/beeline/TestClassNameCompleter.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * 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.hive.beeline; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.net.URLClassLoader; - -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import static org.junit.Assert.*; - -public class TestClassNameCompleter { - - @ClassRule - public static TemporaryFolder tmpFolder = new TemporaryFolder(); - - @Test - public void addingAndEmptyJarFile() throws IOException { - - String fileName = "empty.file.jar"; - File p = tmpFolder.newFile(fileName); - - URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader(); - try { - URLClassLoader newClassLoader = new URLClassLoader(new URL[] { p.toURL() }, classLoader); - - Thread.currentThread().setContextClassLoader(newClassLoader); - ClassNameCompleter.getClassNames(); - fail("an exception was expected!"); - } catch (IOException e) { - assertTrue("Exception message should contain the filename!", - e.getMessage().indexOf(fileName) >= 0); - } finally { - Thread.currentThread().setContextClassLoader(classLoader); - } - - } - - @Test - public void addingEmptyFile() throws IOException { - - String fileName = "empty.file"; - File p = tmpFolder.newFile(fileName); - - URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader(); - try { - URLClassLoader newClassLoader = new URLClassLoader(new URL[] { p.toURL() }, classLoader); - - Thread.currentThread().setContextClassLoader(newClassLoader); - ClassNameCompleter.getClassNames(); - } finally { - Thread.currentThread().setContextClassLoader(classLoader); - } - - } -} http://git-wip-us.apache.org/repos/asf/hive/blob/4cc0771d/common/src/java/org/apache/hive/common/util/HiveTestUtils.java ---------------------------------------------------------------------- diff --git a/common/src/java/org/apache/hive/common/util/HiveTestUtils.java b/common/src/java/org/apache/hive/common/util/HiveTestUtils.java index 88b9f81..abb31cf 100644 --- a/common/src/java/org/apache/hive/common/util/HiveTestUtils.java +++ b/common/src/java/org/apache/hive/common/util/HiveTestUtils.java @@ -20,17 +20,25 @@ package org.apache.hive.common.util; import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; -import com.google.common.io.Files; - +import org.apache.commons.io.IOUtils; import org.apache.hadoop.hive.common.classification.InterfaceAudience; import org.apache.hadoop.hive.common.classification.InterfaceStability; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.io.Files; + @InterfaceAudience.Private @InterfaceStability.Unstable public class HiveTestUtils { @@ -70,16 +78,33 @@ public class HiveTestUtils { public static File genLocalJarForTest(String pathToClazzFile, String clazzName) throws IOException, InterruptedException { + return genLocalJarForTest(pathToClazzFile, clazzName, new HashMap()); + } + + public static File genLocalJarForTest(String pathToClazzFile, String clazzName, MapextraContent) + throws IOException, InterruptedException { String u = pathToClazzFile; File dir = new File(u); File parentDir = dir.getParentFile(); File f = new File(parentDir, clazzName + JAVA_FILE_EXT); Files.copy(dir, f); executeCmd(new String[] { "javac", clazzName + JAVA_FILE_EXT }, parentDir); - executeCmd(new String[] { "jar", "cf", clazzName + JAR_FILE_EXT, clazzName + CLAZZ_FILE_EXT }, - parentDir); f.delete(); - new File(parentDir, clazzName + CLAZZ_FILE_EXT).delete(); - return new File(parentDir, clazzName + JAR_FILE_EXT); + + File outputJar=new File(parentDir, clazzName + JAR_FILE_EXT); + ZipOutputStream zos=new ZipOutputStream(new FileOutputStream(outputJar)); + String contentClassName = clazzName + CLAZZ_FILE_EXT; + zos.putNextEntry(new ZipEntry(contentClassName)); + IOUtils.copy(new FileInputStream(new File(parentDir,contentClassName)), zos); + zos.closeEntry(); + + for (Entry entry : extraContent.entrySet()) { + zos.putNextEntry(new ZipEntry(entry.getKey().toString())); + zos.write(entry.getValue().getBytes()); + zos.closeEntry(); + } + zos.close(); + new File(parentDir, contentClassName).delete(); + return outputJar; } }