/* * 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.accumulo.start; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Collections; import java.util.Map; import java.util.ServiceLoader; import java.util.TreeMap; import java.util.TreeSet; import org.apache.accumulo.start.classloader.AccumuloClassLoader; import org.apache.accumulo.start.spi.KeywordExecutable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Main { private static final Logger log = LoggerFactory.getLogger(Main.class); private static ClassLoader classLoader; private static Class<?> vfsClassLoader; private static Map<String,KeywordExecutable> servicesMap; public static void main(final String[] args) { try { // Preload classes that cause a deadlock between the ServiceLoader and the DFSClient when using // the VFSClassLoader with jars in HDFS. ClassLoader loader = getClassLoader(); Class<?> confClass = null; try { confClass = AccumuloClassLoader.getClassLoader().loadClass("org.apache.hadoop.conf.Configuration"); } catch (ClassNotFoundException e) { log.error("Unable to find Hadoop Configuration class on classpath, check configuration.", e); System.exit(1); } Object conf = null; try { conf = confClass.newInstance(); } catch (Exception e) { log.error("Error creating new instance of Hadoop Configuration", e); System.exit(1); } try { Method getClassByNameOrNullMethod = conf.getClass().getMethod("getClassByNameOrNull", String.class); getClassByNameOrNullMethod.invoke(conf, "org.apache.hadoop.mapred.JobConf"); getClassByNameOrNullMethod.invoke(conf, "org.apache.hadoop.mapred.JobConfigurable"); } catch (Exception e) { log.error("Error pre-loading JobConf and JobConfigurable classes, VFS classloader with " + "system classes in HDFS may not work correctly", e); System.exit(1); } if (args.length == 0) { printUsage(); System.exit(1); } if (args[0].equals("-h") || args[0].equals("-help") || args[0].equals("--help")) { printUsage(); System.exit(1); } // determine whether a keyword was used or a class name, and execute it with the remaining args String keywordOrClassName = args[0]; KeywordExecutable keywordExec = getExecutables(loader).get(keywordOrClassName); if (keywordExec != null) { execKeyword(keywordExec, stripArgs(args, 1)); } else { execMainClassName(keywordOrClassName, stripArgs(args, 1)); } } catch (Throwable t) { log.error("Uncaught exception", t); System.exit(1); } } public static synchronized ClassLoader getClassLoader() { if (classLoader == null) { try { ClassLoader clTmp = (ClassLoader) getVFSClassLoader().getMethod("getClassLoader").invoke(null); classLoader = clTmp; Thread.currentThread().setContextClassLoader(classLoader); } catch (ClassNotFoundException | IOException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { log.error("Problem initializing the class loader", e); System.exit(1); } } return classLoader; } public static synchronized Class<?> getVFSClassLoader() throws IOException, ClassNotFoundException { if (vfsClassLoader == null) { Thread.currentThread().setContextClassLoader(AccumuloClassLoader.getClassLoader()); Class<?> vfsClassLoaderTmp = AccumuloClassLoader.getClassLoader().loadClass("org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader"); vfsClassLoader = vfsClassLoaderTmp; } return vfsClassLoader; } private static void execKeyword(final KeywordExecutable keywordExec, final String[] args) { Runnable r = new Runnable() { @Override public void run() { try { keywordExec.execute(args); } catch (Exception e) { die(e); } } }; startThread(r, keywordExec.keyword()); } private static void execMainClassName(final String className, final String[] args) { Class<?> classWithMain = null; try { classWithMain = getClassLoader().loadClass(className); } catch (ClassNotFoundException cnfe) { System.out.println("Invalid argument: Java <main class> '" + className + "' was not found. Please use the wholly qualified package name."); printUsage(); System.exit(1); } execMainClass(classWithMain, args); } public static void execMainClass(final Class<?> classWithMain, final String[] args) { Method main = null; try { main = classWithMain.getMethod("main", args.getClass()); } catch (Throwable t) { log.error("Could not run main method on '" + classWithMain.getName() + "'.", t); } if (main == null || !Modifier.isPublic(main.getModifiers()) || !Modifier.isStatic(main.getModifiers())) { System.out.println(classWithMain.getName() + " must implement a public static void main(String args[]) method"); System.exit(1); } final Method finalMain = main; Runnable r = new Runnable() { @Override public void run() { try { final Object thisIsJustOneArgument = args; finalMain.invoke(null, thisIsJustOneArgument); } catch (InvocationTargetException e) { if (e.getCause() != null) { die(e.getCause()); } else { // Should never happen, but check anyway. die(e); } } catch (Exception e) { die(e); } } }; startThread(r, classWithMain.getName()); } public static String[] stripArgs(final String[] originalArgs, int numToStrip) { int newSize = originalArgs.length - numToStrip; String newArgs[] = new String[newSize]; System.arraycopy(originalArgs, numToStrip, newArgs, 0, newSize); return newArgs; } private static void startThread(final Runnable r, final String name) { Thread t = new Thread(r, name); t.setContextClassLoader(getClassLoader()); t.start(); } /** * Print a stack trace to stderr and exit with a non-zero status. * * @param t * The {@link Throwable} containing a stack trace to print. */ private static void die(final Throwable t) { log.error("Thread '" + Thread.currentThread().getName() + "' died.", t); System.exit(1); } public static void printCommand(KeywordExecutable ke) { System.out.printf(" %-30s %s\n", ke.usage(), ke.description()); } public static void printUsage() { Map<String,KeywordExecutable> executableMap = new TreeMap<>(getExecutables(getClassLoader())); System.out.println("\nUsage: accumulo <command> [-h] (<argument> ...)\n\n -h Prints usage for specified command\n\nCore Commands:"); for (String cmd : Arrays.asList("init", "shell", "classpath", "version", "admin", "info", "help")) { printCommand(executableMap.remove(cmd)); } System.out.println(" <main class> args Runs Java <main class> located on Accumulo classpath"); System.out.println("\nProcess Commands:"); for (String cmd : Arrays.asList("gc", "master", "monitor", "minicluster", "proxy", "tserver", "tracer", "zookeeper")) { printCommand(executableMap.remove(cmd)); } System.out.println("\nAdvanced Commands:"); for (Map.Entry<String,KeywordExecutable> entry : executableMap.entrySet()) { printCommand(entry.getValue()); } System.out.println(); } public static synchronized Map<String,KeywordExecutable> getExecutables(final ClassLoader cl) { if (servicesMap == null) { servicesMap = checkDuplicates(ServiceLoader.load(KeywordExecutable.class, cl)); } return servicesMap; } public static Map<String,KeywordExecutable> checkDuplicates(final Iterable<? extends KeywordExecutable> services) { TreeSet<String> blacklist = new TreeSet<>(); TreeMap<String,KeywordExecutable> results = new TreeMap<>(); for (KeywordExecutable service : services) { String keyword = service.keyword(); if (blacklist.contains(keyword)) { // subsequent times a duplicate is found, just warn and exclude it warnDuplicate(service); } else if (results.containsKey(keyword)) { // the first time a duplicate is found, blacklist it and warn blacklist.add(keyword); warnDuplicate(results.remove(keyword)); warnDuplicate(service); } else { // first observance of this keyword, so just add it to the list results.put(service.keyword(), service); } } return Collections.unmodifiableSortedMap(results); } private static void warnDuplicate(final KeywordExecutable service) { log.warn("Ambiguous duplicate binding for keyword '" + service.keyword() + "' found: " + service.getClass().getName()); } }