Issue #5133 - Reworking WebAppContext.extraClasspath
+ Now parsed by WebAppContext into List<Resource> + Reintroduced Resource.fromList + Refactored ResourceFactory to never return null and always throw an exception if unable to get/create/resolve the Resource Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
This commit is contained in:
parent
ccdd20b7bc
commit
0b0d7d3282
|
@ -32,7 +32,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.Constants;
|
||||
|
||||
|
@ -145,7 +144,7 @@ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationPa
|
|||
}
|
||||
});
|
||||
boolean hasDotPath = false;
|
||||
StringTokenizer tokenizer = new StringTokenizer(bundleClasspath, ",;", false);
|
||||
StringTokenizer tokenizer = new StringTokenizer(bundleClasspath, StringUtil.DEFAULT_DELIMS, false);
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
String token = tokenizer.nextToken().trim();
|
||||
|
|
|
@ -233,7 +233,7 @@ public class ServerInstanceWrapper
|
|||
Thread.currentThread().setContextClassLoader(libExtClassLoader);
|
||||
|
||||
String jettyConfigurationUrls = (String)props.get(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS);
|
||||
List<URL> jettyConfigurations = jettyConfigurationUrls != null ? Util.fileNamesAsURLs(jettyConfigurationUrls, Util.DEFAULT_DELIMS) : null;
|
||||
List<URL> jettyConfigurations = jettyConfigurationUrls != null ? Util.fileNamesAsURLs(jettyConfigurationUrls, StringUtil.DEFAULT_DELIMS) : null;
|
||||
|
||||
_server = configure(server, jettyConfigurations, props);
|
||||
|
||||
|
@ -418,7 +418,7 @@ public class ServerInstanceWrapper
|
|||
|
||||
List<URL> libURLs = new ArrayList<>();
|
||||
|
||||
StringTokenizer tokenizer = new StringTokenizer(sharedURLs, ",;", false);
|
||||
StringTokenizer tokenizer = new StringTokenizer(sharedURLs, StringUtil.DEFAULT_DELIMS, false);
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
String tok = tokenizer.nextToken();
|
||||
|
|
|
@ -23,12 +23,10 @@ import java.io.IOException;
|
|||
import java.lang.reflect.Field;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.jar.JarFile;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
|
||||
|
@ -201,22 +199,16 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
|
|||
@Override
|
||||
public void addClassPath(String classPath) throws IOException
|
||||
{
|
||||
|
||||
StringTokenizer tokenizer = new StringTokenizer(classPath, ",;");
|
||||
while (tokenizer.hasMoreTokens())
|
||||
for (Resource resource : Resource.fromList(classPath, false, (path) -> getContext().newResource(path)))
|
||||
{
|
||||
String path = tokenizer.nextToken();
|
||||
Resource resource = getContext().newResource(path);
|
||||
|
||||
// Resolve file path if possible
|
||||
File file = resource.getFile();
|
||||
if (file != null && isAcceptableLibrary(file, JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED))
|
||||
{
|
||||
super.addClassPath(path);
|
||||
super.addClassPath(resource);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.info("Did not add " + path + " to the classloader of the webapp " + getContext());
|
||||
LOG.info("Did not add {} to the classloader of the webapp {}", resource, getContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -274,35 +266,4 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
|
|||
}
|
||||
|
||||
private static Field _contextField;
|
||||
|
||||
/**
|
||||
* In the case of the generation of a webapp via a jetty context file we
|
||||
* need a proper classloader to setup the app before we have the
|
||||
* WebappContext So we place a fake one there to start with. We replace it
|
||||
* with the actual webapp context with this method. We also apply the
|
||||
* extraclasspath there at the same time.
|
||||
*
|
||||
* @param webappContext the web app context
|
||||
*/
|
||||
public void setWebappContext(WebAppContext webappContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_contextField == null)
|
||||
{
|
||||
_contextField = WebAppClassLoader.class.getDeclaredField("_context");
|
||||
_contextField.setAccessible(true);
|
||||
}
|
||||
_contextField.set(this, webappContext);
|
||||
if (webappContext.getExtraClasspath() != null)
|
||||
{
|
||||
addClassPath(webappContext.getExtraClasspath());
|
||||
}
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
// humf that will hurt if it does not work.
|
||||
LOG.warn("Unable to set webappcontext", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,6 @@ import org.osgi.framework.InvalidSyntaxException;
|
|||
*/
|
||||
public class Util
|
||||
{
|
||||
public static final String DEFAULT_DELIMS = ",;";
|
||||
|
||||
/**
|
||||
* Create an osgi filter for the given classname and server name.
|
||||
*
|
||||
|
@ -101,7 +99,7 @@ public class Util
|
|||
public static List<URL> fileNamesAsURLs(String val, String delims)
|
||||
throws Exception
|
||||
{
|
||||
String separators = DEFAULT_DELIMS;
|
||||
String separators = StringUtil.DEFAULT_DELIMS;
|
||||
if (delims == null)
|
||||
delims = separators;
|
||||
|
||||
|
|
|
@ -188,7 +188,6 @@ public class CachedContentFactory implements HttpContent.ContentFactory
|
|||
if (_parent != null)
|
||||
{
|
||||
HttpContent httpContent = _parent.getContent(pathInContext, maxBufferSize);
|
||||
if (httpContent != null)
|
||||
return httpContent;
|
||||
}
|
||||
|
||||
|
@ -234,6 +233,8 @@ public class CachedContentFactory implements HttpContent.ContentFactory
|
|||
if (compressedContent == null || compressedContent.isValid())
|
||||
{
|
||||
compressedContent = null;
|
||||
try
|
||||
{
|
||||
Resource compressedResource = _factory.getResource(compressedPathInContext);
|
||||
if (compressedResource.exists() && compressedResource.lastModified() >= resource.lastModified() &&
|
||||
compressedResource.length() < resource.length())
|
||||
|
@ -247,6 +248,12 @@ public class CachedContentFactory implements HttpContent.ContentFactory
|
|||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Unable to find compressed path in context: {}", compressedPathInContext, e);
|
||||
}
|
||||
}
|
||||
if (compressedContent != null)
|
||||
precompresssedContents.put(format, compressedContent);
|
||||
}
|
||||
|
@ -279,6 +286,8 @@ public class CachedContentFactory implements HttpContent.ContentFactory
|
|||
if (compressedContent != null && compressedContent.isValid() && compressedContent.getResource().lastModified() >= resource.lastModified())
|
||||
compressedContents.put(format, compressedContent);
|
||||
|
||||
try
|
||||
{
|
||||
// Is there a precompressed resource?
|
||||
Resource compressedResource = _factory.getResource(compressedPathInContext);
|
||||
if (compressedResource.exists() && compressedResource.lastModified() >= resource.lastModified() &&
|
||||
|
@ -286,6 +295,12 @@ public class CachedContentFactory implements HttpContent.ContentFactory
|
|||
compressedContents.put(format,
|
||||
new ResourceHttpContent(compressedResource, _mimeTypes.getMimeByExtension(compressedPathInContext), maxBufferSize));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Unable to find compressed path in context: {}", compressedPathInContext, e);
|
||||
}
|
||||
}
|
||||
if (!compressedContents.isEmpty())
|
||||
return new ResourceHttpContent(resource, mt, maxBufferSize, compressedContents);
|
||||
}
|
||||
|
|
|
@ -51,14 +51,12 @@ public class ResourceContentFactory implements ContentFactory
|
|||
|
||||
@Override
|
||||
public HttpContent getContent(String pathInContext, int maxBufferSize)
|
||||
throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
// try loading the content from our factory.
|
||||
Resource resource = _factory.getResource(pathInContext);
|
||||
HttpContent loaded = load(pathInContext, resource, maxBufferSize);
|
||||
return loaded;
|
||||
return load(pathInContext, resource, maxBufferSize);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
|
|
|
@ -20,7 +20,7 @@ package org.eclipse.jetty.server.handler;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -74,7 +74,7 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory,
|
|||
{
|
||||
}
|
||||
});
|
||||
_resourceService.setGzipEquivalentFileExtensions(new ArrayList<>(Arrays.asList(new String[]{".svgz"})));
|
||||
_resourceService.setGzipEquivalentFileExtensions(new ArrayList<>(Collections.singletonList(".svgz")));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -86,10 +86,19 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory,
|
|||
for (int i = 0; i < _welcomes.length; i++)
|
||||
{
|
||||
String welcomeInContext = URIUtil.addPaths(pathInContext, _welcomes[i]);
|
||||
try
|
||||
{
|
||||
Resource welcome = getResource(welcomeInContext);
|
||||
if (welcome != null && welcome.exists())
|
||||
if (welcome.exists())
|
||||
return welcomeInContext;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// this happens on a critical failure of Resource
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Failed to resolve welcome file: {}", welcomeInContext);
|
||||
}
|
||||
}
|
||||
// not found
|
||||
return null;
|
||||
}
|
||||
|
@ -140,13 +149,19 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory,
|
|||
}
|
||||
|
||||
@Override
|
||||
public Resource getResource(String path)
|
||||
public Resource getResource(String path) throws IOException
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} getResource({})", _context == null ? _baseResource : _context, _baseResource, path);
|
||||
LOG.debug("{} newResource({}): baseResource:{}", _context == null ? _baseResource : _context, path, _baseResource);
|
||||
|
||||
if (path == null || !path.startsWith("/"))
|
||||
return null;
|
||||
if (path == null)
|
||||
{
|
||||
throw new IOException("null path");
|
||||
}
|
||||
else if (!path.startsWith("/"))
|
||||
{
|
||||
throw new IOException("Invalid path reference: " + path);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -161,7 +176,7 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory,
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("resource={} alias={}", r, r.getAlias());
|
||||
return null;
|
||||
throw new IOException("Unacceptable alias reference: " + r);
|
||||
}
|
||||
}
|
||||
else if (_context != null)
|
||||
|
@ -170,6 +185,7 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory,
|
|||
if ((r == null || !r.exists()) && path.endsWith("/jetty-dir.css"))
|
||||
r = getStylesheet();
|
||||
|
||||
if (r != null)
|
||||
return r;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -177,7 +193,7 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory,
|
|||
LOG.debug("Unable to get Resource for {}", path, e);
|
||||
}
|
||||
|
||||
return null;
|
||||
throw new IOException("Unable to find Resource for " + path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -37,6 +37,7 @@ public class StringUtil
|
|||
|
||||
public static final String ALL_INTERFACES = "0.0.0.0";
|
||||
public static final String CRLF = "\r\n";
|
||||
public static final String DEFAULT_DELIMS = ",;";
|
||||
|
||||
public static final String __ISO_8859_1 = "iso-8859-1";
|
||||
public static final String __UTF8 = "utf-8";
|
||||
|
|
|
@ -34,10 +34,13 @@ import java.nio.file.Paths;
|
|||
import java.nio.file.StandardCopyOption;
|
||||
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;
|
||||
|
@ -419,23 +422,12 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
|
||||
/**
|
||||
* Get a resource from within this resource.
|
||||
* <p>
|
||||
* This method is essentially an alias for {@link #addPath(String)}, but without checked exceptions.
|
||||
* This method satisfied the {@link ResourceFactory} interface.
|
||||
*/
|
||||
@Override
|
||||
public Resource getResource(String path)
|
||||
{
|
||||
try
|
||||
public Resource getResource(String path) throws IOException
|
||||
{
|
||||
return addPath(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.debug("Unable to addPath", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this appears to not be used
|
||||
@SuppressWarnings("javadoc")
|
||||
|
@ -921,4 +913,97 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
{
|
||||
return file.toURI().toURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a list of String delimited resources 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 globDirs true if glob references return directories within the glob as well
|
||||
* @return the list of resources parsed from input string.
|
||||
*/
|
||||
public static List<Resource> fromList(String delimitedReferences, boolean globDirs) throws IOException
|
||||
{
|
||||
return fromList(delimitedReferences, globDirs, 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 globDirs true if glob references return directories within the glob as well
|
||||
* @param resourceFactory the ResourceFactory used to create new Resource references
|
||||
* @return the list of resources parsed from input string.
|
||||
*/
|
||||
public static List<Resource> fromList(String delimitedReferences, boolean globDirs, ResourceFactory resourceFactory) throws IOException
|
||||
{
|
||||
if (StringUtil.isBlank(delimitedReferences))
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Resource> resources = new ArrayList<>();
|
||||
|
||||
StringTokenizer tokenizer = new StringTokenizer(delimitedReferences, StringUtil.DEFAULT_DELIMS);
|
||||
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.getResource(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
|
||||
{
|
||||
Resource resource = dirResource.addPath(entry);
|
||||
if (!resource.isDirectory())
|
||||
{
|
||||
resources.add(resource);
|
||||
}
|
||||
else if (globDirs)
|
||||
{
|
||||
resources.add(resource);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LOG.warn("Bad glob [{}] entry: {}", token, entry, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Simple reference, add as-is
|
||||
resources.add(resourceFactory.getResource(token));
|
||||
}
|
||||
}
|
||||
|
||||
return resources;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,8 +29,8 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
||||
/**
|
||||
|
@ -131,8 +131,9 @@ public class ResourceCollection extends Resource
|
|||
* Instantiates a new resource collection.
|
||||
*
|
||||
* @param csvResources the string containing comma-separated resource strings
|
||||
* @throws IOException if any listed resource is not valid
|
||||
*/
|
||||
public ResourceCollection(String csvResources)
|
||||
public ResourceCollection(String csvResources) throws IOException
|
||||
{
|
||||
setResourcesAsCSV(csvResources);
|
||||
}
|
||||
|
@ -147,6 +148,22 @@ public class ResourceCollection extends Resource
|
|||
return _resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resource collection's resources.
|
||||
*
|
||||
* @param res the resources to set
|
||||
*/
|
||||
public void setResources(List<Resource> res)
|
||||
{
|
||||
if (res.isEmpty())
|
||||
{
|
||||
_resources = null;
|
||||
return;
|
||||
}
|
||||
|
||||
_resources = res.toArray(new Resource[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resource collection's resources.
|
||||
*
|
||||
|
@ -167,13 +184,7 @@ public class ResourceCollection extends Resource
|
|||
res.add(resource);
|
||||
}
|
||||
|
||||
if (res.isEmpty())
|
||||
{
|
||||
_resources = null;
|
||||
return;
|
||||
}
|
||||
|
||||
_resources = res.toArray(new Resource[0]);
|
||||
setResources(res);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -182,56 +193,22 @@ public class ResourceCollection extends Resource
|
|||
*
|
||||
* @param csvResources the comma-separated string containing
|
||||
* one or more resource strings.
|
||||
* @throws IOException if unable resource declared is not valid
|
||||
*/
|
||||
public void setResourcesAsCSV(String csvResources)
|
||||
public void setResourcesAsCSV(String csvResources) throws IOException
|
||||
{
|
||||
if (csvResources == null)
|
||||
if (StringUtil.isBlank(csvResources))
|
||||
{
|
||||
throw new IllegalArgumentException("CSV String is null");
|
||||
}
|
||||
|
||||
StringTokenizer tokenizer = new StringTokenizer(csvResources, ",;");
|
||||
int len = tokenizer.countTokens();
|
||||
if (len == 0)
|
||||
{
|
||||
throw new IllegalArgumentException("ResourceCollection@setResourcesAsCSV(String) " +
|
||||
" argument must be a string containing one or more comma-separated resource strings.");
|
||||
throw new IllegalArgumentException("CSV String is blank");
|
||||
}
|
||||
|
||||
List<Resource> res = new ArrayList<>();
|
||||
|
||||
try
|
||||
for (Resource resource : Resource.fromList(csvResources, false))
|
||||
{
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
String token = tokenizer.nextToken().trim();
|
||||
// TODO: If we want to support CSV tokens with spaces then we should not trim here
|
||||
// However, if we decide to to this, then CVS formatting/syntax becomes more strict.
|
||||
if (token.length() == 0)
|
||||
{
|
||||
continue; // skip
|
||||
}
|
||||
Resource resource = Resource.newResource(token);
|
||||
assertResourceValid(resource);
|
||||
res.add(resource);
|
||||
}
|
||||
|
||||
if (res.isEmpty())
|
||||
{
|
||||
_resources = null;
|
||||
return;
|
||||
}
|
||||
|
||||
_resources = res.toArray(new Resource[0]);
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
setResources(res);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,17 +18,26 @@
|
|||
|
||||
package org.eclipse.jetty.util.resource;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* ResourceFactory.
|
||||
*/
|
||||
public interface ResourceFactory
|
||||
{
|
||||
|
||||
/**
|
||||
* Get a resource for a path.
|
||||
* Get a Resource from a provided String.
|
||||
* <p>
|
||||
* The behavior here is dependent on the
|
||||
* implementation of ResourceFactory.
|
||||
* The provided path can be resolved
|
||||
* against a known Resource, or can
|
||||
* be a from-scratch Resource.
|
||||
* </p>
|
||||
*
|
||||
* @param path The path to the resource
|
||||
* @return The resource or null
|
||||
* @return The resource
|
||||
* @throws IOException if unable to create Resource
|
||||
*/
|
||||
Resource getResource(String path);
|
||||
Resource getResource(String path) throws IOException;
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ public class ResourceCollectionTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSetResourceNullThrowsISE()
|
||||
public void testSetResourceArrayNullThrowsISE()
|
||||
{
|
||||
// Create a ResourceCollection with one valid entry
|
||||
Path path = MavenTestingUtils.getTargetPath();
|
||||
|
@ -121,7 +121,7 @@ public class ResourceCollectionTest
|
|||
ResourceCollection coll = new ResourceCollection(resource);
|
||||
|
||||
// Reset collection to invalid state
|
||||
coll.setResources(null);
|
||||
coll.setResources((Resource[])null);
|
||||
|
||||
assertThrowIllegalStateException(coll);
|
||||
}
|
||||
|
|
|
@ -36,13 +36,12 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jetty.util.PatternMatcher;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.resource.EmptyResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
|
@ -802,7 +801,7 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
*
|
||||
* @param context the context to find extra classpath jars in
|
||||
* @return the list of Resources with the extra classpath, or null if not found
|
||||
* @throws Exception if unable to find the extra classpath jars
|
||||
* @throws Exception if unable to resolve the extra classpath jars
|
||||
*/
|
||||
protected List<Resource> findExtraClasspathJars(WebAppContext context)
|
||||
throws Exception
|
||||
|
@ -810,55 +809,10 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
if (context == null || context.getExtraClasspath() == null)
|
||||
return null;
|
||||
|
||||
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;
|
||||
return context.getExtraClasspath()
|
||||
.stream()
|
||||
.filter(this::isFileSupported)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -892,7 +846,7 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
*
|
||||
* @param context the context to look for extra classpaths in
|
||||
* @return the list of Resources to the extra classpath
|
||||
* @throws Exception if unable to find the extra classpaths
|
||||
* @throws Exception if unable to resolve the extra classpath resources
|
||||
*/
|
||||
protected List<Resource> findExtraClasspathDirs(WebAppContext context)
|
||||
throws Exception
|
||||
|
@ -900,23 +854,10 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
if (context == null || context.getExtraClasspath() == null)
|
||||
return null;
|
||||
|
||||
List<Resource> dirResources = new ArrayList<>();
|
||||
StringTokenizer tokenizer = new StringTokenizer(context.getExtraClasspath(), ",;");
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
String token = tokenizer.nextToken().trim();
|
||||
// ignore glob references, they only refer to lists of jars/zips anyway
|
||||
if (!isGlobReference(token))
|
||||
{
|
||||
Resource resource = context.newResource(token);
|
||||
if (resource.exists() && resource.isDirectory())
|
||||
{
|
||||
dirResources.add(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dirResources;
|
||||
return context.getExtraClasspath()
|
||||
.stream()
|
||||
.filter(Resource::isDirectory)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private String uriJarPrefix(URI uri, String suffix)
|
||||
|
@ -932,13 +873,23 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
}
|
||||
}
|
||||
|
||||
private boolean isGlobReference(String token)
|
||||
{
|
||||
return token.endsWith("/*") || token.endsWith("\\*");
|
||||
}
|
||||
|
||||
private boolean isFileSupported(Resource resource)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (resource.isDirectory())
|
||||
return false;
|
||||
|
||||
if (resource.getFile() == null)
|
||||
return false;
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Bad Resource reference: {}", resource, t);
|
||||
return false;
|
||||
}
|
||||
|
||||
String filenameLowercase = resource.getName().toLowerCase(Locale.ENGLISH);
|
||||
int dot = filenameLowercase.lastIndexOf('.');
|
||||
String extension = (dot < 0 ? null : filenameLowercase.substring(dot));
|
||||
|
|
|
@ -41,6 +41,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
|
||||
import org.eclipse.jetty.util.ClassVisibilityChecker;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
|
@ -112,7 +113,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
*/
|
||||
boolean isParentLoaderPriority();
|
||||
|
||||
String getExtraClasspath();
|
||||
List<Resource> getExtraClasspath();
|
||||
|
||||
boolean isServerResource(String name, URL parentUrl);
|
||||
|
||||
|
@ -185,7 +186,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
String extensions = System.getProperty(WebAppClassLoader.class.getName() + ".extensions");
|
||||
if (extensions != null)
|
||||
{
|
||||
StringTokenizer tokenizer = new StringTokenizer(extensions, ",;");
|
||||
StringTokenizer tokenizer = new StringTokenizer(extensions, StringUtil.DEFAULT_DELIMS);
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
_extensions.add(tokenizer.nextToken().trim());
|
||||
|
@ -193,7 +194,12 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
}
|
||||
|
||||
if (context.getExtraClasspath() != null)
|
||||
addClassPath(context.getExtraClasspath());
|
||||
{
|
||||
for (Resource resource : context.getExtraClasspath())
|
||||
{
|
||||
addClassPath(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -234,51 +240,6 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
addClassPath(resource.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param classPath Comma or semicolon separated path of filenames or URLs
|
||||
* pointing to directories or jar files. Directories should end
|
||||
* with '/'.
|
||||
* @throws IOException if unable to add classpath
|
||||
*/
|
||||
public void addClassPath(String classPath)
|
||||
throws IOException
|
||||
{
|
||||
if (classPath == null)
|
||||
return;
|
||||
|
||||
StringTokenizer tokenizer = new StringTokenizer(classPath, ",;");
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
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();
|
||||
|
@ -299,6 +260,23 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param classPath Comma or semicolon separated path of filenames or URLs
|
||||
* pointing to directories or jar files. Directories should end
|
||||
* with '/'.
|
||||
* @throws IOException if unable to add classpath
|
||||
*/
|
||||
public void addClassPath(String classPath)
|
||||
throws IOException
|
||||
{
|
||||
if (classPath == null)
|
||||
return;
|
||||
|
||||
for (Resource resource : Resource.fromList(classPath, false, _context::newResource))
|
||||
{
|
||||
addClassPath(resource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -204,7 +204,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
private boolean _persistTmpDir = false;
|
||||
|
||||
private String _war;
|
||||
private String _extraClasspath;
|
||||
private List<Resource> _extraClasspath;
|
||||
private Throwable _unavailableException;
|
||||
|
||||
private Map<String, String> _resourceAliases;
|
||||
|
@ -1227,17 +1227,29 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
*/
|
||||
@Override
|
||||
@ManagedAttribute(value = "extra classpath for context classloader", readonly = true)
|
||||
public String getExtraClasspath()
|
||||
public List<Resource> getExtraClasspath()
|
||||
{
|
||||
return _extraClasspath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Extra ClassPath via delimited String.
|
||||
* <p>
|
||||
* This is a convenience method for {@link #setExtraClasspath(List)}
|
||||
* </p>
|
||||
*
|
||||
* @param extraClasspath Comma or semicolon separated path of filenames or URLs
|
||||
* pointing to directories or jar files. Directories should end
|
||||
* with '/'.
|
||||
* @throws IOException if unable to resolve the resources referenced
|
||||
* @see #setExtraClasspath(List)
|
||||
*/
|
||||
public void setExtraClasspath(String extraClasspath)
|
||||
public void setExtraClasspath(String extraClasspath) throws IOException
|
||||
{
|
||||
setExtraClasspath(Resource.fromList(extraClasspath, false, this::newResource));
|
||||
}
|
||||
|
||||
public void setExtraClasspath(List<Resource> extraClasspath)
|
||||
{
|
||||
_extraClasspath = extraClasspath;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue