package org.solrmarc.driver; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.security.CodeSource; import java.util.Set; import org.apache.log4j.Logger; import org.solrmarc.index.indexer.IndexerSpecException; import org.solrmarc.index.utils.FastClasspathUtils; //import org.solrmarc.index.utils.ReflectionUtils; /** * @author rh9ec * */ public class Boot { private static Logger logger; static { addLibDirJarstoClassPath(); logger = Logger.getLogger(Boot.class); } /** * @param args */ public static void main(String[] args) { org.apache.log4j.BasicConfigurator.configure(); if (args.length == 0) { findExecutables(); } else { String classname = null; String[] otherArgs; if (args[0].endsWith(".properties")) { classname = "org.solrmarc.driver.ConfigDriver"; otherArgs = args; } else { otherArgs = new String[args.length - 1]; System.arraycopy(args, 1, otherArgs, 0, args.length - 1); try { classname = classnamefromArg(args[0]); } catch (ClassNotFoundException e1) { logger.fatal("ERROR: Unable to find main class for specified short name: " + args[0]); findExecutables(); System.exit(3); } } invokeMain(classname, otherArgs); } } /** * @param classname * @param otherArgs */ protected static void invokeMain(String classname, String[] otherArgs) { try { Class<?> mainClass = Class.forName(classname); Method mainMethod = mainClass.getMethod("main", String[].class); if (!Modifier.isStatic(mainMethod.getModifiers())) { logger.fatal("ERROR: Main method is not static in class: " + classname); System.exit(1); } mainMethod.invoke(null, (Object) otherArgs); } catch (IllegalAccessException | IllegalArgumentException e) { logger.fatal("ERROR: Unable to invoke main method in specified class: " + classname); logger.fatal(e); System.exit(2); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); logger.fatal("ERROR: Error while invoking main method in specified class: " + classname); logger.fatal(t); System.exit(2); } catch (ClassNotFoundException e) { logger.fatal("ERROR: Unable to find specified main class: " + classname); findExecutables(); System.exit(3); } catch (NoSuchMethodException e) { logger.fatal("ERROR: Unable to find main method in specified class: " + classname); System.exit(4); } catch (SecurityException e) { logger.fatal("ERROR: Unable to access main method in specified class: " + classname); System.exit(5); } } private static String classnamefromArg(String string) throws ClassNotFoundException { Set<Class<? extends BootableMain>> mainClasses = FastClasspathUtils.getBootableMainClasses(); for (Class<?> clazz : mainClasses) { if (string.equals(clazz.getName())) return(clazz.getName()); if (clazz.getName().endsWith("."+string)) return(clazz.getName()); } for (Class<?> clazz : mainClasses) { String shortNameStr = null; try { Field shortName = clazz.getDeclaredField("shortName"); shortNameStr = shortName.get(null).toString(); } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { // no valid short name don't worry. } if (shortNameStr != null && string.equals(shortNameStr)) return(clazz.getName()); } throw new ClassNotFoundException("can't find class"); } private static void findExecutables() { Set<Class<? extends BootableMain>> mainClasses = FastClasspathUtils.getBootableMainClasses(); logger.info("This class org.solrmarc.driver.Boot dynamically loads all of the jars in the lib directory"); logger.info("and then calls the main method of a class that requires those jars"); logger.info("The first argument provided to this class specifies the name of the class to load and execute"); logger.info("all of the subsequent arguments are then passed to that class's main method"); logger.info(""); logger.info("Known classes that can be bootstrapped in tha manner are:"); for (Class<?> mainClass : mainClasses) { try { Method mainMethod = mainClass.getMethod("main", String[].class); String shortNameStr = null; try { Field shortName = mainClass.getDeclaredField("shortName"); shortNameStr = shortName.get(null).toString(); } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { // no valid short name don't worry. } if (Modifier.isStatic(mainMethod.getModifiers())) { logger.info(" " + mainClass.getName() + ((shortNameStr != null) ? " ("+shortNameStr+")" : "")); } } catch (NoSuchMethodException e) { // no prob } } } /** * Find the location of where this class is running from * When run normally this would be the main solrmarc jar * when run from classdirs in eclipse, is is the project location * * @return String - location of where this class is running from. Used * as default search location for local configuration * files (As a side effect, sets System Property * solrmarc.jar.dir to this same value so it can be * referenced in log4j.properties) */ public static String getDefaultHomeDir() { CodeSource codeSource = Boot.class.getProtectionDomain().getCodeSource(); String jarDir; File jarFile = null; try { jarFile = new File(codeSource.getLocation().toURI().getPath()); } catch (URISyntaxException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (jarFile.getName().endsWith(".jar")) { jarDir = jarFile.getParentFile().getPath(); } else { // Not running from a jar. Probably running from eclipse or other // IDE jarDir = new File(".").getAbsoluteFile().getParentFile().getAbsolutePath(); } System.setProperty("solrmarc.jar.dir", jarDir); return(jarDir); } /** * Finds directory "lib" relative to the defaultHomeDir and loads all of * the jar files in that directory dynamically. If it doesn't find a jar * named marc4j*.jar it will log a fatal error and exit the program. */ private static void addLibDirJarstoClassPath() { String homePath = getDefaultHomeDir(); File homeDir = new File(homePath); // Now find the sub-directory "lib" as a sibling of the execution location. File libPath = new File(homeDir, "lib"); try { extendClasspathWithLibJarDir(libPath, "marc4j.*[.]jar"); } catch (RuntimeException ise) { logger.fatal("Fatal error: Failure while loading jars from lib directory" + ise.getMessage()); System.exit(10); } try { Class.forName("org.marc4j.marc.Record"); } catch (ClassNotFoundException e) { try { logger = Logger.getLogger(Boot.class); org.apache.log4j.BasicConfigurator.configure(); logger.fatal("Fatal error: Unable to find marc4j Record class, probably missing many others as well. " + e.getMessage()); } catch (Exception e1) { System.err.println("Fatal error: Unable to find marc4j Record class, probably missing many others as well."); } System.exit(11); } } private static void extendClasspathWithJar(URLClassLoader sysLoader, File jarfile) { URL urls[] = sysLoader.getURLs(); URL ujar; try { ujar = jarfile.toURI().toURL(); } catch (MalformedURLException e) { // This shouldn't happen since the jarfile is passed in as a file // from a directory e.printStackTrace(); return; } String ujars = ujar.toString(); for (int i = 0; i < urls.length; i++) { if (urls[i].toString().equalsIgnoreCase(ujars)) return; } Class<URLClassLoader> sysClass = URLClassLoader.class; try { Method method = sysClass.getDeclaredMethod("addURL", new Class[] { URL.class }); method.setAccessible(true); method.invoke(sysLoader, new Object[] { ujar }); } catch (Throwable t) { t.printStackTrace(); } } private static boolean extendClasspathWithJarDir(URLClassLoader sysLoader, File dir, String patternToLoad, String specialMatch) { boolean foundSpecial = false; if (dir == null || !dir.isDirectory() || dir.listFiles().length == 0) { String dirpath; try { dirpath = dir.getCanonicalPath(); } catch (IOException e) { dirpath = dir.getAbsolutePath(); } throw new RuntimeException("Unable to find any Jars in the provided directory: " + dirpath + " define location of solrj to use with the option -solrj <dir>"); } for (File file : dir.listFiles()) { if (file.getName().matches(patternToLoad)) { extendClasspathWithJar(sysLoader, file); if (specialMatch != null && file.getName().matches(specialMatch)) { foundSpecial = true; } } } return(foundSpecial); } private static void extendClasspathWithDirOfClasses(URLClassLoader sysLoader, File dir) { URI u = dir.toURI(); URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); Class<URLClassLoader> urlClass = URLClassLoader.class; Method method; try { method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class}); method.setAccessible(true); method.invoke(urlClassLoader, new Object[]{u.toURL()}); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static void extendClasspathWithDirOfClasses(File dir) { URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); extendClasspathWithDirOfClasses(sysLoader, dir); } private static boolean extendClasspathWithLibJarDir(File dir, String patternForSpecial) { URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); boolean foundIt = extendClasspathWithJarDir(sysLoader, dir, ".*[.]jar", patternForSpecial); return(foundIt); } /** * Given a directory, add all of the jars there to the classpath. * If none of those jars has a name containing "solrj" then look * in the parent directory of the provided directory for a jar with * a name containing "solrj" * * @param homeDirStrs * @param dir */ static void extendClasspathWithSolJJarDir(String[] homeDirStrs, File dir) { URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); boolean foundSolrj = false; File dirWithJars = getDirToStartFrom(homeDirStrs, dir); foundSolrj = extendClasspathWithJarDir(sysLoader, dirWithJars, ".*[.]jar", ".*solrj.*[.]jar"); File parentDir = null; if (!foundSolrj) { parentDir = dirWithJars.getParentFile(); foundSolrj = extendClasspathWithJarDir(sysLoader, parentDir, ".*solrj.*[.]jar", ".*solrj.*[.]jar"); } if (!foundSolrj) { throw new IndexerSpecException("Unable to find a solrj jar file in directory: "+ dir.getAbsolutePath() + ((parentDir != null) ? "( or "+parentDir.getAbsolutePath()+ ")": "")); } } private static File getDirToStartFrom(String[] homeDirStrs, File dir) { if (homeDirStrs == null) return(dir); // traverse list in stated order so earlier entries will be found and used before later ones. for (int i = 0 ; i < homeDirStrs.length; i++) { String homeDirStr = homeDirStrs[i]; File dirSolrJ = new File(homeDirStr, dir.getPath()); if (dirSolrJ.exists()) { return(dirSolrJ); } } return(dir); } static void extendClasspathWithLocalJarDirs(String[] homeDirStrs, String[] addnlLibDirStrs) { for (String libdirname : addnlLibDirStrs) { File libDir = new File(libdirname); if (!libDir.isAbsolute()) { logger.debug("Number of homeDirStrs: " + homeDirStrs.length); logger.debug("homeDirStrs[0]: " + homeDirStrs[0]); logger.debug("homeDirStrs[1]: " + homeDirStrs[1]); for (int i = homeDirStrs.length - 1; i >= 0; i--) { String homeDir = homeDirStrs[i]; logger.debug("Checking for jars files in directory: " + homeDir + "/" + libdirname); libDir = new File(homeDir, libdirname); if (libDir.exists() && libDir.isDirectory() && libDir.listFiles().length > 0) { logger.debug("Adding jars files in directory: " + libDir.getAbsolutePath()); extendClasspathWithLibJarDir(libDir, null); extendClasspathWithDirOfClasses(libDir); } } } else if (libDir.exists() && libDir.isDirectory() && libDir.listFiles().length > 0) { logger.debug("Adding jars files in directory: " + libDir.getAbsolutePath()); extendClasspathWithLibJarDir(libDir, null); extendClasspathWithDirOfClasses(libDir); } } } }