Updated Branches: refs/heads/master ed3c15737 -> d99626072
ACCUMULO-1494 Add support for accumulo jar command. Signed-off-by: Eric Newton <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/ccc46b06 Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/ccc46b06 Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/ccc46b06 Branch: refs/heads/master Commit: ccc46b06e2ff76ba5c2a40549ad6568299fdcf4f Parents: 3de0c1e Author: Bill Havanki <[email protected]> Authored: Thu Sep 19 16:18:49 2013 -0400 Committer: Eric Newton <[email protected]> Committed: Tue Sep 24 12:13:08 2013 -0400 ---------------------------------------------------------------------- bin/accumulo | 23 +++++- .../java/org/apache/accumulo/start/Main.java | 77 +++++++++++++++--- .../org/apache/accumulo/start/MainTest.java | 84 ++++++++++++++++++++ 3 files changed, 170 insertions(+), 14 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/accumulo/blob/ccc46b06/bin/accumulo ---------------------------------------------------------------------- diff --git a/bin/accumulo b/bin/accumulo index ad42d05..8e1128a 100755 --- a/bin/accumulo +++ b/bin/accumulo @@ -33,7 +33,7 @@ START_JAR="${ACCUMULO_HOME}/lib/accumulo-start.jar" # # Resolve a program to its installation directory # -locationByProgram() +locationByProgram() { RESULT=$( which "$1" ) if [[ "$?" != 0 && -z "${RESULT}" ]]; then @@ -59,6 +59,7 @@ DEFAULT_GENERAL_JAVA_OPTS="" # # ACCUMULO_XTRAJARS is where all of the commandline -add items go into for reading by accumulo. +# It also holds the JAR run with the jar command and, if possible, any items in the JAR manifest's Class-Path. # if [ "$1" = "-add" ] ; then export ACCUMULO_XTRAJARS="$2" @@ -66,6 +67,26 @@ if [ "$1" = "-add" ] ; then else export ACCUMULO_XTRAJARS="" fi +if [ "$1" = "jar" ] ; then + if [[ $2 =~ ^/ ]]; then + jardir="$(dirname "$2")" + jarfile="$2" + else + jardir="$(pwd)" + jarfile="${jardir}/${2}" + fi + if jar tf "$jarfile" | grep -q META-INF/MANIFEST.MF ; then + cp="$(unzip -p "$jarfile" META-INF/MANIFEST.MF | grep ^Class-Path: | sed 's/^Class-Path: *//')" + if [ -n "$cp" ] ; then + for j in $cp; do + if [ "$j" != "Class-Path:" ]; then + ACCUMULO_XTRAJARS="${jardir}/${j},$ACCUMULO_XTRAJARS" + fi + done + fi + fi + ACCUMULO_XTRAJARS="${jarfile},$ACCUMULO_XTRAJARS" +fi # # Add appropriate options for process type http://git-wip-us.apache.org/repos/asf/accumulo/blob/ccc46b06/start/src/main/java/org/apache/accumulo/start/Main.java ---------------------------------------------------------------------- diff --git a/start/src/main/java/org/apache/accumulo/start/Main.java b/start/src/main/java/org/apache/accumulo/start/Main.java index 0d1eeeb..7a9e76b 100644 --- a/start/src/main/java/org/apache/accumulo/start/Main.java +++ b/start/src/main/java/org/apache/accumulo/start/Main.java @@ -16,34 +16,36 @@ */ package org.apache.accumulo.start; +import java.io.IOException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; import org.apache.accumulo.start.classloader.AccumuloClassLoader; public class Main { - + public static void main(String[] args) throws Exception { Runnable r = null; - + try { if (args.length == 0) { printUsage(); System.exit(1); } - final String argsToPass[] = new String[args.length - 1]; - System.arraycopy(args, 1, argsToPass, 0, args.length - 1); - + Thread.currentThread().setContextClassLoader(AccumuloClassLoader.getClassLoader()); - + Class<?> vfsClassLoader = AccumuloClassLoader.getClassLoader().loadClass("org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader"); - + ClassLoader cl = (ClassLoader) vfsClassLoader.getMethod("getClassLoader", new Class[] {}).invoke(null, new Object[] {}); - + Class<?> runTMP = null; - + Thread.currentThread().setContextClassLoader(cl); - + if (args[0].equals("master")) { runTMP = cl.loadClass("org.apache.accumulo.server.master.Master"); } else if (args[0].equals("tserver")) { @@ -81,6 +83,22 @@ public class Main { runTMP = cl.loadClass("org.apache.accumulo.core.util.CreateToken"); } else if (args[0].equals("info")) { runTMP = cl.loadClass("org.apache.accumulo.server.util.Info"); + } else if (args[0].equals("jar")) { + if (args.length < 2) { + printUsage(); + System.exit(1); + } + try { + JarFile f = new JarFile(args[1]); + runTMP = loadClassFromJar(args, f, cl); + } catch (IOException ioe) { + System.out.println("File " + args[1] + " could not be found or read."); + System.exit(1); + } catch (ClassNotFoundException cnfe) { + System.out.println("Classname " + (args.length > 2 ? args[2] : "in JAR manifest") + + " not found. Please make sure you use the wholly qualified package name."); + System.exit(1); + } } else { try { runTMP = cl.loadClass(args[0]); @@ -99,6 +117,18 @@ public class Main { System.out.println(args[0] + " must implement a public static void main(String args[]) method"); System.exit(1); } + int chopArgsCount; + if (args[0].equals("jar")) { + if (args.length > 2 && runTMP.getName().equals(args[2])) { + chopArgsCount = 3; + } else { + chopArgsCount = 2; + } + } else { + chopArgsCount = 1; + } + String argsToPass[] = new String[args.length - chopArgsCount]; + System.arraycopy(args, chopArgsCount, argsToPass, 0, args.length - chopArgsCount); final Object thisIsJustOneArgument = argsToPass; final Method finalMain = main; r = new Runnable() { @@ -112,7 +142,7 @@ public class Main { } } }; - + Thread t = new Thread(r, args[0]); t.setContextClassLoader(cl); t.start(); @@ -122,8 +152,29 @@ public class Main { System.exit(1); } } - + private static void printUsage() { - System.out.println("accumulo init | master | tserver | monitor | shell | admin | gc | classpath | rfile-info | login-info | tracer | minicluster | proxy | zookeeper | create-token | info | version <accumulo class> args"); + System.out.println("accumulo init | master | tserver | monitor | shell | admin | gc | classpath | rfile-info | login-info | tracer | minicluster | proxy | zookeeper | create-token | info | version | jar <jar> [<main class>] args | <accumulo class> args"); + } + + // feature: will work even if main class isn't in the JAR + static Class<?> loadClassFromJar(String[] args, JarFile f, ClassLoader cl) throws IOException, ClassNotFoundException { + ClassNotFoundException explicitNotFound = null; + if (args.length >= 3) { + try { + return cl.loadClass(args[2]); // jar jar-file main-class + } catch (ClassNotFoundException cnfe) { + // assume this is the first argument, look for main class in JAR manifest + explicitNotFound = cnfe; + } + } + String mainClass = f.getManifest().getMainAttributes().getValue(Attributes.Name.MAIN_CLASS); + if (mainClass == null) { + if (explicitNotFound != null) { + throw explicitNotFound; + } + throw new ClassNotFoundException("No main class was specified, and the JAR manifest does not specify one"); + } + return cl.loadClass(mainClass); } } http://git-wip-us.apache.org/repos/asf/accumulo/blob/ccc46b06/start/src/test/java/org/apache/accumulo/start/MainTest.java ---------------------------------------------------------------------- diff --git a/start/src/test/java/org/apache/accumulo/start/MainTest.java b/start/src/test/java/org/apache/accumulo/start/MainTest.java new file mode 100644 index 0000000..02dc46d --- /dev/null +++ b/start/src/test/java/org/apache/accumulo/start/MainTest.java @@ -0,0 +1,84 @@ +package org.apache.accumulo.start; + +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; +import org.easymock.Capture; +import static org.easymock.EasyMock.*; + +public class MainTest { + private static final Class MAIN_CLASS = String.class; // arbitrary + private static final String MAIN_CLASS_NAME = MAIN_CLASS.getName(); + + private JarFile f; + private ClassLoader cl; + @Before public void setUp() { + f = createMock(JarFile.class); + cl = createMock(ClassLoader.class); + } + @Test public void testLoadClassFromJar_ExplicitMainClass() throws Exception { + String[] args = { "jar", "the.jar", "main.class", "arg1", "arg2" }; + expect(cl.loadClass("main.class")).andReturn(MAIN_CLASS); + replay(cl); + assertEquals(MAIN_CLASS, Main.loadClassFromJar(args, f, cl)); + } + @Test public void testLoadClassFromJar_ManifestMainClass() throws Exception { + String[] args = { "jar", "the.jar", "arg1", "arg2" }; + expect(cl.loadClass("arg1")).andThrow(new ClassNotFoundException()); + expect(cl.loadClass(MAIN_CLASS_NAME)).andReturn(MAIN_CLASS); + replay(cl); + mockManifestMainClass(f, MAIN_CLASS.getName()); + replay(f); + assertEquals(MAIN_CLASS, Main.loadClassFromJar(args, f, cl)); + } + @Test(expected=ClassNotFoundException.class) + public void testLoadClassFromJar_NoMainClass() throws Exception { + String[] args = { "jar", "the.jar", "arg1", "arg2" }; + expect(cl.loadClass("arg1")).andThrow(new ClassNotFoundException()); + replay(cl); + mockManifestMainClass(f, null); + replay(f); + Main.loadClassFromJar(args, f, cl); + } + @Test(expected=ClassNotFoundException.class) + public void testLoadClassFromJar_NoMainClassNoArgs() throws Exception { + String[] args = { "jar", "the.jar" }; + mockManifestMainClass(f, null); + replay(f); + Main.loadClassFromJar(args, f, cl); + } + + @Test(expected=ClassNotFoundException.class) + public void testLoadClassFromJar_ExplicitMainClass_Fail() throws Exception { + String[] args = { "jar", "the.jar", "main.class", "arg1", "arg2" }; + expect(cl.loadClass("main.class")).andThrow(new ClassNotFoundException()); + replay(cl); + mockManifestMainClass(f, null); + replay(f); + Main.loadClassFromJar(args, f, cl); + } + @Test(expected=ClassNotFoundException.class) + public void testLoadClassFromJar_ManifestMainClass_Fail() throws Exception { + String[] args = { "jar", "the.jar", "arg1", "arg2" }; + expect(cl.loadClass("arg1")).andThrow(new ClassNotFoundException()); + expect(cl.loadClass(MAIN_CLASS_NAME)).andThrow(new ClassNotFoundException()); + replay(cl); + mockManifestMainClass(f, MAIN_CLASS.getName()); + replay(f); + Main.loadClassFromJar(args, f, cl); + } + + private void mockManifestMainClass(JarFile f, String mainClassName) + throws Exception { + Manifest mf = createMock(Manifest.class); + expect(f.getManifest()).andReturn(mf); + Attributes attrs = createMock(Attributes.class); + expect(mf.getMainAttributes()).andReturn(attrs); + replay(mf); + expect(attrs.getValue(Attributes.Name.MAIN_CLASS)).andReturn(mainClassName); + replay(attrs); + } +}
