diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java index fe906d00327..9739e7442f1 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java @@ -45,6 +45,46 @@ public class FileID return basename; } + /** + * Retrieve the extension of a URI path. + * This is the name of the last segment of the URI path with a substring + * for the extension (if any), including the dot, lower-cased. + * + * @param uri The URI to search + * @return The last segment extension. Null if input uri is null, or scheme is null, or URI is not a `jar:file:` or `file:` based URI + */ + public static String getExtension(URI uri) + { + if (uri == null) + return null; + if (uri.getScheme() == null) + return null; + + String path = null; + if (uri.getScheme().equalsIgnoreCase("jar")) + { + URI sspUri = URI.create(uri.getRawSchemeSpecificPart()); + if (!sspUri.getScheme().equalsIgnoreCase("file")) + { + return null; // not a `jar:file:` based URI + } + + path = sspUri.getPath(); + } + else + { + path = uri.getPath(); + } + + // look for `!/` split + int jarEnd = path.indexOf("!/"); + if (jarEnd >= 0) + { + return getExtension(path.substring(0, jarEnd)); + } + return getExtension(path); + } + /** * Retrieve the extension of a file path (not a directory). * This is the name of the last segment of the file path with a substring @@ -149,35 +189,10 @@ public class FileID */ public static boolean isArchive(URI uri) { - if (uri == null) + String ext = getExtension(uri); + if (ext == null) return false; - if (uri.getScheme() == null) - return false; - if (uri.getScheme().equalsIgnoreCase("jar")) - { - URI sspUri = URI.create(uri.getRawSchemeSpecificPart()); - if (!sspUri.getScheme().equalsIgnoreCase("file")) - { - return false; // not a `jar:file:` based URI - } - - String path = sspUri.getPath(); - - int jarEnd = path.indexOf("!/"); - if (jarEnd >= 0) - { - return isArchive(path.substring(0, jarEnd)); - } - return isArchive(path); - } - String path = uri.getPath(); - // look for `!/` split - int jarEnd = path.indexOf("!/"); - if (jarEnd >= 0) - { - return isArchive(path.substring(0, jarEnd)); - } - return isArchive(path); + return (ext.equals(".jar") || ext.equals(".war") || ext.equals(".zip")); } /** @@ -255,6 +270,17 @@ public class FileID return false; } + /** + * Is the URI pointing to a Java Archive (JAR) File (not directory) + * + * @param uri the uri to test. + * @return True if a .jar file. + */ + public static boolean isJavaArchive(URI uri) + { + return ".jar".equals(getExtension(uri)); + } + /** * Is the path a Java Archive (JAR) File (not directory) * diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/PatternMatcher.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/PatternMatcher.java deleted file mode 100644 index 9fd61032aa5..00000000000 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/PatternMatcher.java +++ /dev/null @@ -1,103 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.util; - -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -public abstract class PatternMatcher -{ - public abstract void matched(URI uri) throws Exception; - - public void match(String pattern, URI[] uris, boolean isNullInclusive) - throws Exception - { - Pattern p = (pattern == null ? null : Pattern.compile(pattern)); - match(p, uris, isNullInclusive); - } - - /** - * Find jar names from the provided list matching a pattern. - * - * If the pattern is null and isNullInclusive is true, then - * all jar names will match. - * - * A pattern is a set of acceptable jar names. Each acceptable - * jar name is a regex. Each regex can be separated by either a - * "," or a "|". If you use a "|" this or's together the jar - * name patterns. This means that ordering of the matches is - * unimportant to you. If instead, you want to match particular - * jar names, and you want to match them in order, you should - * separate the regexs with "," instead. - * - * Eg "aaa-.*\\.jar|bbb-.*\\.jar" - * Will iterate over the jar names and match - * in any order. - * - * Eg "aaa-*\\.jar,bbb-.*\\.jar" - * Will iterate over the jar names, matching - * all those starting with "aaa-" first, then "bbb-". - * - * @param pattern the pattern - * @param uris the uris to test the pattern against - * @param isNullInclusive if true, an empty pattern means all names match, if false, none match - * @throws Exception if fundamental error in pattern matching - */ - public void match(Pattern pattern, URI[] uris, boolean isNullInclusive) - throws Exception - { - if (uris != null) - { - String[] patterns = (pattern == null ? null : pattern.pattern().split(",")); - - List subPatterns = new ArrayList(); - for (int i = 0; patterns != null && i < patterns.length; i++) - { - subPatterns.add(Pattern.compile(patterns[i])); - } - if (subPatterns.isEmpty()) - subPatterns.add(pattern); - - if (subPatterns.isEmpty()) - { - matchPatterns(null, uris, isNullInclusive); - } - else - { - //for each subpattern, iterate over all the urls, processing those that match - for (Pattern p : subPatterns) - { - matchPatterns(p, uris, isNullInclusive); - } - } - } - } - - public void matchPatterns(Pattern pattern, URI[] uris, boolean isNullInclusive) - throws Exception - { - for (int i = 0; i < uris.length; i++) - { - URI uri = uris[i]; - String s = uri.toString(); - if ((pattern == null && isNullInclusive) || - (pattern != null && pattern.matcher(s).matches())) - { - matched(uris[i]); - } - } - } -} diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java index 2a77354706b..ebc6c8b7147 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java @@ -1759,15 +1759,8 @@ public final class URIUtil { // Simple reference URI refUri = toURI(reference); - // Is this a Java Archive that can be mounted? - URI jarFileUri = toJarFileUri(refUri); - if (jarFileUri != null) - // add as mountable URI - uris.add(jarFileUri); - else - // add as normal URI - uris.add(refUri); - + // Ensure that a Java Archive that can be mounted + uris.add(toJarFileUri(refUri)); } } catch (Exception e) @@ -1785,7 +1778,7 @@ public final class URIUtil * The resulting URI will point to the {@code jar:file://foo.jar!/} said Java Archive (jar, war, or zip) * * @param uri the URI to mutate to a {@code jar:file:...} URI. - * @return the jar:${uri_to_java_archive}!/${internal-reference} URI or null if not a Java Archive. + * @return the jar:${uri_to_java_archive}!/${internal-reference} URI or the unchanged URI if not a Java Archive. * @see FileID#isArchive(URI) */ public static URI toJarFileUri(URI uri) @@ -1794,7 +1787,7 @@ public final class URIUtil String scheme = Objects.requireNonNull(uri.getScheme(), "URI scheme"); if (!FileID.isArchive(uri)) - return null; + return uri; boolean hasInternalReference = uri.getRawSchemeSpecificPart().indexOf("!/") > 0; @@ -1835,11 +1828,9 @@ public final class URIUtil Objects.requireNonNull(resource); // Only try URI for string for known schemes, otherwise assume it is a Path - URI uri = (KNOWN_SCHEMES.getBest(resource) != null) - ? URI.create(resource) + return (KNOWN_SCHEMES.getBest(resource) != null) + ? correctFileURI(URI.create(resource)) : Paths.get(resource).toUri(); - - return uri; } /** diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/UriPatternPredicate.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/UriPatternPredicate.java new file mode 100644 index 00000000000..f34ffb33213 --- /dev/null +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/UriPatternPredicate.java @@ -0,0 +1,103 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.util; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.regex.Pattern; + +/** + *

+ * Predicate for matching {@link URI} against a provided Regex {@link Pattern} + *

+ * + *

+ * If the pattern is null and isNullInclusive is true, then + * all URIs will match. + *

+ * + *

+ * A pattern is a set of acceptable URI names. Each acceptable + * name is a regex. Each regex can be separated by either a + * "," or a "|". If you use a "|" this or's together the URI + * name patterns. This means that ordering of the matches is + * unimportant to you. If instead, you want to match particular + * names, and you want to match them in order, you should + * separate the regexs with "," instead. + *

+ * + *

+ * Eg "aaa-.*\\.jar|bbb-.*\\.jar" + * Will iterate over the jar names and match in any order. + *

+ * + *

+ * Eg "aaa-*\\.jar,bbb-.*\\.jar" + * Will iterate over the jar names, matching + * all those starting with "aaa-" first, then "bbb-". + *

+ */ +public class UriPatternPredicate implements Predicate +{ + private final List subPatterns; + private final boolean isNullInclusive; + + public UriPatternPredicate(String regex, boolean isNullInclusive) + { + this(regex == null ? null : Pattern.compile(regex), isNullInclusive); + } + + public UriPatternPredicate(Pattern pattern, boolean isNullInclusive) + { + this.isNullInclusive = isNullInclusive; + String[] patterns = (pattern == null ? null : pattern.pattern().split(",")); + + subPatterns = new ArrayList<>(); + for (int i = 0; patterns != null && i < patterns.length; i++) + { + subPatterns.add(Pattern.compile(patterns[i])); + } + if (subPatterns.isEmpty()) + subPatterns.add(pattern); + } + + @Override + public boolean test(URI uri) + { + if (subPatterns.isEmpty()) + { + return match(null, uri); + } + else + { + // for each sub-pattern, test if a match + for (Pattern p : subPatterns) + { + if (match(p, uri)) + return true; + } + } + + return false; + } + + private boolean match(Pattern pattern, URI uri) + { + String s = uri.toString(); + return ((pattern == null && isNullInclusive) || + (pattern != null && pattern.matcher(s).matches())); + } +} diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/MountedPathResource.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/MountedPathResource.java index e3936524e9a..c317fd00bd6 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/MountedPathResource.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/MountedPathResource.java @@ -37,7 +37,7 @@ public class MountedPathResource extends PathResource @Override public boolean isContainedIn(Resource r) { - return r.getURI().equals(containerUri); + return URIUtil.unwrapContainer(r.getURI()).equals(containerUri); } public Path getContainerPath() diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java index 0e9d2abb9b5..fc27cd562b5 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceCollection.java @@ -81,10 +81,6 @@ public class ResourceCollection extends Resource throw new IllegalArgumentException("Does not exist: " + r); } - if (!r.isDirectory()) - { - throw new IllegalArgumentException("Not a directory: " + r); - } unique.add(r); } } diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceUriPatternPredicate.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceUriPatternPredicate.java new file mode 100644 index 00000000000..8d564e27b6c --- /dev/null +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceUriPatternPredicate.java @@ -0,0 +1,47 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.util.resource; + +import java.net.URI; +import java.util.Objects; +import java.util.function.Predicate; + +import org.eclipse.jetty.util.UriPatternPredicate; + +/** + * Specialized {@link UriPatternPredicate} to allow filtering {@link Resource} entries by their URI. + */ +public class ResourceUriPatternPredicate implements Predicate +{ + private final Predicate uriPredicate; + + public ResourceUriPatternPredicate(String regex, boolean isNullInclusive) + { + this(new UriPatternPredicate(regex, isNullInclusive)); + } + + public ResourceUriPatternPredicate(UriPatternPredicate uriPredicate) + { + this.uriPredicate = Objects.requireNonNull(uriPredicate, "UriPatternPredicate cannot be null"); + } + + @Override + public boolean test(Resource resource) + { + if (resource == null) + return false; + URI uri = resource.getURI(); + return uriPredicate.test(uri); + } +} diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java index 8231084d413..0ae45d9c0fd 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java @@ -903,7 +903,8 @@ public class URIUtilTest // Bad java file.uri syntax String input = "file:/home/user/lib/acme.jar"; List uris = URIUtil.split(input); - String expected = String.format("jar:%s!/", input); + // As zipfs with corrected file.uri syntax as well + String expected = "jar:file:///home/user/lib/acme.jar!/"; assertThat(uris.get(0).toString(), is(expected)); } diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/MetaInfConfiguration.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/MetaInfConfiguration.java index 2059c71d43c..01bf105e0dd 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/MetaInfConfiguration.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/MetaInfConfiguration.java @@ -20,6 +20,7 @@ import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -33,16 +34,18 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; import org.eclipse.jetty.util.FileID; import org.eclipse.jetty.util.IO; -import org.eclipse.jetty.util.PatternMatcher; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.UriPatternPredicate; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.util.resource.ResourceUriPatternPredicate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -79,73 +82,6 @@ public class MetaInfConfiguration extends AbstractConfiguration public static final String WEBINF_JAR_PATTERN = "org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern"; public static final List __allScanTypes = Arrays.asList(METAINF_TLDS, METAINF_RESOURCES, METAINF_FRAGMENTS); - /** - * ContainerPathNameMatcher - * - * Matches names of jars on the container classpath - * against a pattern. If no pattern is specified, no - * jars match. - */ - public class ContainerPathNameMatcher extends PatternMatcher - { - protected final WebAppContext _context; - protected final String _pattern; - - public ContainerPathNameMatcher(WebAppContext context, String pattern) - { - if (context == null) - throw new IllegalArgumentException("Context null"); - _context = context; - _pattern = pattern; - } - - public void match(List uris) throws Exception - { - if (uris == null) - return; - match(_pattern, uris.toArray(new URI[uris.size()]), false); - } - - @Override - public void matched(URI uri) - { - _context.getMetaData().addContainerResource(_resourceFactory.newResource(uri)); - } - } - - /** - * WebAppPathNameMatcher - * - * Matches names of jars or dirs on the webapp classpath - * against a pattern. If there is no pattern, all jars or dirs - * will match. - */ - public class WebAppPathNameMatcher extends PatternMatcher - { - protected final WebAppContext _context; - protected final String _pattern; - - public WebAppPathNameMatcher(WebAppContext context, String pattern) - { - if (context == null) - throw new IllegalArgumentException("Context null"); - _context = context; - _pattern = pattern; - } - - public void match(List uris) - throws Exception - { - match(_pattern, uris.toArray(new URI[uris.size()]), true); - } - - @Override - public void matched(URI uri) - { - _context.getMetaData().addWebInfResource(_resourceFactory.newResource(uri)); - } - } - /** * If set, to a list of URLs, these resources are added to the context * resource base as a resource collection. @@ -209,28 +145,29 @@ public class MetaInfConfiguration extends AbstractConfiguration // Apply an initial name filter to the jars to select which will be eventually // scanned for META-INF info and annotations. The filter is based on inclusion patterns. - ContainerPathNameMatcher containerPathNameMatcher = new ContainerPathNameMatcher(context, pattern); - List containerUris = getAllContainerJars(context); + UriPatternPredicate uriPatternPredicate = new UriPatternPredicate(pattern, false); + Consumer addContainerResource = (uri) -> + { + Resource resource = _resourceFactory.newResource(uri); + context.getMetaData().addContainerResource(resource); + }; + List containerUris = getAllContainerJars(context); if (LOG.isDebugEnabled()) LOG.debug("All container urls {}", containerUris); - containerPathNameMatcher.match(containerUris); + containerUris.stream() + .filter(uriPatternPredicate) + .forEach(addContainerResource); - // When running on jvm 9 or above, we we won't be able to look at the application + // When running on jvm 9 or above, we won't be able to look at the application // classloader to extract urls, so we need to examine the classpath instead. String classPath = System.getProperty("java.class.path"); if (classPath != null) { - List cpUris = new ArrayList<>(); - String[] entries = classPath.split(File.pathSeparator); - for (String entry : entries) - { - File f = new File(entry); - cpUris.add(f.toURI()); - } - if (LOG.isDebugEnabled()) - LOG.debug("Matching java.class.path {}", cpUris); - containerPathNameMatcher.match(cpUris); + Stream.of(classPath.split(File.pathSeparator)) + .map(URIUtil::toURI) + .filter(uriPatternPredicate) + .forEach(addContainerResource); } // We also need to examine the module path. @@ -240,30 +177,31 @@ public class MetaInfConfiguration extends AbstractConfiguration String modulePath = System.getProperty("jdk.module.path"); if (modulePath != null) { - List moduleUris = new ArrayList<>(); - String[] entries = modulePath.split(File.pathSeparator); - for (String entry : entries) + List matchingBasePaths = + Stream.of(modulePath.split(File.pathSeparator)) + .map(URIUtil::toURI) + .filter(uriPatternPredicate) + .map(Paths::get) + .toList(); + for (Path path: matchingBasePaths) { - File file = new File(entry); - if (file.isDirectory()) + if (Files.isDirectory(path)) { - File[] files = file.listFiles(); - if (files != null) + try (Stream listing = Files.list(path)) { - for (File f : files) + for (Path listEntry: listing.toList()) { - moduleUris.add(f.toURI()); + Resource resource = _resourceFactory.newResource(listEntry); + context.getMetaData().addContainerResource(resource); } } } else { - moduleUris.add(file.toURI()); + Resource resource = _resourceFactory.newResource(path); + context.getMetaData().addContainerResource(resource); } } - if (LOG.isDebugEnabled()) - LOG.debug("Matching jdk.module.path {}", moduleUris); - containerPathNameMatcher.match(moduleUris); } if (LOG.isDebugEnabled()) @@ -286,22 +224,18 @@ public class MetaInfConfiguration extends AbstractConfiguration { //Apply filter to WEB-INF/lib jars String pattern = (String)context.getAttribute(WEBINF_JAR_PATTERN); - WebAppPathNameMatcher matcher = new WebAppPathNameMatcher(context, pattern); + ResourceUriPatternPredicate webinfPredicate = new ResourceUriPatternPredicate(pattern, true); List jars = findJars(context); if (LOG.isDebugEnabled()) LOG.debug("webapp {}={} jars {}", WEBINF_JAR_PATTERN, pattern, jars); - //Convert to uris for matching + // Only add matching Resources to metadata.webInfResources if (jars != null) { - List uris = new ArrayList<>(); - int i = 0; - for (Resource r : jars) - { - uris.add(r.getURI()); - } - matcher.match(uris); + jars.stream() + .filter(webinfPredicate) + .forEach(resource -> context.getMetaData().addWebInfResource(resource)); } } diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/MetaInfConfiguration.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/MetaInfConfiguration.java index 9fbd2fa780c..45e878f0cad 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/MetaInfConfiguration.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/MetaInfConfiguration.java @@ -20,6 +20,7 @@ import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -33,16 +34,18 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; import org.eclipse.jetty.util.FileID; import org.eclipse.jetty.util.IO; -import org.eclipse.jetty.util.PatternMatcher; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.UriPatternPredicate; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.util.resource.ResourceUriPatternPredicate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -79,73 +82,6 @@ public class MetaInfConfiguration extends AbstractConfiguration public static final String WEBINF_JAR_PATTERN = "org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern"; public static final List __allScanTypes = Arrays.asList(METAINF_TLDS, METAINF_RESOURCES, METAINF_FRAGMENTS); - /** - * ContainerPathNameMatcher - * - * Matches names of jars on the container classpath - * against a pattern. If no pattern is specified, no - * jars match. - */ - public class ContainerPathNameMatcher extends PatternMatcher - { - protected final WebAppContext _context; - protected final String _pattern; - - public ContainerPathNameMatcher(WebAppContext context, String pattern) - { - if (context == null) - throw new IllegalArgumentException("Context null"); - _context = context; - _pattern = pattern; - } - - public void match(List uris) throws Exception - { - if (uris == null) - return; - match(_pattern, uris.toArray(new URI[uris.size()]), false); - } - - @Override - public void matched(URI uri) - { - _context.getMetaData().addContainerResource(_resourceFactory.newResource(uri)); - } - } - - /** - * WebAppPathNameMatcher - * - * Matches names of jars or dirs on the webapp classpath - * against a pattern. If there is no pattern, all jars or dirs - * will match. - */ - public class WebAppPathNameMatcher extends PatternMatcher - { - protected final WebAppContext _context; - protected final String _pattern; - - public WebAppPathNameMatcher(WebAppContext context, String pattern) - { - if (context == null) - throw new IllegalArgumentException("Context null"); - _context = context; - _pattern = pattern; - } - - public void match(List uris) - throws Exception - { - match(_pattern, uris.toArray(new URI[uris.size()]), true); - } - - @Override - public void matched(URI uri) - { - _context.getMetaData().addWebInfResource(_resourceFactory.newResource(uri)); - } - } - /** * If set, to a list of URLs, these resources are added to the context * resource base as a resource collection. @@ -202,33 +138,36 @@ public class MetaInfConfiguration extends AbstractConfiguration public void findAndFilterContainerPaths(final WebAppContext context) throws Exception { String pattern = (String)context.getAttribute(CONTAINER_JAR_PATTERN); + if (LOG.isDebugEnabled()) + LOG.debug("{}={}", CONTAINER_JAR_PATTERN, pattern); if (StringUtil.isBlank(pattern)) return; // TODO review if this short cut will allow later code simplifications // Apply an initial name filter to the jars to select which will be eventually // scanned for META-INF info and annotations. The filter is based on inclusion patterns. - ContainerPathNameMatcher containerPathNameMatcher = new ContainerPathNameMatcher(context, pattern); + UriPatternPredicate uriPatternPredicate = new UriPatternPredicate(pattern, false); + Consumer addContainerResource = (uri) -> + { + Resource resource = _resourceFactory.newResource(uri); + context.getMetaData().addContainerResource(resource); + }; + List containerUris = getAllContainerJars(context); - if (LOG.isDebugEnabled()) - LOG.debug("Matching container urls {}", containerUris); - containerPathNameMatcher.match(containerUris); + LOG.debug("All container urls {}", containerUris); + containerUris.stream() + .filter(uriPatternPredicate) + .forEach(addContainerResource); - // When running on jvm 9 or above, we we won't be able to look at the application + // When running on jvm 9 or above, we won't be able to look at the application // classloader to extract urls, so we need to examine the classpath instead. String classPath = System.getProperty("java.class.path"); if (classPath != null) { - List cpUris = new ArrayList<>(); - String[] entries = classPath.split(File.pathSeparator); - for (String entry : entries) - { - File f = new File(entry); - cpUris.add(f.toURI()); - } - if (LOG.isDebugEnabled()) - LOG.debug("Matching java.class.path {}", cpUris); - containerPathNameMatcher.match(cpUris); + Stream.of(classPath.split(File.pathSeparator)) + .map(URIUtil::toURI) + .filter(uriPatternPredicate) + .forEach(addContainerResource); } // We also need to examine the module path. @@ -238,30 +177,31 @@ public class MetaInfConfiguration extends AbstractConfiguration String modulePath = System.getProperty("jdk.module.path"); if (modulePath != null) { - List moduleUris = new ArrayList<>(); - String[] entries = modulePath.split(File.pathSeparator); - for (String entry : entries) + List matchingBasePaths = + Stream.of(modulePath.split(File.pathSeparator)) + .map(URIUtil::toURI) + .filter(uriPatternPredicate) + .map(Paths::get) + .toList(); + for (Path path: matchingBasePaths) { - File file = new File(entry); - if (file.isDirectory()) + if (Files.isDirectory(path)) { - File[] files = file.listFiles(); - if (files != null) + try (Stream listing = Files.list(path)) { - for (File f : files) + for (Path listEntry: listing.toList()) { - moduleUris.add(f.toURI()); + Resource resource = _resourceFactory.newResource(listEntry); + context.getMetaData().addContainerResource(resource); } } } else { - moduleUris.add(file.toURI()); + Resource resource = _resourceFactory.newResource(path); + context.getMetaData().addContainerResource(resource); } } - if (LOG.isDebugEnabled()) - LOG.debug("Matching jdk.module.path {}", moduleUris); - containerPathNameMatcher.match(moduleUris); } if (LOG.isDebugEnabled()) @@ -283,19 +223,19 @@ public class MetaInfConfiguration extends AbstractConfiguration throws Exception { //Apply filter to WEB-INF/lib jars - WebAppPathNameMatcher matcher = new WebAppPathNameMatcher(context, (String)context.getAttribute(WEBINF_JAR_PATTERN)); + String pattern = (String)context.getAttribute(WEBINF_JAR_PATTERN); + ResourceUriPatternPredicate webinfPredicate = new ResourceUriPatternPredicate(pattern, true); List jars = findJars(context); + if (LOG.isDebugEnabled()) + LOG.debug("webapp {}={} jars {}", WEBINF_JAR_PATTERN, pattern, jars); - //Convert to uris for matching + // Only add matching Resources to metadata.webInfResources if (jars != null) { - List uris = new ArrayList<>(); - for (Resource r : jars) - { - uris.add(r.getURI()); - } - matcher.match(uris); + jars.stream() + .filter(webinfPredicate) + .forEach(resource -> context.getMetaData().addWebInfResource(resource)); } }