diff --git a/jetty-server/src/main/config/modules/ext.mod b/jetty-server/src/main/config/modules/ext.mod index 1207bddc391..56b10f7ea42 100644 --- a/jetty-server/src/main/config/modules/ext.mod +++ b/jetty-server/src/main/config/modules/ext.mod @@ -1,9 +1,9 @@ # -# Module to add all lib/ext/*.jar files to classpath +# Module to add all lib/ext/**.jar files to classpath # [lib] -regex:lib/ext/.*\.jar$ +lib/ext/**.jar [files] lib/ diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/BaseHome.java b/jetty-start/src/main/java/org/eclipse/jetty/start/BaseHome.java index 8fb78424149..2c22ce37aa4 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/BaseHome.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/BaseHome.java @@ -19,21 +19,21 @@ package org.eclipse.jetty.start; import java.io.File; -import java.io.FileFilter; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.FileVisitOption; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.PathMatcher; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; +import java.util.EnumSet; import java.util.List; -import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.eclipse.jetty.start.FS.RelativeRegexFilter; - /** * File access for ${jetty.home}, ${jetty.base}, directories. *

@@ -45,6 +45,9 @@ import org.eclipse.jetty.start.FS.RelativeRegexFilter; */ public class BaseHome { + private final static EnumSet SEARCH_VISIT_OPTIONS = EnumSet.of(FileVisitOption.FOLLOW_LINKS);; + private final static int MAX_SEARCH_DEPTH = 30; + private File homeDir; private File baseDir; @@ -119,7 +122,7 @@ public class BaseHome *

    *
  1. If exists relative to ${jetty.base}, return that reference
  2. *
  3. If exists relative to ${jetty.home}, return that reference
  4. - *
  5. Otherwise return absolute path reference
  6. + *
  7. Otherwise return absolute path reference (standard java logic)
  8. *
* * @param path @@ -151,6 +154,140 @@ public class BaseHome return new File(rpath); } + /** + * Get a List of {@link Path}s from a provided pattern. + *

+ * Resolution Steps: + *

    + *
  1. If the pattern starts with "regex:" or "glob:" then a standard {@link PathMatcher} is built using + * {@link java.nio.file.FileSystem#getPathMatcher(String)} as a file search.
  2. + *
  3. If pattern starts with a known filesystem root (using information from {@link java.nio.file.FileSystem#getRootDirectories()}) then this is assumed to + * be a absolute file system pattern.
  4. + *
  5. All other patterns are treated as relative to BaseHome information: + *
      + *
    1. Search ${jetty.home} first
    2. + *
    3. Search ${jetty.base} for overrides
    4. + *
    + *
  6. + *
+ *

+ * Pattern examples: + *

+ *
lib/logging/*.jar
+ *
Relative pattern, not recursive, search ${jetty.home} then ${jetty.base} for lib/logging/*.jar content
+ * + *
lib/**/*-dev.jar
+ *
Relative pattern, recursive search ${jetty.home} then ${jetty.base} for files under lib ending in + * -dev.jar
+ *
+ * + *
etc/jetty.xml
+ *
Relative pattern, no glob, search for ${jetty.home}/etc/jetty.xml then ${jetty.base}/etc/jetty.xml
+ * + *
glob:/opt/app/common/*-corp.jar
+ *
PathMapper pattern, glob, search /opt/app/common/ for *-corp.jar
+ * + * + * + *

+ * Notes: + *

+ * + * @param pattern + * the pattern to search. + * @return the collection of paths found + * @throws IOException + * if error during search operation + */ + public List getPaths(String pattern) throws IOException + { + List hits = new ArrayList<>(); + + if (PathMatchers.isAbsolute(pattern)) + { + Path root = PathMatchers.getSearchRoot(pattern); + PathMatcher matcher = PathMatchers.getMatcher(pattern); + + if (FS.isValidDirectory(root)) + { + PathFinder finder = new PathFinder(); + finder.setFileMatcher(matcher); + finder.setBase(root); + Files.walkFileTree(root,SEARCH_VISIT_OPTIONS,MAX_SEARCH_DEPTH,finder); + hits.addAll(finder.getHits()); + } + } + else + { + Path relativePath = PathMatchers.getSearchRoot(pattern); + PathMatcher matcher = PathMatchers.getMatcher(pattern); + PathFinder finder = new PathFinder(); + finder.setFileMatcher(matcher); + + Path homePath = homeDir.toPath().resolve(relativePath); + + if (FS.isValidDirectory(homePath)) + { + finder.setBase(homePath); + Files.walkFileTree(homePath,SEARCH_VISIT_OPTIONS,MAX_SEARCH_DEPTH,finder); + } + + if (isBaseDifferent()) + { + Path basePath = baseDir.toPath().resolve(relativePath); + if (FS.isValidDirectory(basePath)) + { + finder.setBase(basePath); + Files.walkFileTree(basePath,SEARCH_VISIT_OPTIONS,MAX_SEARCH_DEPTH,finder); + } + } + hits.addAll(finder.getHits()); + } + + Collections.sort(hits,new NaturalSort.Paths()); + return hits; + } + + /** + * Search specified Path with pattern and return hits + * + * @param dir + * the path to a directory to start search from + * @param searchDepth + * the number of directories deep to perform the search + * @param pattern + * the raw pattern to use for the search (must be relative) + * @return the list of Paths found + * @throws IOException + * if unable to search the path + */ + public List getPaths(Path dir, int searchDepth, String pattern) throws IOException + { + if (PathMatchers.isAbsolute(pattern)) + { + throw new RuntimeException("Pattern cannot be absolute: " + pattern); + } + + List hits = new ArrayList<>(); + if (FS.isValidDirectory(dir)) + { + PathMatcher matcher = PathMatchers.getMatcher(pattern); + PathFinder finder = new PathFinder(); + finder.setFileMatcher(matcher); + finder.setBase(dir); + Files.walkFileTree(dir,SEARCH_VISIT_OPTIONS,searchDepth,finder); + hits.addAll(finder.getHits()); + Collections.sort(hits,new NaturalSort.Paths()); + } + return hits; + } + public String getHome() { return homeDir.getAbsolutePath(); @@ -213,183 +350,6 @@ public class BaseHome return homeDir.compareTo(baseDir) != 0; } - /** - * Get all of the files that are in a specific relative directory. - *

- * If the same found path exists in both ${jetty.base} and ${jetty.home}, then the one in ${jetty.base} is returned - * (it overrides the one in ${jetty.home}) - * - * @param relPathToDirectory - * the relative path to the directory - * @return the list of files found. - */ - public List listFiles(String relPathToDirectory) - { - return listFiles(relPathToDirectory,FS.AllFilter.INSTANCE); - } - - /** - * Get all of the files that are in a specific relative directory, with applied {@link FileFilter} - *

- * If the same found path exists in both ${jetty.base} and ${jetty.home}, then the one in ${jetty.base} is returned - * (it overrides the one in ${jetty.home}) - * - * @param relPathToDirectory - * the relative path to the directory - * @param filter - * the filter to use - * @return the list of files found. - */ - public List listFiles(String relPathToDirectory, FileFilter filter) - { - Objects.requireNonNull(filter,"FileFilter cannot be null"); - - File homePath = new File(homeDir,FS.separators(relPathToDirectory)); - List homeFiles = new ArrayList<>(); - if (FS.canReadDirectory(homePath)) - { - homeFiles.addAll(Arrays.asList(homePath.listFiles(filter))); - } - - if (isBaseDifferent()) - { - // merge - File basePath = new File(baseDir,FS.separators(relPathToDirectory)); - List ret = new ArrayList<>(); - if (FS.canReadDirectory(basePath)) - { - File baseFiles[] = basePath.listFiles(filter); - - if (baseFiles != null) - { - for (File base : baseFiles) - { - String relpath = toRelativePath(baseDir,base); - File home = new File(homeDir,FS.separators(relpath)); - if (home.exists()) - { - homeFiles.remove(home); - } - ret.add(base); - } - } - } - - // add any remaining home files. - ret.addAll(homeFiles); - - Collections.sort(ret,new NaturalSort.Files()); - return ret; - } - else - { - // simple return - Collections.sort(homeFiles,new NaturalSort.Files()); - return homeFiles; - } - } - - /** - * Get all of the files that are in a specific relative directory, with applied regex. - *

- * If the same found path exists in both ${jetty.base} and ${jetty.home}, then the one in ${jetty.base} is returned - * (it overrides the one in ${jetty.home}) - *

- * All regex paths are assumed to be in unix notation (use of "/" to separate paths, as "\" is used to escape in regex) - * - * @param regex - * the regex to use to match against the found files. - * @return the list of files found. - */ - public List listFilesRegex(String regex) - { - Objects.requireNonNull(regex,"Glob cannot be null"); - - Pattern pattern = Pattern.compile(regex); - - List homeFiles = new ArrayList<>(); - if (FS.canReadDirectory(homeDir)) - { - StartLog.debug("Finding files in ${jetty.home} that match: %s",regex); - recurseDir(homeFiles,homeDir,new FS.RelativeRegexFilter(homeDir,pattern)); - StartLog.debug("Found %,d files",homeFiles.size()); - } - - if (isBaseDifferent()) - { - // merge - List ret = new ArrayList<>(); - if (FS.canReadDirectory(baseDir)) - { - List baseFiles = new ArrayList<>(); - StartLog.debug("Finding files in ${jetty.base} that match: %s",regex); - recurseDir(baseFiles,baseDir,new FS.RelativeRegexFilter(baseDir,pattern)); - StartLog.debug("Found %,d files",baseFiles.size()); - - for (File base : baseFiles) - { - String relpath = toRelativePath(baseDir,base); - File home = new File(homeDir,FS.separators(relpath)); - if (home.exists()) - { - homeFiles.remove(home); - } - ret.add(base); - } - } - - // add any remaining home files. - ret.addAll(homeFiles); - StartLog.debug("Merged Files: %,d files%n",ret.size()); - - Collections.sort(ret,new NaturalSort.Files()); - return ret; - } - else - { - // simple return - Collections.sort(homeFiles,new NaturalSort.Files()); - return homeFiles; - } - } - - private void recurseDir(List files, File dir, RelativeRegexFilter filter) - { - // find matches first - files.addAll(Arrays.asList(dir.listFiles(filter))); - - // now dive down into sub-directories - for (File subdir : dir.listFiles(FS.DirFilter.INSTANCE)) - { - recurseDir(files,subdir,filter); - } - } - - /** - * Collect the list of files in both ${jetty.base} and ${jetty.home}, even if the same file shows up in both places. - */ - public List rawListFiles(String relPathToDirectory, FileFilter filter) - { - Objects.requireNonNull(filter,"FileFilter cannot be null"); - - List ret = new ArrayList<>(); - - // Home Dir - File homePath = new File(homeDir,FS.separators(relPathToDirectory)); - ret.addAll(Arrays.asList(homePath.listFiles(filter))); - - if (isBaseDifferent()) - { - // Base Dir - File basePath = new File(baseDir,FS.separators(relPathToDirectory)); - ret.addAll(Arrays.asList(basePath.listFiles(filter))); - } - - // Sort - Collections.sort(ret,new NaturalSort.Files()); - return ret; - } - public void setBaseDir(File dir) { try @@ -416,12 +376,6 @@ public class BaseHome } } - // TODO - inline - private String toRelativePath(File dir, File path) - { - return FS.toRelativePath(dir,path); - } - /** * Convenience method for toShortForm(file.getCanonicalPath()) */ diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/FS.java b/jetty-start/src/main/java/org/eclipse/jetty/start/FS.java index 5fd41b92dd5..de5f02676a6 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/FS.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/FS.java @@ -22,11 +22,15 @@ import java.io.Closeable; import java.io.File; import java.io.FileFilter; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; import java.util.Locale; import java.util.regex.Pattern; public class FS { + @Deprecated public static class AllFilter implements FileFilter { public static final AllFilter INSTANCE = new AllFilter(); @@ -37,7 +41,8 @@ public class FS return true; } } - + + @Deprecated public static class DirFilter implements FileFilter { public static final DirFilter INSTANCE = new DirFilter(); @@ -48,7 +53,8 @@ public class FS return path.isDirectory(); } } - + + @Deprecated public static class RelativeRegexFilter implements FileFilter { private final File baseDir; @@ -65,12 +71,13 @@ public class FS { // get relative path String relativePath = FS.toRelativePath(baseDir,path); - + // see if it matches return (pattern.matcher(relativePath).matches()); } } + @Deprecated public static class FilenameRegexFilter implements FileFilter { private final Pattern pattern; @@ -87,6 +94,7 @@ public class FS } } + @Deprecated public static class FileNamesFilter implements FileFilter { private final String filenames[]; @@ -114,6 +122,7 @@ public class FS } } + @Deprecated public static class IniFilter extends FilenameRegexFilter { public IniFilter() @@ -122,6 +131,7 @@ public class FS } } + @Deprecated public static class XmlFilter extends FilenameRegexFilter { public XmlFilter() @@ -130,11 +140,36 @@ public class FS } } + public static boolean isValidDirectory(Path path) + { + LinkOption lopts[] = new LinkOption[0]; + if (!Files.exists(path,lopts)) + { + // doesn't exist, not a valid directory + return false; + } + + if (!Files.isDirectory(path,lopts)) + { + // not a directory (as expected) + StartLog.warn("Not a directory: " + path); + return false; + } + + return true; + } + public static boolean canReadDirectory(File path) { return (path.exists() && path.isDirectory() && path.canRead()); } + public static boolean canReadDirectory(Path path) + { + LinkOption lopts[] = new LinkOption[0]; + return Files.exists(path,lopts) && Files.isDirectory(path,lopts) && Files.isReadable(path); + } + public static boolean canReadFile(File path) { return (path.exists() && path.isFile() && path.canRead()); @@ -168,7 +203,7 @@ public class FS throw new IOException("Unable to create directory: " + dir.getAbsolutePath()); } } - + public static void ensureDirectoryWritable(File dir) throws IOException { if (!dir.exists()) @@ -194,7 +229,7 @@ public class FS { return filename.toLowerCase(Locale.ENGLISH).endsWith(".xml"); } - + public static String toRelativePath(File baseDir, File path) { return baseDir.toURI().relativize(path.toURI()).toASCIIString(); @@ -216,4 +251,9 @@ public class FS } return ret.toString(); } + + public static boolean exists(Path path) + { + return Files.exists(path,new LinkOption[0]); + } } diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java index c196affc580..e41ed7b417a 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java @@ -18,9 +18,7 @@ package org.eclipse.jetty.start; -import static org.eclipse.jetty.start.UsageException.ERR_INVOKE_MAIN; -import static org.eclipse.jetty.start.UsageException.ERR_NOT_STOPPED; -import static org.eclipse.jetty.start.UsageException.ERR_UNKNOWN; +import static org.eclipse.jetty.start.UsageException.*; import java.io.BufferedReader; import java.io.File; @@ -39,6 +37,7 @@ import java.net.InetAddress; import java.net.Socket; import java.net.SocketTimeoutException; import java.net.URL; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -534,17 +533,12 @@ public class Main File start_d = baseHome.getBaseFile("start.d"); if (FS.canReadDirectory(start_d)) { - List files = new ArrayList<>(); - for (File file : start_d.listFiles(new FS.IniFilter())) + List paths = baseHome.getPaths(start_d.toPath(),1,"*.ini"); + Collections.sort(paths,new NaturalSort.Paths()); + for (Path path: paths) { - files.add(file); - } - - Collections.sort(files,new NaturalSort.Files()); - for (File file : files) - { - StartLog.debug("Reading ${jetty.base}/start.d/%s - %s",file.getName(),file); - args.parse(baseHome,new StartIni(file)); + StartLog.debug("Reading ${jetty.base}/start.d/%s - %s",path.getFileName(),path); + args.parse(baseHome,new StartIni(path)); } } diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java index 95bd8076f46..2bf6b4c5aba 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.start; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -45,7 +46,7 @@ public class Modules implements Iterable * ex: modules/npn/npn-1.7.0_01.mod (property expansion resolves to non-existent file) */ private Set missingModules = new HashSet(); - + private int maxDepth = -1; private Set asNameSet(Set moduleSet) @@ -294,10 +295,10 @@ public class Modules implements Iterable private void findParents(Module module, Map ret) { - ret.put(module.getName(), module); + ret.put(module.getName(),module); for (Module parent : module.getParentEdges()) { - ret.put(parent.getName(), parent); + ret.put(parent.getName(),parent); findParents(parent,ret); } } @@ -371,13 +372,13 @@ public class Modules implements Iterable public void registerAll(BaseHome basehome, StartArgs args) throws IOException { - for (File file : basehome.listFiles("modules",new FS.FilenameRegexFilter("^.*\\.mod$"))) + for (Path path : basehome.getPaths("modules/*.mod")) { - registerModule(basehome,args,file); + registerModule(basehome,args,path.toFile()); } // load missing post-expanded dependent modules - boolean done = false; + boolean done = false; while (!done) { done = true; @@ -399,14 +400,14 @@ public class Modules implements Iterable for (String missingParent : missingParents) { File file = basehome.getFile("modules/" + missingParent + ".mod"); - if ( FS.canReadFile(file) ) + if (FS.canReadFile(file)) { Module module = registerModule(basehome,args,file); updateParentReferencesTo(module); } else { - StartLog.debug("Missing module definition: [ Mod: %s | File: %s]", missingParent, file); + StartLog.debug("Missing module definition: [ Mod: %s | File: %s]",missingParent,file); missingModules.add(missingParent); } } @@ -440,7 +441,7 @@ public class Modules implements Iterable */ public List resolveEnabled() { - Map active = new HashMap(); + Map active = new HashMap(); for (Module module : modules.values()) { @@ -455,20 +456,20 @@ public class Modules implements Iterable * * Ex: npn should match anything under npn/ */ - for ( String missing : missingModules ) + for (String missing : missingModules) { - for (String activeModule: active.keySet()) - { - if ( missing.startsWith(activeModule) ) + for (String activeModule : active.keySet()) + { + if (missing.startsWith(activeModule)) { - StartLog.warn("** Unable to continue, required dependency missing. [%s]", missing); + StartLog.warn("** Unable to continue, required dependency missing. [%s]",missing); StartLog.warn("** As configured, Jetty is unable to start due to a missing enabled module dependency."); StartLog.warn("** This may be due to a transitive dependency akin to spdy on npn, which resolves based on the JDK in use."); return Collections.emptyList(); } } } - + List ordered = new ArrayList<>(); ordered.addAll(active.values()); Collections.sort(ordered,new Module.DepthComparator()); @@ -477,7 +478,7 @@ public class Modules implements Iterable public Set resolveParentModulesOf(String moduleName) { - Map ret = new HashMap<>(); + Map ret = new HashMap<>(); Module module = get(moduleName); findParents(module,ret); return ret.keySet(); @@ -524,4 +525,26 @@ public class Modules implements Iterable m.setParentNames(resolvedParents); } } + + @Override + public String toString() + { + StringBuilder str = new StringBuilder(); + str.append("Modules["); + str.append("count=").append(modules.size()); + str.append(",<"); + boolean delim = false; + for (String name : modules.keySet()) + { + if (delim) + { + str.append(','); + } + str.append(name); + delim = true; + } + str.append(">"); + str.append("]"); + return str.toString(); + } } diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/NaturalSort.java b/jetty-start/src/main/java/org/eclipse/jetty/start/NaturalSort.java index 042b326370d..838a96d9216 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/NaturalSort.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/NaturalSort.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.start; import java.io.File; +import java.nio.file.Path; import java.text.CollationKey; import java.text.Collator; import java.util.Comparator; @@ -28,6 +29,19 @@ import java.util.Comparator; */ public class NaturalSort { + public static class Paths implements Comparator + { + private final Collator collator = Collator.getInstance(); + + @Override + public int compare(Path o1, Path o2) + { + CollationKey key1 = collator.getCollationKey(o1.toString()); + CollationKey key2 = collator.getCollationKey(o2.toString()); + return key1.compareTo(key2); + } + } + public static class Files implements Comparator { private final Collator collator = Collator.getInstance(); diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/PathFinder.java b/jetty-start/src/main/java/org/eclipse/jetty/start/PathFinder.java new file mode 100644 index 00000000000..10c52197bcf --- /dev/null +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/PathFinder.java @@ -0,0 +1,161 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.start; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystemLoopException; +import java.nio.file.FileVisitResult; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class PathFinder extends SimpleFileVisitor +{ + private boolean includeDirsInResults = false; + private Map hits = new HashMap<>(); + private Path basePath = null; + private PathMatcher dirMatcher = PathMatchers.getNonHidden(); + private PathMatcher fileMatcher = PathMatchers.getNonHidden(); + + private void addHit(Path path) + { + String relPath = basePath.relativize(path).toString(); + StartLog.debug("addHit(" + path + ") = [" + relPath + "," + path + "]"); + hits.put(relPath,path); + } + + public PathMatcher getDirMatcher() + { + return dirMatcher; + } + + public PathMatcher getFileMatcher() + { + return fileMatcher; + } + + public List getHitList() + { + List ret = new ArrayList<>(); + for (Path path : hits.values()) + { + ret.add(path.toFile()); + } + return ret; + } + + public Collection getHits() + { + return hits.values(); + } + + public boolean isIncludeDirsInResults() + { + return includeDirsInResults; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException + { + if (dirMatcher.matches(dir)) + { + StartLog.debug("Following dir: " + dir); + if (includeDirsInResults) + { + addHit(dir); + } + return FileVisitResult.CONTINUE; + } + else + { + StartLog.debug("Skipping dir: " + dir); + return FileVisitResult.SKIP_SUBTREE; + } + } + + /** + * Set the active basePath, used for resolving relative paths. + *

+ * When a hit arrives for a subsequent find that has the same relative path as a prior hit, the new hit overrides the prior path as the active hit. + * + * @param basePath + * the basePath to tag all hits with + */ + public void setBase(Path basePath) + { + this.basePath = basePath; + } + + public void setDirMatcher(PathMatcher dirMatcher) + { + this.dirMatcher = dirMatcher; + } + + public void setFileMatcher(PathMatcher fileMatcher) + { + this.fileMatcher = fileMatcher; + } + + public void setFileMatcher(String pattern) + { + this.fileMatcher = PathMatchers.getMatcher(pattern); + } + + public void setIncludeDirsInResults(boolean includeDirsInResults) + { + this.includeDirsInResults = includeDirsInResults; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException + { + if (fileMatcher.matches(file)) + { + StartLog.debug("Found file: " + file); + addHit(file); + } + else + { + StartLog.debug("Ignoring file: " + file); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException + { + if (exc instanceof FileSystemLoopException) + { + StartLog.warn("skipping detected filesystem loop: " + file); + return FileVisitResult.SKIP_SUBTREE; + } + else + { + StartLog.warn(exc); + return super.visitFileFailed(file,exc); + } + } +} \ No newline at end of file diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/PathMatchers.java b/jetty-start/src/main/java/org/eclipse/jetty/start/PathMatchers.java new file mode 100644 index 00000000000..609c8b211aa --- /dev/null +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/PathMatchers.java @@ -0,0 +1,208 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.start; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.PathMatcher; + +/** + * Common PathMatcher implementations. + */ +public class PathMatchers +{ + private static class NonHiddenMatcher implements PathMatcher + { + @Override + public boolean matches(Path path) + { + try + { + return !Files.isHidden(path); + } + catch (IOException e) + { + StartLog.debug(e); + return false; + } + } + } + + private static final char GLOB_CHARS[] = "*?".toCharArray(); + private static final char SYNTAXED_GLOB_CHARS[] = "{}[]|:".toCharArray(); + private static final Path EMPTY_PATH = new File(".").toPath(); + + /** + * Convert a pattern to a Path object. + * + * @param pattern + * the raw pattern (can contain "glob:" or "regex:" syntax indicator) + * @return the Path version of the pattern provided. + */ + private static Path asPath(String pattern) + { + String test = pattern; + if (test.startsWith("glob:")) + { + test = test.substring("glob:".length()); + } + else if (test.startsWith("regex:")) + { + test = test.substring("regex:".length()); + } + return new File(test).toPath(); + } + + public static PathMatcher getMatcher(String pattern) + { + FileSystem fs = FileSystems.getDefault(); + + // If using FileSystem.getPathMatcher() with "glob:" or "regex:" + // use FileSystem default pattern behavior + if (pattern.startsWith("glob:") || pattern.startsWith("regex:")) + { + StartLog.debug("Using Standard " + fs.getClass().getName() + " pattern: " + pattern); + return fs.getPathMatcher(pattern); + } + + // If the pattern starts with a root path then its assumed to + // be a full system path + for (Path root : fs.getRootDirectories()) + { + StartLog.debug("root: " + root); + if (pattern.startsWith(root.toString())) + { + String pat = "glob:" + pattern; + StartLog.debug("Using absolute path pattern: " + pat); + return fs.getPathMatcher(pat); + } + } + + // Doesn't start with filesystem root, then assume the pattern + // is a relative file path pattern. + String pat = "glob:**/" + pattern; + StartLog.debug("Using relative path pattern: " + pat); + return fs.getPathMatcher(pat); + } + + public static PathMatcher getNonHidden() + { + return new NonHiddenMatcher(); + } + + /** + * Provide the non-glob / non-regex prefix on the pattern as a Path reference. + * + * @param pattern + * the pattern to test + * @return the Path representing the search root for the pattern provided. + */ + public static Path getSearchRoot(final String pattern) + { + Path path = asPath(pattern); + Path test = path.getRoot(); + + boolean isSyntaxed = pattern.startsWith("glob:") || pattern.startsWith("regex:"); + + int len = path.getNameCount(); + for (int i = 0; i < len; i++) + { + Path part = path.getName(i); + if (isGlob(part.toString(),isSyntaxed)) + { + // found a glob part, return prior parts now + break; + } + + // is this the last entry? + if (i == (len - 1)) + { + // always return prior entries + break; + } + + if (test == null) + { + test = part; + } + else + { + test = test.resolve(part); + } + } + + if (test == null) + { + return EMPTY_PATH; + } + return test; + } + + /** + * Tests if provided pattern is an absolute reference (or not) + * + * @param pattern + * the pattern to test + * @return true if pattern is an absolute reference. + */ + public static boolean isAbsolute(final String pattern) + { + return asPath(pattern).isAbsolute(); + } + + /** + * Determine if part is a glob pattern. + * + * @param part + * the string to check + * @param syntaxed + * true if overall pattern is syntaxed with "glob:" or "regex:" + * @return true if part has glob characters + */ + private static boolean isGlob(String part, boolean syntaxed) + { + int len = part.length(); + for (int i = 0; i < len; i++) + { + char c = part.charAt(i); + for (char g : GLOB_CHARS) + { + if (c == g) + { + return true; + } + } + if (syntaxed) + { + for (char g : SYNTAXED_GLOB_CHARS) + { + if (c == g) + { + return true; + } + } + } + } + return false; + } +} diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java index 9892127b83a..eb419bbc01d 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java @@ -21,9 +21,9 @@ package org.eclipse.jetty.start; import static org.eclipse.jetty.start.UsageException.*; import java.io.File; -import java.io.FileFilter; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -325,66 +325,10 @@ public class StartArgs StartLog.debug("rawlibref = " + rawlibref); String libref = properties.expand(rawlibref); StartLog.debug("expanded = " + libref); - - if (libref.startsWith("regex:")) + + for (Path libpath : baseHome.getPaths(libref)) { - String regex = libref.substring("regex:".length()); - for (File libfile : baseHome.listFilesRegex(regex)) - { - classpath.addComponent(libfile); - } - continue; - } - - libref = FS.separators(libref); - - // Any globs here? - if (libref.contains("*")) - { - // Glob Reference - int idx = libref.lastIndexOf(File.separatorChar); - - String relativePath = "/"; - String filenameRef = libref; - if (idx >= 0) - { - relativePath = libref.substring(0,idx); - filenameRef = libref.substring(idx + 1); - } - - StringBuilder regex = new StringBuilder(); - regex.append('^'); - for (char c : filenameRef.toCharArray()) - { - switch (c) - { - case '*': - regex.append(".*"); - break; - case '.': - regex.append("\\."); - break; - default: - regex.append(c); - } - } - regex.append('$'); - StartLog.debug("regex = " + regex); - - FileFilter filter = new FS.FilenameRegexFilter(regex.toString()); - - List libs = baseHome.listFiles(relativePath,filter); - StartLog.debug("found " + libs.size() + " libs"); - for (File libfile : libs) - { - classpath.addComponent(libfile); - } - } - else - { - // Straight Reference - File libfile = baseHome.getFile(libref); - classpath.addComponent(libfile); + classpath.addComponent(libpath.toFile()); } } diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/StartIni.java b/jetty-start/src/main/java/org/eclipse/jetty/start/StartIni.java index c2635aeb72d..192e74adfdb 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/StartIni.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/StartIni.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.start; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Path; /** * Simple Start .INI handler @@ -32,6 +33,11 @@ public class StartIni extends TextFile super(file); } + public StartIni(Path path) throws FileNotFoundException, IOException + { + this(path.toFile()); + } + @Override public void addUniqueLine(String line) { diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java index ef3ac686e2b..b2c19cbd50a 100644 --- a/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java +++ b/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java @@ -22,6 +22,7 @@ import static org.hamcrest.Matchers.*; import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -32,7 +33,55 @@ import org.junit.Test; public class BaseHomeTest { - private void assertFileList(BaseHome hb, String message, List expected, List files) + public static void assertPathList(BaseHome hb, String message, List expected, PathFinder finder) + { + List actual = new ArrayList<>(); + for (Path path : finder.getHits()) + { + actual.add(hb.toShortForm(path.toFile())); + } + + if (actual.size() != expected.size()) + { + System.out.printf("Actual Path(s): %,d hits%n",actual.size()); + for (String path : actual) + { + System.out.printf(" %s%n",path); + } + System.out.printf("Expected Path(s): %,d entries%n",expected.size()); + for (String path : expected) + { + System.out.printf(" %s%n",path); + } + } + Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray())); + } + + public static void assertPathList(BaseHome hb, String message, List expected, List paths) + { + List actual = new ArrayList<>(); + for (Path path : paths) + { + actual.add(hb.toShortForm(path.toFile())); + } + + if (actual.size() != expected.size()) + { + System.out.printf("Actual Path(s): %,d hits%n",actual.size()); + for (String path : actual) + { + System.out.printf(" %s%n",path); + } + System.out.printf("Expected Path(s): %,d entries%n",expected.size()); + for (String path : expected) + { + System.out.printf(" %s%n",path); + } + } + Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray())); + } + + public static void assertFileList(BaseHome hb, String message, List expected, List files) { List actual = new ArrayList<>(); for (File file : files) @@ -42,15 +91,6 @@ public class BaseHomeTest Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray())); } - private void toOsSeparators(List expected) - { - for (int i = 0; i < expected.size(); i++) - { - String fixed = FS.separators(expected.get(i)); - expected.set(i,fixed); - } - } - @Test public void testGetFile_OnlyHome() throws IOException { @@ -68,13 +108,13 @@ public class BaseHomeTest } @Test - public void testListFiles_OnlyHome() throws IOException + public void testGetPaths_OnlyHome() throws IOException { File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home"); File baseDir = null; BaseHome hb = new BaseHome(homeDir,baseDir); - List files = hb.listFiles("/start.d"); + List paths = hb.getPaths("start.d/*"); List expected = new ArrayList<>(); expected.add("${jetty.home}/start.d/jmx.ini"); @@ -82,19 +122,19 @@ public class BaseHomeTest expected.add("${jetty.home}/start.d/jsp.ini"); expected.add("${jetty.home}/start.d/logging.ini"); expected.add("${jetty.home}/start.d/ssl.ini"); - toOsSeparators(expected); + FSTest.toOsSeparators(expected); - assertFileList(hb,"Files found",expected,files); + assertPathList(hb,"Paths found",expected,paths); } @Test - public void testListFiles_Filtered_OnlyHome() throws IOException + public void testGetPaths_OnlyHome_InisOnly() throws IOException { File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home"); File baseDir = null; BaseHome hb = new BaseHome(homeDir,baseDir); - List files = hb.listFiles("/start.d",new FS.IniFilter()); + List paths = hb.getPaths("start.d/*.ini"); List expected = new ArrayList<>(); expected.add("${jetty.home}/start.d/jmx.ini"); @@ -102,19 +142,19 @@ public class BaseHomeTest expected.add("${jetty.home}/start.d/jsp.ini"); expected.add("${jetty.home}/start.d/logging.ini"); expected.add("${jetty.home}/start.d/ssl.ini"); - toOsSeparators(expected); + FSTest.toOsSeparators(expected); - assertFileList(hb,"Files found",expected,files); + assertPathList(hb,"Paths found",expected,paths); } @Test - public void testListFiles_Both() throws IOException + public void testGetPaths_Both() throws IOException { File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home"); File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base"); BaseHome hb = new BaseHome(homeDir,baseDir); - List files = hb.listFiles("/start.d"); + List paths = hb.getPaths("start.d/*.ini"); List expected = new ArrayList<>(); expected.add("${jetty.base}/start.d/jmx.ini"); @@ -123,9 +163,9 @@ public class BaseHomeTest expected.add("${jetty.base}/start.d/logging.ini"); expected.add("${jetty.home}/start.d/ssl.ini"); expected.add("${jetty.base}/start.d/myapp.ini"); - toOsSeparators(expected); + FSTest.toOsSeparators(expected); - assertFileList(hb,"Files found",expected,files); + assertPathList(hb,"Paths found",expected,paths); } @Test diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/FSTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/FSTest.java new file mode 100644 index 00000000000..e44fb5cbe65 --- /dev/null +++ b/jetty-start/src/test/java/org/eclipse/jetty/start/FSTest.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.start; + +import java.io.File; +import java.util.List; + +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.junit.Assert; +import org.junit.Test; + +public class FSTest +{ + @Test + public void testCanReadDirectory() + { + File targetDir = MavenTestingUtils.getTargetDir(); + Assert.assertTrue("Can read dir: " + targetDir,FS.canReadDirectory(targetDir)); + } + + @Test + public void testCanReadDirectory_NotDir() + { + File bogusFile = MavenTestingUtils.getTestResourceFile("bogus.xml"); + Assert.assertFalse("Can read dir: " + bogusFile,FS.canReadDirectory(bogusFile)); + } + + @Test + public void testCanReadFile() + { + File pom = MavenTestingUtils.getProjectFile("pom.xml"); + Assert.assertTrue("Can read file: " + pom,FS.canReadFile(pom)); + } + + /** + * Utility method used by other test cases + */ + public static void toOsSeparators(List expected) + { + for (int i = 0; i < expected.size(); i++) + { + String fixed = FS.separators(expected.get(i)); + expected.set(i,fixed); + } + } +} diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java index 222f760f8fb..5fe9b1a363b 100644 --- a/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java +++ b/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java @@ -98,7 +98,7 @@ public class MainTest List cmdLineArgs = new ArrayList<>(); addUseCasesHome(cmdLineArgs); - + // JVM args cmdLineArgs.add("--exec"); cmdLineArgs.add("-Xms1024m"); diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/ModulesTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/ModulesTest.java index 27d5726c42a..6ce160cd9d1 100644 --- a/jetty-start/src/test/java/org/eclipse/jetty/start/ModulesTest.java +++ b/jetty-start/src/test/java/org/eclipse/jetty/start/ModulesTest.java @@ -35,7 +35,7 @@ public class ModulesTest { private final static List TEST_SOURCE = Collections.singletonList(""); private StartArgs DEFAULT_ARGS = new StartArgs(new String[] { "jetty.version=TEST" }).parseCommandLine(); - + @Test public void testLoadAllModules() throws IOException { @@ -43,10 +43,26 @@ public class ModulesTest BaseHome basehome = new BaseHome(homeDir,homeDir); Modules modules = new Modules(); - modules.registerAll(basehome, DEFAULT_ARGS); - Assert.assertThat("Module count",modules.count(),is(30)); + modules.registerAll(basehome,DEFAULT_ARGS); + + List moduleNames = new ArrayList<>(); + for (Module mod : modules) + { + // skip npn-boot in this test (as its behavior is jdk specific) + if (mod.getName().equals("npn-boot")) + { + continue; + } + moduleNames.add(mod.getName()); + } + + String expected[] = { "jmx", "client", "stats", "spdy", "deploy", "debug", "security", "npn", "ext", "websocket", "rewrite", "ipaccess", "xinetd", + "proxy", "webapp", "jndi", "lowresources", "https", "plus", "requestlog", "jsp", "monitor", "xml", "servlet", "jaas", "http", "base", "server", + "annotations" }; + + Assert.assertThat("Module count: " + moduleNames,moduleNames.size(),is(expected.length)); } - + @Test public void testEnableRegexSimple() throws IOException { @@ -54,11 +70,11 @@ public class ModulesTest BaseHome basehome = new BaseHome(homeDir,homeDir); Modules modules = new Modules(); - modules.registerAll(basehome, DEFAULT_ARGS); + modules.registerAll(basehome,DEFAULT_ARGS); modules.enable("[sj]{1}.*",TEST_SOURCE); - + String expected[] = { "jmx", "stats", "spdy", "security", "jndi", "jsp", "servlet", "jaas", "server" }; - + Assert.assertThat("Enabled Module count",modules.resolveEnabled().size(),is(expected.length)); } @@ -70,7 +86,7 @@ public class ModulesTest // Register modules Modules modules = new Modules(); - modules.registerAll(basehome, DEFAULT_ARGS); + modules.registerAll(basehome,DEFAULT_ARGS); modules.buildGraph(); // Enable 2 modules @@ -108,12 +124,12 @@ public class ModulesTest List actualLibs = modules.normalizeLibs(active); Assert.assertThat("Resolved Libs: " + actualLibs,actualLibs,contains(expectedLibs.toArray())); - + // Assert XML List List expectedXmls = new ArrayList<>(); expectedXmls.add("etc/jetty.xml"); expectedXmls.add("etc/jetty-http.xml"); - + List actualXmls = modules.normalizeXmls(active); Assert.assertThat("Resolved XMLs: " + actualXmls,actualXmls,contains(expectedXmls.toArray())); } @@ -126,7 +142,7 @@ public class ModulesTest // Register modules Modules modules = new Modules(); - modules.registerAll(basehome, DEFAULT_ARGS); + modules.registerAll(basehome,DEFAULT_ARGS); modules.buildGraph(); // modules.dump(); @@ -136,7 +152,7 @@ public class ModulesTest // Collect active module list List active = modules.resolveEnabled(); - + // Assert names are correct, and in the right order List expectedNames = new ArrayList<>(); expectedNames.add("base"); @@ -174,10 +190,10 @@ public class ModulesTest expectedLibs.add("lib/jetty-annotations-${jetty.version}.jar"); expectedLibs.add("lib/annotations/*.jar"); expectedLibs.add("lib/websocket/*.jar"); - + List actualLibs = modules.normalizeLibs(active); Assert.assertThat("Resolved Libs: " + actualLibs,actualLibs,contains(expectedLibs.toArray())); - + // Assert XML List List expectedXmls = new ArrayList<>(); expectedXmls.add("etc/jetty.xml"); @@ -185,7 +201,7 @@ public class ModulesTest expectedXmls.add("etc/jetty-plus.xml"); expectedXmls.add("etc/jetty-annotations.xml"); expectedXmls.add("etc/jetty-websockets.xml"); - + List actualXmls = modules.normalizeXmls(active); Assert.assertThat("Resolved XMLs: " + actualXmls,actualXmls,contains(expectedXmls.toArray())); } diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/PathFinderTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/PathFinderTest.java new file mode 100644 index 00000000000..bc14d322001 --- /dev/null +++ b/jetty-start/src/test/java/org/eclipse/jetty/start/PathFinderTest.java @@ -0,0 +1,87 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.start; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileVisitOption; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.junit.Test; + +public class PathFinderTest +{ + @Test + public void testFindInis() throws IOException + { + File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home"); + Path homePath = homeDir.toPath(); + + PathFinder finder = new PathFinder(); + finder.setFileMatcher("glob:**/*.ini"); + finder.setBase(homePath); + + Files.walkFileTree(homePath,EnumSet.of(FileVisitOption.FOLLOW_LINKS),30,finder); + + List expected = new ArrayList<>(); + expected.add("${jetty.home}/start.d/jmx.ini"); + expected.add("${jetty.home}/start.d/jndi.ini"); + expected.add("${jetty.home}/start.d/jsp.ini"); + expected.add("${jetty.home}/start.d/logging.ini"); + expected.add("${jetty.home}/start.d/ssl.ini"); + expected.add("${jetty.home}/start.ini"); + FSTest.toOsSeparators(expected); + + BaseHome hb = new BaseHome(homeDir,null); + BaseHomeTest.assertPathList(hb,"Files found",expected,finder); + } + + @Test + public void testFindMods() throws IOException + { + File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home"); + + List expected = new ArrayList<>(); + File modulesDir = new File(homeDir,"modules"); + for (File file : modulesDir.listFiles()) + { + if (file.getName().endsWith(".mod")) + { + expected.add("${jetty.home}/modules/" + file.getName()); + } + } + FSTest.toOsSeparators(expected); + + Path modulesPath = modulesDir.toPath(); + + PathFinder finder = new PathFinder(); + finder.setFileMatcher(PathMatchers.getMatcher("modules/*.mod")); + finder.setBase(modulesPath); + + Files.walkFileTree(modulesPath,EnumSet.of(FileVisitOption.FOLLOW_LINKS),1,finder); + + BaseHome hb = new BaseHome(homeDir,null); + BaseHomeTest.assertPathList(hb,"Files found",expected,finder); + } +} diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/PathMatchersTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/PathMatchersTest.java new file mode 100644 index 00000000000..460f88d2a65 --- /dev/null +++ b/jetty-start/src/test/java/org/eclipse/jetty/start/PathMatchersTest.java @@ -0,0 +1,105 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.start; + +import static org.hamcrest.Matchers.*; + +import java.nio.file.Path; + +import org.eclipse.jetty.toolchain.test.OS; +import org.junit.Assert; +import org.junit.Test; + +public class PathMatchersTest +{ + private void assertIsAbsolute(String pattern, boolean expected) + { + Assert.assertThat("isAbsolute(\"" + pattern + "\")",PathMatchers.isAbsolute(pattern),is(expected)); + } + + @Test + public void testIsAbsolute() + { + if (OS.IS_UNIX) + { + assertIsAbsolute("/opt/app",true); + assertIsAbsolute("/opt/florb",true); + assertIsAbsolute("/home/user/benfranklin",true); + assertIsAbsolute("glob:/home/user/benfranklin/*.jar",true); + assertIsAbsolute("glob:/**/*.jar",true); + assertIsAbsolute("regex:/*-[^dev].ini",true); + } + + if (OS.IS_WINDOWS) + { + assertIsAbsolute("C:\\\\System32",true); + assertIsAbsolute("C:\\\\Program Files",true); + } + } + + @Test + public void testIsNotAbsolute() + { + assertIsAbsolute("etc",false); + assertIsAbsolute("lib",false); + assertIsAbsolute("${user.dir}",false); + assertIsAbsolute("**/*.jar",false); + assertIsAbsolute("glob:*.ini",false); + assertIsAbsolute("regex:*-[^dev].ini",false); + } + + private void assertSearchRoot(String pattern, String expectedSearchRoot) + { + Path actual = PathMatchers.getSearchRoot(pattern); + String expectedNormal = FS.separators(expectedSearchRoot); + Assert.assertThat(".getSearchRoot(\"" + pattern + "\")",actual.toString(),is(expectedNormal)); + } + + @Test + public void testGetSearchRoot() + { + if (OS.IS_UNIX) + { + // absolute first + assertSearchRoot("/opt/app/*.jar","/opt/app"); + assertSearchRoot("/lib/jvm/**/jre/lib/*.jar","/lib/jvm"); + assertSearchRoot("glob:/var/lib/*.xml","/var/lib"); + assertSearchRoot("glob:/var/lib/*.{xml,java}","/var/lib"); + assertSearchRoot("glob:/opt/corporate/lib-{dev,prod}/*.ini","/opt/corporate"); + assertSearchRoot("regex:/opt/jetty/.*/lib-(dev|prod)/*.ini","/opt/jetty"); + + assertSearchRoot("/*.ini","/"); + assertSearchRoot("/etc/jetty.conf","/etc"); + assertSearchRoot("/common.conf","/"); + } + + if (OS.IS_WINDOWS) + { + // absolute patterns (complete with required windows slash escaping) + assertSearchRoot("C:\\\\corp\\\\lib\\\\*.jar","C:\\corp\\lib"); + assertSearchRoot("D:\\\\lib\\\\**\\\\jre\\\\lib\\\\*.jar","C:\\lib"); + } + + // some relative paths + assertSearchRoot("lib/*.jar","lib"); + assertSearchRoot("etc/jetty.xml","etc"); + assertSearchRoot("start.ini","."); + assertSearchRoot("start.d/","."); + } +} diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java b/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java index ec419cfc432..28ce62f71c0 100644 --- a/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java +++ b/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java @@ -76,7 +76,6 @@ public class TestUseCases @Test public void testWithSpdyBadNpnVersion() throws Exception { - //StartLog.enableDebug(); assertUseCase("home","base.enable.spdy.bad.npn.version","assert-enable-spdy-bad-npn-version.txt","java.version=1.7.0_01"); } diff --git a/jetty-start/src/test/resources/usecases/home/modules/ext.mod b/jetty-start/src/test/resources/usecases/home/modules/ext.mod index c84697a5631..66c051911db 100644 --- a/jetty-start/src/test/resources/usecases/home/modules/ext.mod +++ b/jetty-start/src/test/resources/usecases/home/modules/ext.mod @@ -3,7 +3,7 @@ # [lib] -regex:lib/ext/.*\.jar$ +lib/ext/**.jar [files] lib/