Issue #5129 - Reverting Resource.fromReferences()

+ Will migrate to jetty-10.0.x

Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
This commit is contained in:
Joakim Erdfelt 2020-08-11 08:15:27 -05:00
parent 5b8c343a2b
commit 150dc22f0a
No known key found for this signature in database
GPG Key ID: 2D0E1FB8FE4B68B4
3 changed files with 169 additions and 153 deletions

View File

@ -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.
* <p>
* This is different then {@link ResourceFactory} in that
* it must return a {@link Resource} or throw an IOException,
* never null.
* </p>
*
* @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.
* <p>
* 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.
* </p>
*
* @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<Resource> 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.
* <p>
* 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.
* </p>
*
* @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<Resource> fromReferences(String delimitedReferences, Resource.Factory resourceFactory) throws IOException
{
if (StringUtil.isBlank(delimitedReferences))
{
return Collections.emptyList();
}
List<Resource> 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;
}
}

View File

@ -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);
}
}
}

View File

@ -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<Resource> 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<Resource> 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('.');