diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java index 5939d58cfd3..34bbbfc23e7 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java @@ -32,13 +32,11 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.text.DateFormat; import java.util.ArrayList; -import java.util.Arrays; import java.util.Base64; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; -import java.util.StringTokenizer; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.Loader; @@ -996,107 +994,4 @@ public abstract class Resource implements ResourceFactory, Closeable { return file.toURI().toURL(); } - - /** - * Factory to create new Resource instance from a reference. - */ - public interface Factory - { - /** - * Create a new Resource from the factory's point of view. - *

- * This is different then {@link ResourceFactory} in that - * it must return a {@link Resource} or throw an IOException, - * never null. - *

- * - * @param reference the string reference. - * @return the Resource instance - * @throws IOException if unable to create a Resource reference - */ - Resource newResource(String reference) throws IOException; - } - - /** - * Parse a delimited String of resource references and - * return the List of Resources instances it represents. - *

- * Supports glob references that end in {@code /*} or {@code \*}. - * Glob references will only iterate through the level specified and will not traverse - * found directories within the glob reference. - *

- * - * @param delimitedReferences the comma {@code ,} or semicolon {@code ;} delimited - * String of resource references. - * @return the list of resources parsed from input string. - */ - public static List fromReferences(String delimitedReferences) throws IOException - { - return fromReferences(delimitedReferences, Resource::newResource); - } - - /** - * Parse a delimited String of resource references and - * return the List of Resources instances it represents. - *

- * Supports glob references that end in {@code /*} or {@code \*}. - * Glob references will only iterate through the level specified and will not traverse - * found directories within the glob reference. - *

- * - * @param delimitedReferences the comma {@code ,} or semicolon {@code ;} delimited - * String of resource references. - * @param resourceFactory the Resource.Factory used to create new Resource references - * @return the list of resources parsed from input string. - */ - public static List fromReferences(String delimitedReferences, Resource.Factory resourceFactory) throws IOException - { - if (StringUtil.isBlank(delimitedReferences)) - { - return Collections.emptyList(); - } - - List resources = new ArrayList<>(); - - StringTokenizer tokenizer = new StringTokenizer(delimitedReferences, ",;"); - while (tokenizer.hasMoreTokens()) - { - String token = tokenizer.nextToken().trim(); - - // Is this a glob reference? - if (token.endsWith("/*") || token.endsWith("\\*")) - { - String dir = token.substring(0, token.length() - 2); - // Use directory - Resource dirResource = resourceFactory.newResource(dir); - if (dirResource.exists() && dirResource.isDirectory()) - { - // To obtain the list of entries - String[] entries = dirResource.list(); - if (entries != null) - { - Arrays.sort(entries); - for (String entry : entries) - { - try - { - resources.add(dirResource.addPath(entry)); - } - catch (Exception ex) - { - LOG.warn(Log.EXCEPTION, ex); - } - } - } - } - } - else - { - // Simple reference, add as-is - resources.add(resourceFactory.newResource(token)); - } - } - - return resources; - } } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java index 72cfea85210..8d9e5137384 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java @@ -234,23 +234,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility } else { - // Resolve file path if possible - File file = resource.getFile(); - if (file != null) - { - URL url = resource.getURI().toURL(); - addURL(url); - } - else if (resource.isDirectory()) - { - addURL(resource.getURI().toURL()); - } - else - { - if (LOG.isDebugEnabled()) - LOG.debug("Check file exists and is not nested jar: " + resource); - throw new IllegalArgumentException("File not resolvable or incompatible with URLClassloader: " + resource); - } + addClassPath(resource.toString()); } } @@ -266,9 +250,53 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility if (classPath == null) return; - for (Resource resource : Resource.fromReferences(classPath, _context::newResource)) + StringTokenizer tokenizer = new StringTokenizer(classPath, ",;"); + while (tokenizer.hasMoreTokens()) { - addClassPath(resource); + String token = tokenizer.nextToken().trim(); + + if (token.endsWith("*")) + { + if (token.length() > 1) + { + token = token.substring(0, token.length() - 1); + Resource resource = _context.newResource(token); + if (LOG.isDebugEnabled()) + LOG.debug("Glob Path resource=" + resource); + resource = _context.newResource(token); + addJars(resource); + } + return; + } + + Resource resource = _context.newResource(token); + if (LOG.isDebugEnabled()) + LOG.debug("Path resource=" + resource); + + if (resource.isDirectory() && resource instanceof ResourceCollection) + { + addClassPath(resource); + } + else + { + // Resolve file path if possible + File file = resource.getFile(); + if (file != null) + { + URL url = resource.getURI().toURL(); + addURL(url); + } + else if (resource.isDirectory()) + { + addURL(resource.getURI().toURL()); + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("Check file exists and is not nested jar: " + resource); + throw new IllegalArgumentException("File not resolvable or incompatible with URLClassloader: " + resource); + } + } } } @@ -291,29 +319,37 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility { if (lib.exists() && lib.isDirectory()) { - String[] files = lib.list(); - if (files != null) + String[] entries = lib.list(); + if (entries != null) { - Arrays.sort(files); - } - for (int f = 0; files != null && f < files.length; f++) - { - try + Arrays.sort(entries); + + for (String entry : entries) { - Resource fn = lib.addPath(files[f]); - if (LOG.isDebugEnabled()) - LOG.debug("addJar - {}", fn); - String fnlc = fn.getName().toLowerCase(Locale.ENGLISH); - // don't check if this is a directory (prevents use of symlinks), see Bug 353165 - if (isFileSupported(fnlc)) + try { - String jar = URIUtil.encodeSpecific(fn.toString(), ",;"); - addClassPath(jar); + Resource resource = lib.addPath(entry); + if (LOG.isDebugEnabled()) + LOG.debug("addJar - {}", resource); + if (resource.isDirectory()) + { + addURL(resource.getURI().toURL()); + } + else + { + String fnlc = resource.getName().toLowerCase(Locale.ENGLISH); + // don't check if this is a directory (prevents use of symlinks), see Bug 353165 + if (isFileSupported(fnlc)) + { + String jar = URIUtil.encodeSpecific(resource.toString(), ",;"); + addClassPath(jar); + } + } + } + catch (Exception ex) + { + LOG.warn(Log.EXCEPTION, ex); } - } - catch (Exception ex) - { - LOG.warn(Log.EXCEPTION, ex); } } } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java index 7d016c55429..ad8aabab01b 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java @@ -29,8 +29,8 @@ import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Set; +import java.util.StringTokenizer; import java.util.regex.Pattern; -import java.util.stream.Collectors; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.NetworkConnector; @@ -943,10 +943,55 @@ public class WebInfConfiguration extends AbstractConfiguration if (context == null || context.getExtraClasspath() == null) return null; - return Resource.fromReferences(context.getExtraClasspath()) - .stream() - .filter(WebInfConfiguration::isFileSupported) - .collect(Collectors.toList()); + List jarResources = new ArrayList<>(); + StringTokenizer tokenizer = new StringTokenizer(context.getExtraClasspath(), ",;"); + while (tokenizer.hasMoreTokens()) + { + String token = tokenizer.nextToken().trim(); + + // Is this a Glob Reference? + if (isGlobReference(token)) + { + String dir = token.substring(0, token.length() - 2); + // Use directory + Resource dirResource = context.newResource(dir); + if (dirResource.exists() && dirResource.isDirectory()) + { + // To obtain the list of files + String[] entries = dirResource.list(); + if (entries != null) + { + Arrays.sort(entries); + for (String entry : entries) + { + try + { + Resource fileResource = dirResource.addPath(entry); + if (isFileSupported(fileResource)) + { + jarResources.add(fileResource); + } + } + catch (Exception ex) + { + LOG.warn(Log.EXCEPTION, ex); + } + } + } + } + } + else + { + // Simple reference, add as-is + Resource resource = context.newResource(token); + if (isFileSupported(resource)) + { + jarResources.add(resource); + } + } + } + + return jarResources; } /** @@ -988,13 +1033,53 @@ public class WebInfConfiguration extends AbstractConfiguration if (context == null || context.getExtraClasspath() == null) return null; - return Resource.fromReferences(context.getExtraClasspath()) - .stream() - .filter(Resource::isDirectory) - .collect(Collectors.toList()); + List dirResources = new ArrayList<>(); + StringTokenizer tokenizer = new StringTokenizer(context.getExtraClasspath(), ",;"); + while (tokenizer.hasMoreTokens()) + { + String token = tokenizer.nextToken().trim(); + if (isGlobReference(token)) + { + String dir = token.substring(0, token.length() - 2); + // Use directory + Resource dirResource = context.newResource(dir); + if (dirResource.exists() && dirResource.isDirectory()) + { + // To obtain the list of files + String[] entries = dirResource.list(); + if (entries != null) + { + Arrays.sort(entries); + for (String entry : entries) + { + Resource resource = dirResource.addPath(entry); + if (resource.isDirectory()) + { + dirResources.add(resource); + } + } + } + } + } + else + { + Resource resource = context.newResource(token); + if (resource.exists() && resource.isDirectory()) + { + dirResources.add(resource); + } + } + } + + return dirResources; } - private static boolean isFileSupported(Resource resource) + private boolean isGlobReference(String token) + { + return token.endsWith("/*") || token.endsWith("\\*"); + } + + private boolean isFileSupported(Resource resource) { String filenameLowercase = resource.getName().toLowerCase(Locale.ENGLISH); int dot = filenameLowercase.lastIndexOf('.');