From 780397bd6b3b582de6671490c321170fd3d15a77 Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Tue, 7 Jan 2014 11:41:52 +0000 Subject: [PATCH] Fix for wildcard recursive classpath handling Previous fix for handling wildcard entries in a classpath imposed a new problem in a case where entry is a directory with a jar files but also contains a lot of nested directories. For example entry "./*" resulted for scanning whole disk starting from "/". In case of default hadoop classpath, it scanned everything under hadoop's installation. On some cases this deep scan was hidden and was revealed by NPE's for file access exceptions. When we want to support wildcard entries we only want to get jar files from that directory, while boot itself have a need to travel recursively to find classfiles from an expoded archive. We handle this case by using recursive(true by default) flag in ExplodedArchive and this flag is set to false in PropertiesLauncher if we match wildcard. --- .../boot/loader/PropertiesLauncher.java | 141 +++--------------- .../boot/loader/archive/ExplodedArchive.java | 17 ++- 2 files changed, 36 insertions(+), 122 deletions(-) diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/PropertiesLauncher.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/PropertiesLauncher.java index ab490ad830..4131644f85 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/PropertiesLauncher.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/PropertiesLauncher.java @@ -33,11 +33,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Properties; -import java.util.jar.Manifest; -import java.util.logging.Level; import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.springframework.boot.loader.archive.Archive; import org.springframework.boot.loader.archive.Archive.Entry; @@ -120,8 +116,6 @@ public class PropertiesLauncher extends Launcher { private static final List DEFAULT_PATHS = Arrays.asList("lib/"); - private static final Pattern WORD_SEPARATOR = Pattern.compile("\\W+"); - private final File home; private List paths = new ArrayList(DEFAULT_PATHS); @@ -129,9 +123,6 @@ public class PropertiesLauncher extends Launcher { private Properties properties = new Properties(); public PropertiesLauncher() { - if (!isDebug()) { - this.logger.setLevel(Level.SEVERE); - } try { this.home = getHomeDirectory(); initializeProperties(this.home); @@ -142,22 +133,6 @@ public class PropertiesLauncher extends Launcher { } } - private boolean isDebug() { - String debug = System.getProperty("debug"); - if (debug != null && !"false".equals(debug)) { - return true; - } - debug = System.getProperty("DEBUG"); - if (debug != null && !"false".equals(debug)) { - return true; - } - debug = System.getenv("DEBUG"); - if (debug != null && !"false".equals(debug)) { - return true; - } - return false; - } - protected File getHomeDirectory() { return new File(SystemPropertyUtils.resolvePlaceholders(System.getProperty(HOME, "${user.dir}"))); @@ -315,82 +290,30 @@ public class PropertiesLauncher extends Launcher { @Override protected String getMainClass() throws Exception { - String mainClass = getProperty(MAIN, "Start-Class"); - if (mainClass == null) { - throw new IllegalStateException("No '" + MAIN - + "' or 'Start-Class' specified"); - } - return mainClass; - } - - @Override - protected ClassLoader createClassLoader(List archives) throws Exception { - ClassLoader loader = super.createClassLoader(archives); - String classLoaderType = getProperty("loader.classLoader"); - if (classLoaderType != null) { - Class type = Class.forName(classLoaderType, true, loader); - try { - loader = (ClassLoader) type.getConstructor(ClassLoader.class) - .newInstance(loader); - } - catch (NoSuchMethodException e) { - try { - loader = (ClassLoader) type.getConstructor(URL[].class, - ClassLoader.class).newInstance(new URL[0], loader); - } - catch (NoSuchMethodException ex) { - loader = (ClassLoader) type.newInstance(); - } - } - this.logger.info("Using custom class loader: " + classLoaderType); - } - return loader; - } - - private String getProperty(String propertyKey) throws Exception { - return getProperty(propertyKey, null); - } - - private String getProperty(String propertyKey, String manifestKey) throws Exception { - if (manifestKey == null) { - manifestKey = propertyKey.replace(".", "-"); - manifestKey = toCamelCase(manifestKey); - } - String property = SystemPropertyUtils.getProperty(propertyKey); + String property = SystemPropertyUtils.getProperty(MAIN); if (property != null) { - String value = SystemPropertyUtils.resolvePlaceholders(property); - this.logger.fine("Property '" + propertyKey + "' from environment: " + value); - return value; + String mainClass = SystemPropertyUtils.resolvePlaceholders(property); + this.logger.info("Main class from environment: " + mainClass); + return mainClass; } - if (this.properties.containsKey(propertyKey)) { - String value = SystemPropertyUtils.resolvePlaceholders(this.properties - .getProperty(propertyKey)); - this.logger.fine("Property '" + propertyKey + "' from properties: " + value); - return value; + if (this.properties.containsKey(MAIN)) { + String mainClass = SystemPropertyUtils.resolvePlaceholders(this.properties + .getProperty(MAIN)); + this.logger.info("Main class from properties: " + mainClass); + return mainClass; } try { // Prefer home dir for MANIFEST if there is one - Manifest manifest = new ExplodedArchive(this.home).getManifest(); - if (manifest != null) { - String value = manifest.getMainAttributes().getValue(manifestKey); - this.logger.fine("Property '" + manifestKey - + "' from home directory manifest: " + value); - return value; - } + String mainClass = new ExplodedArchive(this.home).getMainClass(); + this.logger.info("Main class from home directory manifest: " + mainClass); + return mainClass; } catch (IllegalStateException ex) { + // Otherwise try the parent archive + String mainClass = createArchive().getMainClass(); + this.logger.info("Main class from archive manifest: " + mainClass); + return mainClass; } - // Otherwise try the parent archive - Manifest manifest = createArchive().getManifest(); - if (manifest != null) { - String value = manifest.getMainAttributes().getValue(manifestKey); - if (value != null) { - this.logger.fine("Property '" + manifestKey + "' from archive manifest: " - + value); - return value; - } - } - return null; } @Override @@ -478,6 +401,7 @@ public class PropertiesLauncher extends Launcher { URISyntaxException { ClassLoader parentClassLoader = getClass().getClassLoader(); if (parentClassLoader instanceof URLClassLoader) { + @SuppressWarnings("resource") URLClassLoader urlClassLoader = (URLClassLoader) parentClassLoader; for (URL url : urlClassLoader.getURLs()) { if (url.toString().endsWith(".jar") || url.toString().endsWith(".zip")) { @@ -485,9 +409,12 @@ public class PropertiesLauncher extends Launcher { } else if (url.toString().endsWith("/*")) { String name = url.getFile(); - lib.add(0, - new ExplodedArchive(new File(name.substring(0, - name.length() - 1)))); + File dir = new File(name.substring(0, name.length() - 1)); + if (dir.exists()) { + lib.add(0, + new ExplodedArchive(new File(name.substring(0, + name.length() - 1)), false)); + } } else { lib.add(0, new ExplodedArchive(new File(url.getFile()))); @@ -521,28 +448,6 @@ public class PropertiesLauncher extends Launcher { new PropertiesLauncher().launch(args); } - public static String toCamelCase(CharSequence string) { - if (string == null) { - return null; - } - StringBuilder builder = new StringBuilder(); - Matcher matcher = WORD_SEPARATOR.matcher(string); - int pos = 0; - while (matcher.find()) { - builder.append(capitalize(string.subSequence(pos, matcher.end()).toString())); - pos = matcher.end(); - } - builder.append(capitalize(string.subSequence(pos, string.length()).toString())); - return builder.toString(); - } - - private static Object capitalize(String str) { - StringBuilder sb = new StringBuilder(str.length()); - sb.append(Character.toUpperCase(str.charAt(0))); - sb.append(str.substring(1)); - return sb.toString(); - } - /** * Convenience class for finding nested archives (archive entries that can be * classpath entries). diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/archive/ExplodedArchive.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/archive/ExplodedArchive.java index 449389e80e..df3080ae52 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/archive/ExplodedArchive.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/archive/ExplodedArchive.java @@ -56,11 +56,18 @@ public class ExplodedArchive extends Archive { private Manifest manifest; + private boolean recursive = true; + public ExplodedArchive(File root) { + this(root, true); + } + + public ExplodedArchive(File root, boolean recursive) { if (!root.exists() || !root.isDirectory()) { throw new IllegalArgumentException("Invalid source folder " + root); } this.root = root; + this.recursive = recursive; buildEntries(root); this.entries = Collections.unmodifiableMap(this.entries); } @@ -73,14 +80,16 @@ public class ExplodedArchive extends Archive { private void buildEntries(File file) { if (!file.equals(this.root)) { String name = file.toURI().getPath() - .substring(root.toURI().getPath().length()); + .substring(this.root.toURI().getPath().length()); FileEntry entry = new FileEntry(new AsciiBytes(name), file); this.entries.put(entry.getName(), entry); } if (file.isDirectory()) { - for (File child : file.listFiles()) { - if (!SKIPPED_NAMES.contains(child.getName())) { - buildEntries(child); + if (this.recursive) { + for (File child : file.listFiles()) { + if (!SKIPPED_NAMES.contains(child.getName())) { + buildEntries(child); + } } } }