ResourceCollection should not have a path (#8711)
ResourceCollection should not have a path Nor name, nor filename unless all resources agree on it. revert combine and related methods to return Resource and not explicitly a ResourceCollection, as if there is only 1, then a collection is not needed cleanup ResourceCollection creation. Avoid sanity checks on resolved resources.
This commit is contained in:
parent
4225054b62
commit
976ab3df8d
|
@ -11,7 +11,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.ee9.quickstart;
|
||||
package org.eclipse.jetty.util.resource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -22,18 +22,15 @@ import java.nio.file.FileSystems;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -165,81 +162,79 @@ public class AttributeNormalizer
|
|||
}
|
||||
}
|
||||
|
||||
private static Comparator<Attribute> attrComparator = new Comparator<Attribute>()
|
||||
private static final Comparator<Attribute> attrComparator = (o1, o2) ->
|
||||
{
|
||||
@Override
|
||||
public int compare(Attribute o1, Attribute o2)
|
||||
if ((o1.value == null) && (o2.value != null))
|
||||
{
|
||||
if ((o1.value == null) && (o2.value != null))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((o1.value != null) && (o2.value == null))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((o1.value == null) && (o2.value == null))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Different lengths?
|
||||
int diff = o2.value.length() - o1.value.length();
|
||||
if (diff != 0)
|
||||
{
|
||||
return diff;
|
||||
}
|
||||
|
||||
// Different names?
|
||||
diff = o2.value.compareTo(o1.value);
|
||||
if (diff != 0)
|
||||
{
|
||||
return diff;
|
||||
}
|
||||
|
||||
// The paths are the same, base now on weight
|
||||
return o2.weight - o1.weight;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((o1.value != null) && (o2.value == null))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (o1.value == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Different lengths?
|
||||
int diff = o2.value.length() - o1.value.length();
|
||||
if (diff != 0)
|
||||
{
|
||||
return diff;
|
||||
}
|
||||
|
||||
// Different names?
|
||||
diff = o2.value.compareTo(o1.value);
|
||||
if (diff != 0)
|
||||
{
|
||||
return diff;
|
||||
}
|
||||
|
||||
// The paths are the same, base now on weight
|
||||
return o2.weight - o1.weight;
|
||||
};
|
||||
|
||||
private URI warURI;
|
||||
private Map<String, Attribute> attributes = new HashMap<>();
|
||||
private List<PathAttribute> paths = new ArrayList<>();
|
||||
private List<URIAttribute> uris = new ArrayList<>();
|
||||
private final List<PathAttribute> paths = new ArrayList<>();
|
||||
private final List<URIAttribute> uris = new ArrayList<>();
|
||||
|
||||
public AttributeNormalizer(Resource baseResource)
|
||||
{
|
||||
if (baseResource == null)
|
||||
throw new IllegalArgumentException("No base resource!");
|
||||
|
||||
warURI = toCanonicalURI(baseResource.getURI());
|
||||
if (!warURI.isAbsolute())
|
||||
throw new IllegalArgumentException("WAR URI is not absolute: " + warURI);
|
||||
|
||||
addSystemProperty("jetty.base", 9);
|
||||
addSystemProperty("jetty.home", 8);
|
||||
addSystemProperty("user.home", 7);
|
||||
addSystemProperty("user.dir", 6);
|
||||
|
||||
if (warURI.getScheme().equalsIgnoreCase("file"))
|
||||
paths.add(new PathAttribute("WAR.path", toCanonicalPath(new File(warURI).toString()), 10));
|
||||
uris.add(new URIAttribute("WAR.uri", warURI, 9)); // preferred encoding
|
||||
uris.add(new URIAttribute("WAR", warURI, 8)); // legacy encoding
|
||||
Set<Path> rootPaths = new HashSet<>();
|
||||
for (Resource r : baseResource)
|
||||
{
|
||||
if (r instanceof MountedPathResource mpr && rootPaths.contains(mpr.getContainerPath()))
|
||||
return;
|
||||
|
||||
Collections.sort(paths, attrComparator);
|
||||
Collections.sort(uris, attrComparator);
|
||||
URI warURI = toCanonicalURI(r.getURI());
|
||||
if (!warURI.isAbsolute())
|
||||
throw new IllegalArgumentException("WAR URI is not absolute: " + warURI);
|
||||
|
||||
Stream.concat(paths.stream(), uris.stream()).forEach(a -> attributes.put(a.key, a));
|
||||
Path path = r.getPath();
|
||||
if (path != null)
|
||||
{
|
||||
rootPaths.add(path);
|
||||
paths.add(new PathAttribute("WAR.path", toCanonicalPath(path), 10));
|
||||
}
|
||||
uris.add(new URIAttribute("WAR.uri", warURI, 9)); // preferred encoding
|
||||
uris.add(new URIAttribute("WAR", warURI, 8)); // legacy encoding
|
||||
}
|
||||
|
||||
paths.sort(attrComparator);
|
||||
uris.sort(attrComparator);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
for (Attribute attr : attributes.values())
|
||||
{
|
||||
LOG.debug(attr.toString());
|
||||
}
|
||||
}
|
||||
Stream.concat(paths.stream(), uris.stream()).map(Object::toString).forEach(LOG::debug);
|
||||
}
|
||||
|
||||
private void addSystemProperty(String key, int weight)
|
||||
|
@ -352,7 +347,7 @@ public class AttributeNormalizer
|
|||
return String.format("${%s}", a.key);
|
||||
|
||||
String s = uPath.substring(aPath.length());
|
||||
if (s.length() > 0 && s.charAt(0) != '/')
|
||||
if (s.charAt(0) != '/')
|
||||
continue;
|
||||
|
||||
return String.format("${%s}%s", a.key, s);
|
||||
|
@ -375,95 +370,79 @@ public class AttributeNormalizer
|
|||
}
|
||||
|
||||
if (path.startsWith(a.path))
|
||||
return String.format("${%s}%c%s", a.key, File.separatorChar, a.path.relativize(path).toString());
|
||||
return String.format("${%s}%c%s", a.key, File.separatorChar, a.path.relativize(path));
|
||||
}
|
||||
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
public String expand(String str)
|
||||
{
|
||||
return expand(str, new Stack<String>());
|
||||
}
|
||||
|
||||
public String expand(String str, Stack<String> seenStack)
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
if (str.indexOf("${") < 0)
|
||||
if (!str.contains("${"))
|
||||
{
|
||||
// Contains no potential expressions.
|
||||
return str;
|
||||
}
|
||||
|
||||
Matcher mat = __propertyPattern.matcher(str);
|
||||
StringBuilder expanded = new StringBuilder();
|
||||
int offset = 0;
|
||||
String property;
|
||||
String value;
|
||||
|
||||
while (mat.find(offset))
|
||||
if (mat.find(0))
|
||||
{
|
||||
property = mat.group(1);
|
||||
|
||||
// Loop detection
|
||||
if (seenStack.contains(property))
|
||||
{
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Property expansion loop detected: ");
|
||||
int idx = seenStack.lastIndexOf(property);
|
||||
for (int i = idx; i < seenStack.size(); i++)
|
||||
{
|
||||
err.append(seenStack.get(i));
|
||||
err.append(" -> ");
|
||||
}
|
||||
err.append(property);
|
||||
throw new RuntimeException(err.toString());
|
||||
}
|
||||
|
||||
seenStack.push(property);
|
||||
|
||||
// find property name
|
||||
expanded.append(str.subSequence(offset, mat.start()));
|
||||
// get property value
|
||||
value = getString(property);
|
||||
if (value == null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Unable to expand: {}", property);
|
||||
expanded.append(mat.group());
|
||||
}
|
||||
else
|
||||
{
|
||||
// recursively expand
|
||||
value = expand(value, seenStack);
|
||||
expanded.append(value);
|
||||
}
|
||||
// update offset
|
||||
offset = mat.end();
|
||||
String prefix = str.substring(0, mat.start());
|
||||
String property = mat.group(1);
|
||||
String suffix = str.substring(mat.end());
|
||||
str = expand(prefix, property, suffix);
|
||||
}
|
||||
|
||||
// leftover
|
||||
expanded.append(str.substring(offset));
|
||||
|
||||
return StringUtil.replace(expanded.toString(), "$$", "$");
|
||||
return StringUtil.replace(str, "$$", "$");
|
||||
}
|
||||
|
||||
private String getString(String property)
|
||||
private String expand(String prefix, String property, String suffix)
|
||||
{
|
||||
if (property == null)
|
||||
{
|
||||
return null;
|
||||
|
||||
for (URIAttribute attr : uris)
|
||||
{
|
||||
if (property.equals(attr.key))
|
||||
{
|
||||
try
|
||||
{
|
||||
String uri = prefix + attr.value + suffix;
|
||||
Resource resource = ResourceFactory.root().newResource(uri);
|
||||
if (resource.exists())
|
||||
return uri;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.trace("ignored", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Attribute a = attributes.get(property);
|
||||
if (a != null)
|
||||
return a.value;
|
||||
for (PathAttribute attr : paths)
|
||||
{
|
||||
if (property.equals(attr.key))
|
||||
{
|
||||
String path = prefix + attr.value + suffix;
|
||||
if (Files.exists(Path.of(path)))
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
// Use system properties next
|
||||
return System.getProperty(property);
|
||||
String system = System.getProperty(property);
|
||||
if (system != null)
|
||||
return prefix + system + suffix;
|
||||
|
||||
String unexpanded = prefix + "${" + property + "}" + suffix;
|
||||
LOG.warn("Cannot expand: {}", unexpanded);
|
||||
return unexpanded;
|
||||
}
|
||||
}
|
|
@ -13,11 +13,8 @@
|
|||
|
||||
package org.eclipse.jetty.util.resource;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
|
@ -30,32 +27,34 @@ import java.util.stream.Collectors;
|
|||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
||||
/**
|
||||
* A collection of Resources.
|
||||
* Allows webapps to have multiple sources.
|
||||
* The first resource in the collection is the main resource.
|
||||
* If a resource is not found in the main resource, it looks it up in
|
||||
* the order the provided in the constructors
|
||||
* Multiple resource directories presented as a single Resource.
|
||||
*/
|
||||
public class ResourceCollection extends Resource
|
||||
public class CombinedResource extends Resource
|
||||
{
|
||||
private final List<Resource> _resources;
|
||||
|
||||
/**
|
||||
* Instantiates a new resource collection.
|
||||
*
|
||||
* @param resources the resources to be added to collection
|
||||
* <p>Make a Resource containing a combination of other resources</p>
|
||||
* @param resources multiple resources to combine as a single resource. Typically, they are directories.
|
||||
* @return A Resource of multiple resources or a single resource if only 1 is passed, or null if none are passed
|
||||
* @see CombinedResource
|
||||
*/
|
||||
ResourceCollection(List<Resource> resources)
|
||||
static Resource combine(List<Resource> resources)
|
||||
{
|
||||
List<Resource> res = new ArrayList<>();
|
||||
gatherUniqueFlatResourceList(res, resources);
|
||||
_resources = Collections.unmodifiableList(res);
|
||||
resources = CombinedResource.gatherUniqueFlatResourceList(resources);
|
||||
|
||||
if (resources == null || resources.isEmpty())
|
||||
return null;
|
||||
if (resources.size() == 1)
|
||||
return resources.get(0);
|
||||
|
||||
return new CombinedResource(resources);
|
||||
}
|
||||
|
||||
private static void gatherUniqueFlatResourceList(List<Resource> unique, List<Resource> resources)
|
||||
static List<Resource> gatherUniqueFlatResourceList(List<Resource> resources)
|
||||
{
|
||||
if (resources == null || resources.isEmpty())
|
||||
throw new IllegalArgumentException("Empty Resource collection");
|
||||
return null;
|
||||
|
||||
List<Resource> unique = new ArrayList<>(resources.size());
|
||||
|
||||
for (Resource r : resources)
|
||||
{
|
||||
|
@ -64,9 +63,9 @@ public class ResourceCollection extends Resource
|
|||
throw new IllegalArgumentException("Null Resource entry encountered");
|
||||
}
|
||||
|
||||
if (r instanceof ResourceCollection resourceCollection)
|
||||
if (r instanceof CombinedResource resourceCollection)
|
||||
{
|
||||
gatherUniqueFlatResourceList(unique, resourceCollection.getResources());
|
||||
unique.addAll(gatherUniqueFlatResourceList(resourceCollection.getResources()));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -89,6 +88,19 @@ public class ResourceCollection extends Resource
|
|||
unique.add(r);
|
||||
}
|
||||
}
|
||||
return unique;
|
||||
}
|
||||
|
||||
private final List<Resource> _resources;
|
||||
|
||||
/**
|
||||
* Instantiates a new resource collection.
|
||||
*
|
||||
* @param resources the resources to be added to collection
|
||||
*/
|
||||
CombinedResource(List<Resource> resources)
|
||||
{
|
||||
_resources = Collections.unmodifiableList(resources);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,127 +139,67 @@ public class ResourceCollection extends Resource
|
|||
ArrayList<Resource> resources = null;
|
||||
|
||||
// Attempt a simple (single) Resource lookup that exists
|
||||
Resource addedResource = null;
|
||||
Resource resolved = null;
|
||||
for (Resource res : _resources)
|
||||
{
|
||||
addedResource = res.resolve(subUriPath);
|
||||
if (Resources.missing(addedResource))
|
||||
resolved = res.resolve(subUriPath);
|
||||
if (Resources.missing(resolved))
|
||||
continue; // skip, doesn't exist
|
||||
if (!addedResource.isDirectory())
|
||||
return addedResource; // Return simple (non-directory) Resource
|
||||
if (!resolved.isDirectory())
|
||||
return resolved; // Return simple (non-directory) Resource
|
||||
if (resources == null)
|
||||
resources = new ArrayList<>();
|
||||
resources.add(addedResource);
|
||||
resources.add(resolved);
|
||||
}
|
||||
|
||||
if (resources == null)
|
||||
return addedResource; // This will not exist
|
||||
return resolved; // This will not exist
|
||||
|
||||
if (resources.size() == 1)
|
||||
return resources.get(0);
|
||||
|
||||
return new ResourceCollection(resources);
|
||||
return new CombinedResource(resources);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists()
|
||||
{
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
if (r.exists())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return _resources.stream().anyMatch(Resource::exists);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getPath()
|
||||
{
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
Path p = r.getPath();
|
||||
if (p != null)
|
||||
return p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream newInputStream() throws IOException
|
||||
{
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
if (!r.exists())
|
||||
{
|
||||
// Skip, cannot open anyway
|
||||
continue;
|
||||
}
|
||||
InputStream is = r.newInputStream();
|
||||
if (is != null)
|
||||
{
|
||||
return is;
|
||||
}
|
||||
}
|
||||
|
||||
throw new FileNotFoundException("Resource does not exist");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadableByteChannel newReadableByteChannel() throws IOException
|
||||
{
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
ReadableByteChannel channel = r.newReadableByteChannel();
|
||||
if (channel != null)
|
||||
{
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
String name = r.getName();
|
||||
if (name != null)
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName()
|
||||
{
|
||||
String filename = null;
|
||||
// return a non-null filename only if all resources agree on the same name.
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
String filename = r.getFileName();
|
||||
if (filename != null)
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
String fn = r.getFileName();
|
||||
if (fn == null)
|
||||
return null;
|
||||
if (filename == null)
|
||||
filename = fn;
|
||||
else if (!filename.equals(fn))
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
return filename;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI()
|
||||
{
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
URI uri = r.getURI();
|
||||
if (uri != null)
|
||||
{
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -326,7 +278,7 @@ public class ResourceCollection extends Resource
|
|||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
ResourceCollection other = (ResourceCollection)o;
|
||||
CombinedResource other = (CombinedResource)o;
|
||||
return Objects.equals(_resources, other._resources);
|
||||
}
|
||||
|
||||
|
@ -343,7 +295,7 @@ public class ResourceCollection extends Resource
|
|||
public String toString()
|
||||
{
|
||||
return _resources.stream()
|
||||
.map(Resource::getName)
|
||||
.map(Resource::toString)
|
||||
.collect(Collectors.joining(", ", "[", "]"));
|
||||
}
|
||||
|
|
@ -113,11 +113,8 @@ public abstract class Resource implements Iterable<Resource>
|
|||
public abstract boolean isContainedIn(Resource r);
|
||||
|
||||
/**
|
||||
* Return an Iterator of all Resource's referenced in this Resource.
|
||||
*
|
||||
* <p>
|
||||
* This is meaningful if you have a Composite Resource, otherwise it will be a single entry Iterator.
|
||||
* </p>
|
||||
* <p>Return an Iterator of all Resource's referenced in this Resource.</p>
|
||||
* <p>This is meaningful if you have a Composite Resource, otherwise it will be a single entry Iterator of this resource.</p>
|
||||
*
|
||||
* @return the iterator of Resources.
|
||||
*/
|
||||
|
@ -198,22 +195,28 @@ public abstract class Resource implements Iterable<Resource>
|
|||
/**
|
||||
* Creates a new input stream to the resource.
|
||||
*
|
||||
* @return an input stream to the resource
|
||||
* @throws IOException if unable to open the input stream
|
||||
* @return an input stream to the resource or null if one is not available.
|
||||
* @throws IOException if there is a problem opening the input stream
|
||||
*/
|
||||
public InputStream newInputStream() throws IOException
|
||||
{
|
||||
return Files.newInputStream(getPath(), StandardOpenOption.READ);
|
||||
Path path = getPath();
|
||||
if (path == null)
|
||||
return null;
|
||||
return Files.newInputStream(path, StandardOpenOption.READ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Readable ByteChannel for the resource.
|
||||
*
|
||||
* @return an readable bytechannel to the resource or null if one is not available.
|
||||
* @return a readable {@link java.nio.channels.ByteChannel} to the resource or null if one is not available.
|
||||
* @throws IOException if unable to open the readable bytechannel for the resource.
|
||||
*/
|
||||
public ReadableByteChannel newReadableByteChannel() throws IOException
|
||||
{
|
||||
Path path = getPath();
|
||||
if (path == null)
|
||||
return null;
|
||||
return Files.newByteChannel(getPath(), StandardOpenOption.READ);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,27 +35,23 @@ public interface ResourceFactory
|
|||
/**
|
||||
* <p>Make a Resource containing a collection of other resources</p>
|
||||
* @param resources multiple resources to combine as a single resource. Typically, they are directories.
|
||||
* @return A Resource of multiple resources.
|
||||
* @see ResourceCollection
|
||||
* @return A Resource of multiple resources or a single resource if only 1 is passed, or null if none are passed
|
||||
* @see CombinedResource
|
||||
*/
|
||||
static ResourceCollection combine(List<Resource> resources)
|
||||
static Resource combine(List<Resource> resources)
|
||||
{
|
||||
if (resources == null || resources.isEmpty())
|
||||
throw new IllegalArgumentException("No resources");
|
||||
return new ResourceCollection(resources);
|
||||
return CombinedResource.combine(resources);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Make a Resource containing a collection of other resources</p>
|
||||
* @param resources multiple resources to combine as a single resource. Typically, they are directories.
|
||||
* @return A Resource of multiple resources.
|
||||
* @see ResourceCollection
|
||||
* @see CombinedResource
|
||||
*/
|
||||
static ResourceCollection combine(Resource... resources)
|
||||
static Resource combine(Resource... resources)
|
||||
{
|
||||
if (resources == null || resources.length == 0)
|
||||
throw new IllegalArgumentException("No resources");
|
||||
return new ResourceCollection(List.of(resources));
|
||||
return CombinedResource.combine(List.of(resources));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -203,12 +199,12 @@ public interface ResourceFactory
|
|||
}
|
||||
|
||||
/**
|
||||
* Construct a ResourceCollection from a list of URIs
|
||||
* Construct a possible {@link CombinedResource} from a list of URIs
|
||||
*
|
||||
* @param uris the URIs
|
||||
* @return the Resource for the provided path
|
||||
*/
|
||||
default ResourceCollection newResource(List<URI> uris)
|
||||
default Resource newResource(List<URI> uris)
|
||||
{
|
||||
if ((uris == null) || (uris.isEmpty()))
|
||||
throw new IllegalArgumentException("List of URIs is invalid");
|
||||
|
|
|
@ -11,8 +11,9 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.ee9.quickstart;
|
||||
package org.eclipse.jetty.util.resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
@ -26,11 +27,9 @@ import java.util.stream.Stream;
|
|||
import org.eclipse.jetty.toolchain.test.FS;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.resource.FileSystemPool;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
|
@ -43,7 +42,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||
|
||||
public class AttributeNormalizerTest
|
||||
{
|
||||
public static Stream<Arguments> scenarios()
|
||||
public static Stream<Arguments> scenarios() throws IOException
|
||||
{
|
||||
final List<Arguments> data = new ArrayList<>();
|
||||
final String arch = String.format("%s/%s", System.getProperty("os.name"), System.getProperty("os.arch"));
|
||||
|
@ -55,18 +54,22 @@ public class AttributeNormalizerTest
|
|||
|
||||
// ------
|
||||
title = "Typical Setup";
|
||||
jettyHome = asTargetPath(title, "jetty-distro");
|
||||
jettyBase = asTargetPath(title, "jetty-distro/demo.base");
|
||||
war = asTargetPath(title, "jetty-distro/demo.base/webapps/FOO");
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, war)));
|
||||
jettyHome = asTargetPath(title, "jetty-typical");
|
||||
jettyBase = asTargetPath(title, "jetty-typical/demo.base");
|
||||
war = asTargetPath(title, "jetty-typical/demo.base/webapps/FOO");
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, resourceFactory.newResource(war))));
|
||||
|
||||
// ------
|
||||
title = "Old Setup";
|
||||
jettyHome = asTargetPath(title, "jetty-distro");
|
||||
jettyBase = asTargetPath(title, "jetty-distro");
|
||||
war = asTargetPath(title, "jetty-distro/webapps/FOO");
|
||||
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, war)));
|
||||
jettyHome = asTargetPath(title, "jetty-old");
|
||||
jettyBase = asTargetPath(title, "jetty-old");
|
||||
war = asTargetPath(title, "jetty-old/webapps/FOO");
|
||||
if (!Files.exists(war.resolve("index.html")))
|
||||
{
|
||||
Files.createFile(war.resolve("index.html"));
|
||||
Files.createFile(war.resolve("favicon.ico"));
|
||||
}
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, resourceFactory.newResource(war))));
|
||||
|
||||
// ------
|
||||
// This puts the jetty.home inside the jetty.base
|
||||
|
@ -74,8 +77,7 @@ public class AttributeNormalizerTest
|
|||
jettyHome = asTargetPath(title, "app/dist");
|
||||
jettyBase = asTargetPath(title, "app");
|
||||
war = asTargetPath(title, "app/webapps/FOO");
|
||||
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, war)));
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, resourceFactory.newResource(war))));
|
||||
|
||||
// ------
|
||||
// This tests a path scenario often seen on various automatic deployments tooling
|
||||
|
@ -84,8 +86,16 @@ public class AttributeNormalizerTest
|
|||
jettyHome = asTargetPath(title, "app%2Fnasty/dist");
|
||||
jettyBase = asTargetPath(title, "app%2Fnasty/base");
|
||||
war = asTargetPath(title, "app%2Fnasty/base/webapps/FOO");
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, resourceFactory.newResource(war))));
|
||||
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, war)));
|
||||
// ------
|
||||
title = "ResourceCollection Setup";
|
||||
jettyHome = asTargetPath(title, "jetty-collection");
|
||||
jettyBase = asTargetPath(title, "jetty-collection/demo.base");
|
||||
Path warA = asTargetPath(title, "jetty-collection/demo.base/webapps/WarA");
|
||||
Path warB = asTargetPath(title, "jetty-collection/demo.base/webapps/WarB");
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase,
|
||||
ResourceFactory.combine(resourceFactory.newResource(warA), resourceFactory.newResource(warB)))));
|
||||
|
||||
return data.stream();
|
||||
}
|
||||
|
@ -166,7 +176,9 @@ public class AttributeNormalizerTest
|
|||
public void testNormalizeWarAsString(final Scenario scenario)
|
||||
{
|
||||
// Normalize WAR as String path
|
||||
assertNormalize(scenario, scenario.war.toString(), scenario.war.toString());
|
||||
Path path = scenario.war.getPath();
|
||||
Assumptions.assumeTrue(path != null);
|
||||
assertNormalize(scenario, path.toString(), path.toString());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
|
@ -259,12 +271,22 @@ public class AttributeNormalizerTest
|
|||
assertExpandPath(scenario, "${jetty.home}", scenario.jettyHome.toString());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testExpandWebInfAsURI(final Scenario scenario)
|
||||
{
|
||||
// Expand
|
||||
assertExpandURI(scenario, "${WAR.uri}/WEB-INF/web.xml", scenario.webXml.toUri());
|
||||
assertExpandURI(scenario, "${WAR.uri}/WEB-INF/test.tld", scenario.testTld.toUri());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testNormalizeWarAsURI(final Scenario scenario)
|
||||
{
|
||||
// Normalize WAR as URI
|
||||
URI testWarURI = scenario.war.toUri();
|
||||
URI testWarURI = scenario.war.getURI();
|
||||
Assumptions.assumeTrue(testWarURI != null);
|
||||
assertNormalize(scenario, testWarURI, "${WAR.uri}");
|
||||
}
|
||||
|
||||
|
@ -273,7 +295,7 @@ public class AttributeNormalizerTest
|
|||
public void testNormalizeWarDeepAsPath(final Scenario scenario)
|
||||
{
|
||||
// Normalize WAR deep path as File
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref");
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref").getPath();
|
||||
assertNormalize(scenario, testWarDeep, "${WAR.path}" + FS.separators("/deep/ref"));
|
||||
}
|
||||
|
||||
|
@ -282,7 +304,7 @@ public class AttributeNormalizerTest
|
|||
public void testNormalizeWarDeepAsString(final Scenario scenario)
|
||||
{
|
||||
// Normalize WAR deep path as String
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref");
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref").getPath();
|
||||
assertNormalize(scenario, testWarDeep.toString(), testWarDeep.toString());
|
||||
}
|
||||
|
||||
|
@ -291,30 +313,22 @@ public class AttributeNormalizerTest
|
|||
public void testNormalizeWarDeepAsURI(final Scenario scenario)
|
||||
{
|
||||
// Normalize WAR deep path as URI
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref");
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref").getPath();
|
||||
assertNormalize(scenario, testWarDeep.toUri(), "${WAR.uri}/deep/ref");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testExpandWarDeep(final Scenario scenario)
|
||||
{
|
||||
// Expand WAR deep path
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref");
|
||||
URI uri = URI.create("jar:" + testWarDeep.toUri().toASCIIString() + "!/other/file");
|
||||
assertExpandURI(scenario, "jar:${WAR.uri}/deep/ref!/other/file", uri);
|
||||
}
|
||||
|
||||
public static class Scenario
|
||||
{
|
||||
private final Path jettyHome;
|
||||
private final Path jettyBase;
|
||||
private final Path war;
|
||||
private final Resource war;
|
||||
private Path webXml;
|
||||
private Path testTld;
|
||||
private final String arch;
|
||||
private final String title;
|
||||
private final AttributeNormalizer normalizer;
|
||||
|
||||
public Scenario(String arch, String title, Path jettyHome, Path jettyBase, Path war)
|
||||
public Scenario(String arch, String title, Path jettyHome, Path jettyBase, Resource war)
|
||||
{
|
||||
this.arch = arch;
|
||||
this.title = title;
|
||||
|
@ -326,15 +340,48 @@ public class AttributeNormalizerTest
|
|||
|
||||
assertTrue(Files.exists(this.jettyHome));
|
||||
assertTrue(Files.exists(this.jettyBase));
|
||||
assertTrue(Files.exists(this.war));
|
||||
assertTrue(war.exists());
|
||||
|
||||
// Set some System Properties that AttributeNormalizer expects
|
||||
System.setProperty("jetty.home", jettyHome.toString());
|
||||
System.setProperty("jetty.base", jettyBase.toString());
|
||||
|
||||
for (Resource w : war)
|
||||
{
|
||||
try
|
||||
{
|
||||
Path webinf = w.getPath().resolve("WEB-INF");
|
||||
if (!Files.exists(webinf))
|
||||
Files.createDirectory(webinf);
|
||||
Path deep = w.getPath().resolve("deep");
|
||||
if (!Files.exists(deep))
|
||||
Files.createDirectory(deep);
|
||||
Path ref = deep.resolve("ref");
|
||||
if (!Files.exists(ref))
|
||||
Files.createFile(ref);
|
||||
|
||||
if (w.getFileName().equals("FOO") || w.getFileName().equals("WarA"))
|
||||
{
|
||||
webXml = webinf.resolve("web.xml");
|
||||
if (!Files.exists(webXml))
|
||||
Files.createFile(webXml);
|
||||
}
|
||||
|
||||
if (w.getFileName().equals("FOO") || w.getFileName().equals("WarB"))
|
||||
{
|
||||
testTld = webinf.resolve("test.tld");
|
||||
if (!Files.exists(testTld))
|
||||
Files.createFile(testTld);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Setup normalizer
|
||||
Resource webresource = resourceFactory.newResource(war);
|
||||
this.normalizer = new AttributeNormalizer(webresource);
|
||||
this.normalizer = new AttributeNormalizer(war);
|
||||
}
|
||||
|
||||
@Override
|
|
@ -11,7 +11,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.ee9.quickstart;
|
||||
package org.eclipse.jetty.util.resource;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
|
@ -52,7 +52,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
|
|||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(WorkDirExtension.class)
|
||||
public class ResourceCollectionTest
|
||||
public class CombinedResourceTest
|
||||
{
|
||||
private final ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable();
|
||||
public WorkDir workDir;
|
||||
|
@ -78,7 +78,7 @@ public class ResourceCollectionTest
|
|||
Path two = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/two");
|
||||
Path three = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/three");
|
||||
|
||||
ResourceCollection rc = ResourceFactory.combine(
|
||||
Resource rc = ResourceFactory.combine(
|
||||
resourceFactory.newResource(one),
|
||||
resourceFactory.newResource(two),
|
||||
resourceFactory.newResource(three)
|
||||
|
@ -127,7 +127,7 @@ public class ResourceCollectionTest
|
|||
Path two = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/two");
|
||||
Path three = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/three");
|
||||
|
||||
ResourceCollection rc = ResourceFactory.combine(
|
||||
Resource rc = ResourceFactory.combine(
|
||||
resourceFactory.newResource(one),
|
||||
resourceFactory.newResource(two),
|
||||
resourceFactory.newResource(three)
|
||||
|
@ -135,8 +135,8 @@ public class ResourceCollectionTest
|
|||
|
||||
// This should return a ResourceCollection with 3 `/dir/` sub-directories.
|
||||
Resource r = rc.resolve("dir");
|
||||
assertTrue(r instanceof ResourceCollection);
|
||||
rc = (ResourceCollection)r;
|
||||
assertTrue(r instanceof CombinedResource);
|
||||
rc = (CombinedResource)r;
|
||||
assertEquals(getContent(rc, "1.txt"), "1 - one (in dir)");
|
||||
assertEquals(getContent(rc, "2.txt"), "2 - two (in dir)");
|
||||
assertEquals(getContent(rc, "3.txt"), "3 - three (in dir)");
|
||||
|
@ -149,7 +149,7 @@ public class ResourceCollectionTest
|
|||
Path two = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/two");
|
||||
Path three = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/three");
|
||||
|
||||
ResourceCollection rc = ResourceFactory.combine(
|
||||
Resource rc = ResourceFactory.combine(
|
||||
resourceFactory.newResource(one),
|
||||
resourceFactory.newResource(two),
|
||||
resourceFactory.newResource(three)
|
||||
|
@ -175,7 +175,7 @@ public class ResourceCollectionTest
|
|||
Path three = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/three");
|
||||
Path twoDir = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/two/dir");
|
||||
|
||||
ResourceCollection rc1 = ResourceFactory.combine(
|
||||
Resource rc1 = ResourceFactory.combine(
|
||||
List.of(
|
||||
resourceFactory.newResource(one),
|
||||
resourceFactory.newResource(two),
|
||||
|
@ -183,7 +183,7 @@ public class ResourceCollectionTest
|
|||
)
|
||||
);
|
||||
|
||||
ResourceCollection rc2 = ResourceFactory.combine(
|
||||
Resource rc2 = ResourceFactory.combine(
|
||||
List.of(
|
||||
// the original ResourceCollection
|
||||
rc1,
|
||||
|
@ -202,9 +202,11 @@ public class ResourceCollectionTest
|
|||
};
|
||||
|
||||
List<URI> actual = new ArrayList<>();
|
||||
for (Resource res: rc2.getResources())
|
||||
assertThat(rc2, instanceOf(CombinedResource.class));
|
||||
if (rc2 instanceof CombinedResource combinedResource)
|
||||
{
|
||||
actual.add(res.getURI());
|
||||
for (Resource res : combinedResource.getResources())
|
||||
actual.add(res.getURI());
|
||||
}
|
||||
assertThat(actual, contains(expected));
|
||||
}
|
||||
|
@ -307,7 +309,7 @@ public class ResourceCollectionTest
|
|||
// Since this is user space, we cannot know ahead of time what
|
||||
// this list contains, so we mount because we assume there
|
||||
// will be necessary things to mount
|
||||
ResourceCollection rc = resourceFactory.newResource(uris);
|
||||
Resource rc = resourceFactory.newResource(uris);
|
||||
assertThat(getContent(rc, "test.txt"), is("Test"));
|
||||
}
|
||||
|
||||
|
@ -333,7 +335,7 @@ public class ResourceCollectionTest
|
|||
// Since this is user space, we cannot know ahead of time what
|
||||
// this list contains, so we mount because we assume there
|
||||
// will be necessary things to mount
|
||||
ResourceCollection rc = resourceFactory.newResource(uris);
|
||||
Resource rc = resourceFactory.newResource(uris);
|
||||
assertThat(getContent(rc, "test.txt"), is("Test inside lib-foo.jar"));
|
||||
assertThat(getContent(rc, "testZed.txt"), is("TestZed inside lib-zed.jar"));
|
||||
}
|
|
@ -17,8 +17,8 @@ import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration;
|
|||
import org.eclipse.jetty.ee10.webapp.Configuration;
|
||||
import org.eclipse.jetty.ee10.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.resource.CombinedResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -47,9 +47,9 @@ public class MavenQuickStartConfiguration extends QuickStartConfiguration
|
|||
//Iterate over all of the resource bases and ignore any that were original bases, just
|
||||
//deleting the overlays
|
||||
Resource res = context.getBaseResource();
|
||||
if (res instanceof ResourceCollection)
|
||||
if (res instanceof CombinedResource)
|
||||
{
|
||||
for (Resource r : ((ResourceCollection)res).getResources())
|
||||
for (Resource r : ((CombinedResource)res).getResources())
|
||||
{
|
||||
if (originalBaseStr.contains(r.toString()))
|
||||
continue;
|
||||
|
|
|
@ -39,8 +39,8 @@ import org.eclipse.jetty.ee10.webapp.WebAppContext;
|
|||
import org.eclipse.jetty.util.FileID;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.resource.CombinedResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.eclipse.jetty.util.resource.Resources;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -218,7 +218,7 @@ public class MavenWebAppContext extends WebAppContext
|
|||
* configuration
|
||||
*
|
||||
* @param resourceBases Array of resources strings to set as a
|
||||
* {@link ResourceCollection}.
|
||||
* {@link CombinedResource}.
|
||||
*/
|
||||
public void setResourceBases(String[] resourceBases)
|
||||
{
|
||||
|
|
|
@ -30,8 +30,8 @@ import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.resource.CombinedResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.eclipse.jetty.xml.XmlConfiguration;
|
||||
|
||||
/**
|
||||
|
@ -106,8 +106,8 @@ public class WebAppPropertyConverter
|
|||
|
||||
//send over the calculated resource bases that includes unpacked overlays
|
||||
Resource baseResource = webApp.getBaseResource();
|
||||
if (baseResource instanceof ResourceCollection)
|
||||
props.put(BASE_DIRS, toCSV(((ResourceCollection)webApp.getBaseResource()).getResources()));
|
||||
if (baseResource instanceof CombinedResource)
|
||||
props.put(BASE_DIRS, toCSV(((CombinedResource)webApp.getBaseResource()).getResources()));
|
||||
else if (baseResource instanceof Resource)
|
||||
props.put(BASE_DIRS, webApp.getBaseResource().toString());
|
||||
|
||||
|
|
|
@ -16,17 +16,17 @@ package org.eclipse.jetty.ee10.maven.plugin;
|
|||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.eclipse.jetty.ee10.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.resource.CombinedResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
|
@ -152,10 +152,13 @@ public class TestWebAppPropertyConverter
|
|||
assertEquals(true, webApp.isPersistTempDirectory());
|
||||
assertEquals(war.getAbsolutePath(), webApp.getWar());
|
||||
assertEquals(webXml.getAbsolutePath(), webApp.getDescriptor());
|
||||
assertThat(webApp.getBaseResource(), instanceOf(ResourceCollection.class));
|
||||
assertThat(webApp.getBaseResource(), instanceOf(CombinedResource.class));
|
||||
|
||||
ResourceCollection resourceCollection = (ResourceCollection)webApp.getBaseResource();
|
||||
List<URI> actual = resourceCollection.getResources().stream().filter(Objects::nonNull).map(Resource::getURI).toList();
|
||||
Resource combinedResource = webApp.getBaseResource();
|
||||
List<URI> actual = new ArrayList<>();
|
||||
for (Resource r : combinedResource)
|
||||
if (r != null)
|
||||
actual.add(r.getURI());
|
||||
URI[] expected = new URI[]{base1.toURI(), base2.toURI()};
|
||||
assertThat(actual, containsInAnyOrder(expected));
|
||||
}
|
||||
|
|
|
@ -1,469 +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.ee10.quickstart;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Normalize Attribute to String.
|
||||
* <p>
|
||||
* Replaces and expands:
|
||||
* <ul>
|
||||
* <li>${WAR}</li>
|
||||
* <li>${WAR.path}</li>
|
||||
* <li>${WAR.uri}</li>
|
||||
* <li>${jetty.base}</li>
|
||||
* <li>${jetty.base.uri}</li>
|
||||
* <li>${jetty.home}</li>
|
||||
* <li>${jetty.home.uri}</li>
|
||||
* <li>${user.home}</li>
|
||||
* <li>${user.home.uri}</li>
|
||||
* <li>${user.dir}</li>
|
||||
* <li>${user.dir.uri}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class AttributeNormalizer
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AttributeNormalizer.class);
|
||||
private static final Pattern __propertyPattern = Pattern.compile("(?<=[^$]|^)\\$\\{([^}]*)\\}");
|
||||
|
||||
private static class Attribute
|
||||
{
|
||||
final String key;
|
||||
final String value;
|
||||
final int weight;
|
||||
|
||||
public Attribute(String key, String value, int weight)
|
||||
{
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.weight = weight;
|
||||
}
|
||||
}
|
||||
|
||||
public static URI toCanonicalURI(URI uri)
|
||||
{
|
||||
uri = uri.normalize();
|
||||
String path = uri.getPath();
|
||||
if (path != null && path.length() > 1 && path.endsWith("/"))
|
||||
{
|
||||
try
|
||||
{
|
||||
String ascii = uri.toASCIIString();
|
||||
uri = new URI(ascii.substring(0, ascii.length() - 1));
|
||||
}
|
||||
catch (URISyntaxException e)
|
||||
{
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
public static String toCanonicalURI(String uri)
|
||||
{
|
||||
if (uri != null && uri.length() > 1 && uri.endsWith("/"))
|
||||
{
|
||||
return uri.substring(0, uri.length() - 1);
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
public static Path toCanonicalPath(String path)
|
||||
{
|
||||
if (path == null)
|
||||
return null;
|
||||
if (path.length() > 1 && path.endsWith("/"))
|
||||
path = path.substring(0, path.length() - 1);
|
||||
return toCanonicalPath(FileSystems.getDefault().getPath(path));
|
||||
}
|
||||
|
||||
private static Path toCanonicalPath(Path path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (Files.exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
return path.toRealPath();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
return path.toAbsolutePath();
|
||||
}
|
||||
|
||||
private static class PathAttribute extends Attribute
|
||||
{
|
||||
public final Path path;
|
||||
|
||||
public PathAttribute(String key, Path path, int weight)
|
||||
{
|
||||
super(key, path.toString(), weight);
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("PathAttribute[%s=>%s]", key, path);
|
||||
}
|
||||
}
|
||||
|
||||
private static class URIAttribute extends Attribute
|
||||
{
|
||||
public final URI uri;
|
||||
|
||||
public URIAttribute(String key, URI uri, int weight)
|
||||
{
|
||||
super(key, toCanonicalURI(uri.toASCIIString()), weight);
|
||||
this.uri = toCanonicalURI(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("URIAttribute[%s=>%s]", key, uri);
|
||||
}
|
||||
}
|
||||
|
||||
private static Comparator<Attribute> attrComparator = new Comparator<Attribute>()
|
||||
{
|
||||
@Override
|
||||
public int compare(Attribute o1, Attribute o2)
|
||||
{
|
||||
if ((o1.value == null) && (o2.value != null))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((o1.value != null) && (o2.value == null))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((o1.value == null) && (o2.value == null))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Different lengths?
|
||||
int diff = o2.value.length() - o1.value.length();
|
||||
if (diff != 0)
|
||||
{
|
||||
return diff;
|
||||
}
|
||||
|
||||
// Different names?
|
||||
diff = o2.value.compareTo(o1.value);
|
||||
if (diff != 0)
|
||||
{
|
||||
return diff;
|
||||
}
|
||||
|
||||
// The paths are the same, base now on weight
|
||||
return o2.weight - o1.weight;
|
||||
}
|
||||
};
|
||||
|
||||
private URI warURI;
|
||||
private Map<String, Attribute> attributes = new HashMap<>();
|
||||
private List<PathAttribute> paths = new ArrayList<>();
|
||||
private List<URIAttribute> uris = new ArrayList<>();
|
||||
|
||||
public AttributeNormalizer(Resource baseResource)
|
||||
{
|
||||
if (baseResource == null)
|
||||
throw new IllegalArgumentException("No base resource!");
|
||||
|
||||
warURI = toCanonicalURI(baseResource.getURI());
|
||||
if (!warURI.isAbsolute())
|
||||
throw new IllegalArgumentException("WAR URI is not absolute: " + warURI);
|
||||
|
||||
addSystemProperty("jetty.base", 9);
|
||||
addSystemProperty("jetty.home", 8);
|
||||
addSystemProperty("user.home", 7);
|
||||
addSystemProperty("user.dir", 6);
|
||||
|
||||
if (warURI.getScheme().equalsIgnoreCase("file"))
|
||||
paths.add(new PathAttribute("WAR.path", toCanonicalPath(new File(warURI).toString()), 10));
|
||||
uris.add(new URIAttribute("WAR.uri", warURI, 9)); // preferred encoding
|
||||
uris.add(new URIAttribute("WAR", warURI, 8)); // legacy encoding
|
||||
|
||||
Collections.sort(paths, attrComparator);
|
||||
Collections.sort(uris, attrComparator);
|
||||
|
||||
Stream.concat(paths.stream(), uris.stream()).forEach(a -> attributes.put(a.key, a));
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
for (Attribute attr : attributes.values())
|
||||
{
|
||||
LOG.debug(attr.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addSystemProperty(String key, int weight)
|
||||
{
|
||||
String value = System.getProperty(key);
|
||||
if (value != null)
|
||||
{
|
||||
Path path = toCanonicalPath(value);
|
||||
paths.add(new PathAttribute(key, path, weight));
|
||||
uris.add(new URIAttribute(key + ".uri", path.toUri(), weight));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a URI, URL, or File reference by replacing known attributes with ${key} attributes.
|
||||
*
|
||||
* @param o the object to normalize into a string
|
||||
* @return the string representation of the object, with expansion keys.
|
||||
*/
|
||||
public String normalize(Object o)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Find a URI
|
||||
URI uri = null;
|
||||
Path path = null;
|
||||
if (o instanceof URI)
|
||||
uri = toCanonicalURI(((URI)o));
|
||||
else if (o instanceof Resource)
|
||||
uri = toCanonicalURI(((Resource)o).getURI());
|
||||
else if (o instanceof URL)
|
||||
uri = toCanonicalURI(((URL)o).toURI());
|
||||
else if (o instanceof File)
|
||||
path = ((File)o).getAbsoluteFile().getCanonicalFile().toPath();
|
||||
else if (o instanceof Path)
|
||||
path = (Path)o;
|
||||
else
|
||||
{
|
||||
String s = o.toString();
|
||||
try
|
||||
{
|
||||
uri = new URI(s);
|
||||
if (uri.getScheme() == null)
|
||||
{
|
||||
// Unknown scheme? not relevant to normalize
|
||||
return s;
|
||||
}
|
||||
}
|
||||
catch (URISyntaxException e)
|
||||
{
|
||||
// This path occurs for many reasons, but most common is when this
|
||||
// is executed on MS Windows, on a string like "D:\jetty"
|
||||
// and the new URI() fails for
|
||||
// java.net.URISyntaxException: Illegal character in opaque part at index 2: D:\jetty
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
if (uri != null)
|
||||
{
|
||||
if ("jar".equalsIgnoreCase(uri.getScheme()))
|
||||
{
|
||||
String raw = uri.getRawSchemeSpecificPart();
|
||||
int bang = raw.indexOf("!/");
|
||||
String normal = normalize(raw.substring(0, bang));
|
||||
String suffix = raw.substring(bang);
|
||||
return "jar:" + normal + suffix;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (uri.isAbsolute())
|
||||
{
|
||||
return normalizeUri(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (path != null)
|
||||
return normalizePath(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Failed to normalize {}", o, e);
|
||||
}
|
||||
return String.valueOf(o);
|
||||
}
|
||||
|
||||
protected String normalizeUri(URI uri)
|
||||
{
|
||||
for (URIAttribute a : uris)
|
||||
{
|
||||
if (uri.compareTo(a.uri) == 0)
|
||||
return String.format("${%s}", a.key);
|
||||
|
||||
if (!a.uri.getScheme().equalsIgnoreCase(uri.getScheme()))
|
||||
continue;
|
||||
if (a.uri.getHost() == null && uri.getHost() != null)
|
||||
continue;
|
||||
if (a.uri.getHost() != null && !a.uri.getHost().equals(uri.getHost()))
|
||||
continue;
|
||||
|
||||
String aPath = a.uri.getPath();
|
||||
String uPath = uri.getPath();
|
||||
if (aPath.equals(uPath))
|
||||
return a.value;
|
||||
|
||||
if (!uPath.startsWith(aPath))
|
||||
continue;
|
||||
|
||||
if (uPath.length() == aPath.length())
|
||||
return String.format("${%s}", a.key);
|
||||
|
||||
String s = uPath.substring(aPath.length());
|
||||
if (s.length() > 0 && s.charAt(0) != '/')
|
||||
continue;
|
||||
|
||||
return String.format("${%s}%s", a.key, s);
|
||||
}
|
||||
return uri.toASCIIString();
|
||||
}
|
||||
|
||||
protected String normalizePath(Path path)
|
||||
{
|
||||
for (PathAttribute a : paths)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (path.equals(a.path) || Files.isSameFile(path, a.path))
|
||||
return String.format("${%s}", a.key);
|
||||
}
|
||||
catch (IOException ignore)
|
||||
{
|
||||
LOG.trace("IGNORED", ignore);
|
||||
}
|
||||
|
||||
if (path.startsWith(a.path))
|
||||
return String.format("${%s}%c%s", a.key, File.separatorChar, a.path.relativize(path).toString());
|
||||
}
|
||||
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
public String expand(String str)
|
||||
{
|
||||
return expand(str, new Stack<String>());
|
||||
}
|
||||
|
||||
public String expand(String str, Stack<String> seenStack)
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
if (str.indexOf("${") < 0)
|
||||
{
|
||||
// Contains no potential expressions.
|
||||
return str;
|
||||
}
|
||||
|
||||
Matcher mat = __propertyPattern.matcher(str);
|
||||
StringBuilder expanded = new StringBuilder();
|
||||
int offset = 0;
|
||||
String property;
|
||||
String value;
|
||||
|
||||
while (mat.find(offset))
|
||||
{
|
||||
property = mat.group(1);
|
||||
|
||||
// Loop detection
|
||||
if (seenStack.contains(property))
|
||||
{
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Property expansion loop detected: ");
|
||||
int idx = seenStack.lastIndexOf(property);
|
||||
for (int i = idx; i < seenStack.size(); i++)
|
||||
{
|
||||
err.append(seenStack.get(i));
|
||||
err.append(" -> ");
|
||||
}
|
||||
err.append(property);
|
||||
throw new RuntimeException(err.toString());
|
||||
}
|
||||
|
||||
seenStack.push(property);
|
||||
|
||||
// find property name
|
||||
expanded.append(str.subSequence(offset, mat.start()));
|
||||
// get property value
|
||||
value = getString(property);
|
||||
if (value == null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Unable to expand: {}", property);
|
||||
expanded.append(mat.group());
|
||||
}
|
||||
else
|
||||
{
|
||||
// recursively expand
|
||||
value = expand(value, seenStack);
|
||||
expanded.append(value);
|
||||
}
|
||||
// update offset
|
||||
offset = mat.end();
|
||||
}
|
||||
|
||||
// leftover
|
||||
expanded.append(str.substring(offset));
|
||||
|
||||
return StringUtil.replace(expanded.toString(), "$$", "$");
|
||||
}
|
||||
|
||||
private String getString(String property)
|
||||
{
|
||||
if (property == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Attribute a = attributes.get(property);
|
||||
if (a != null)
|
||||
return a.value;
|
||||
|
||||
// Use system properties next
|
||||
return System.getProperty(property);
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ import org.eclipse.jetty.util.IO;
|
|||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.resource.AttributeNormalizer;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||
import org.eclipse.jetty.util.resource.Resources;
|
||||
|
|
|
@ -57,6 +57,7 @@ import org.eclipse.jetty.ee10.webapp.WebInfConfiguration;
|
|||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.resource.AttributeNormalizer;
|
||||
import org.eclipse.jetty.util.security.Constraint;
|
||||
import org.eclipse.jetty.xml.XmlAppendable;
|
||||
import org.slf4j.Logger;
|
||||
|
|
|
@ -1,347 +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.ee10.quickstart;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.FS;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.resource.FileSystemPool;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class AttributeNormalizerTest
|
||||
{
|
||||
public static Stream<Arguments> scenarios()
|
||||
{
|
||||
final List<Arguments> data = new ArrayList<>();
|
||||
final String arch = String.format("%s/%s", System.getProperty("os.name"), System.getProperty("os.arch"));
|
||||
|
||||
String title;
|
||||
Path jettyHome;
|
||||
Path jettyBase;
|
||||
Path war;
|
||||
|
||||
// ------
|
||||
title = "Typical Setup";
|
||||
jettyHome = asTargetPath(title, "jetty-distro");
|
||||
jettyBase = asTargetPath(title, "jetty-distro/demo.base");
|
||||
war = asTargetPath(title, "jetty-distro/demo.base/webapps/FOO");
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, war)));
|
||||
|
||||
// ------
|
||||
title = "Old Setup";
|
||||
jettyHome = asTargetPath(title, "jetty-distro");
|
||||
jettyBase = asTargetPath(title, "jetty-distro");
|
||||
war = asTargetPath(title, "jetty-distro/webapps/FOO");
|
||||
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, war)));
|
||||
|
||||
// ------
|
||||
// This puts the jetty.home inside the jetty.base
|
||||
title = "Overlap Setup";
|
||||
jettyHome = asTargetPath(title, "app/dist");
|
||||
jettyBase = asTargetPath(title, "app");
|
||||
war = asTargetPath(title, "app/webapps/FOO");
|
||||
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, war)));
|
||||
|
||||
// ------
|
||||
// This tests a path scenario often seen on various automatic deployments tooling
|
||||
// such as Kubernetes, CircleCI, TravisCI, and Jenkins.
|
||||
title = "Nasty Path Setup";
|
||||
jettyHome = asTargetPath(title, "app%2Fnasty/dist");
|
||||
jettyBase = asTargetPath(title, "app%2Fnasty/base");
|
||||
war = asTargetPath(title, "app%2Fnasty/base/webapps/FOO");
|
||||
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, war)));
|
||||
|
||||
return data.stream();
|
||||
}
|
||||
|
||||
private static Path asTargetPath(String title, String subpath)
|
||||
{
|
||||
Path rootPath = MavenTestingUtils.getTargetTestingPath(title);
|
||||
FS.ensureDirExists(rootPath);
|
||||
Path path = rootPath.resolve(FS.separators(subpath));
|
||||
FS.ensureDirExists(path);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private static final Map<String, String> originalEnv = new HashMap<>();
|
||||
private static ResourceFactory.Closeable resourceFactory;
|
||||
|
||||
@BeforeAll
|
||||
public static void rememberOriginalEnv()
|
||||
{
|
||||
assertThat(FileSystemPool.INSTANCE.mounts(), empty());
|
||||
resourceFactory = ResourceFactory.closeable();
|
||||
System.getProperties().stringPropertyNames()
|
||||
.forEach((name) -> originalEnv.put(name, System.getProperty(name)));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void afterAll()
|
||||
{
|
||||
IO.close(resourceFactory);
|
||||
assertThat(FileSystemPool.INSTANCE.mounts(), empty());
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void restoreOriginalEnv()
|
||||
{
|
||||
originalEnv.forEach(AttributeNormalizerTest::restoreSystemProperty);
|
||||
}
|
||||
|
||||
private static void restoreSystemProperty(String key, String value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
System.clearProperty(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.setProperty(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertNormalize(final Scenario scenario, Object o, String expected)
|
||||
{
|
||||
String result = scenario.normalizer.normalize(o);
|
||||
assertThat("normalize((" + o.getClass().getSimpleName() + ") " + Objects.toString(o, "<null>") + ")",
|
||||
result, is(expected));
|
||||
}
|
||||
|
||||
private void assertExpandPath(final Scenario scenario, String line, String expected)
|
||||
{
|
||||
String result = scenario.normalizer.expand(line);
|
||||
|
||||
// Treat output as strings
|
||||
assertThat("expand('" + line + "')", result, is(expected));
|
||||
}
|
||||
|
||||
private void assertExpandURI(final Scenario scenario, String line, URI expected)
|
||||
{
|
||||
String result = scenario.normalizer.expand(line);
|
||||
|
||||
URI resultURI = URI.create(result);
|
||||
assertThat("expand('" + line + "')", resultURI.getScheme(), is(expected.getScheme()));
|
||||
assertThat("expand('" + line + "')", resultURI.getPath(), is(expected.getPath()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testNormalizeWarAsString(final Scenario scenario)
|
||||
{
|
||||
// Normalize WAR as String path
|
||||
assertNormalize(scenario, scenario.war.toString(), scenario.war.toString());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testNormalizeJettyBaseAsFile(final Scenario scenario)
|
||||
{
|
||||
// Normalize jetty.base as File path
|
||||
assertNormalize(scenario, scenario.jettyBase.toFile(), "${jetty.base}");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testNormalizeJettyHomeAsFile(final Scenario scenario)
|
||||
{
|
||||
// Normalize jetty.home as File path
|
||||
String expected = scenario.jettyBase.equals(scenario.jettyHome) ? "${jetty.base}" : "${jetty.home}";
|
||||
assertNormalize(scenario, scenario.jettyHome.toFile(), expected);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testNormalizeJettyBaseAsPath(final Scenario scenario)
|
||||
{
|
||||
// Normalize jetty.base as File path
|
||||
assertNormalize(scenario, scenario.jettyBase, "${jetty.base}");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testNormalizeJettyHomeAsPath(final Scenario scenario)
|
||||
{
|
||||
// Normalize jetty.home as File path
|
||||
String expected = scenario.jettyBase.equals(scenario.jettyHome) ? "${jetty.base}" : "${jetty.home}";
|
||||
assertNormalize(scenario, scenario.jettyHome, expected);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testNormalizeJettyBaseAsURIWithAuthority(final Scenario scenario)
|
||||
{
|
||||
// Normalize jetty.base as URI path
|
||||
// Path.toUri() typically includes an URI authority
|
||||
assertNormalize(scenario, scenario.jettyBase.toUri(), "${jetty.base.uri}");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testNormalizeJettyBaseAsURIWithoutAuthority(final Scenario scenario)
|
||||
{
|
||||
// Normalize jetty.base as URI path
|
||||
// File.toURI() typically DOES NOT include an URI authority
|
||||
assertNormalize(scenario, scenario.jettyBase.toFile().toURI(), "${jetty.base.uri}");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testNormalizeJettyHomeAsURIWithAuthority(final Scenario scenario)
|
||||
{
|
||||
// Normalize jetty.home as URI path
|
||||
String expected = scenario.jettyBase.equals(scenario.jettyHome) ? "${jetty.base.uri}" : "${jetty.home.uri}";
|
||||
|
||||
// Path.toUri() typically includes an URI authority
|
||||
assertNormalize(scenario, scenario.jettyHome.toUri(), expected);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testNormalizeJettyHomeAsURIWithoutAuthority(final Scenario scenario)
|
||||
{
|
||||
// Normalize jetty.home as URI path
|
||||
String expected = scenario.jettyBase.equals(scenario.jettyHome) ? "${jetty.base.uri}" : "${jetty.home.uri}";
|
||||
|
||||
// File.toURI() typically DOES NOT include an URI authority
|
||||
assertNormalize(scenario, scenario.jettyHome.toFile().toURI(), expected);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testExpandJettyBase(final Scenario scenario)
|
||||
{
|
||||
// Expand jetty.base
|
||||
assertExpandPath(scenario, "${jetty.base}", scenario.jettyBase.toString());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testExpandJettyHome(final Scenario scenario)
|
||||
{
|
||||
// Expand jetty.home
|
||||
assertExpandPath(scenario, "${jetty.home}", scenario.jettyHome.toString());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testNormalizeWarAsURI(final Scenario scenario)
|
||||
{
|
||||
// Normalize WAR as URI
|
||||
URI testWarURI = scenario.war.toUri();
|
||||
assertNormalize(scenario, testWarURI, "${WAR.uri}");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testNormalizeWarDeepAsPath(final Scenario scenario)
|
||||
{
|
||||
// Normalize WAR deep path as File
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref");
|
||||
assertNormalize(scenario, testWarDeep, "${WAR.path}" + FS.separators("/deep/ref"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testNormalizeWarDeepAsString(final Scenario scenario)
|
||||
{
|
||||
// Normalize WAR deep path as String
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref");
|
||||
assertNormalize(scenario, testWarDeep.toString(), testWarDeep.toString());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testNormalizeWarDeepAsURI(final Scenario scenario)
|
||||
{
|
||||
// Normalize WAR deep path as URI
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref");
|
||||
assertNormalize(scenario, testWarDeep.toUri(), "${WAR.uri}/deep/ref");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testExpandWarDeep(final Scenario scenario)
|
||||
{
|
||||
// Expand WAR deep path
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref");
|
||||
URI uri = URI.create("jar:" + testWarDeep.toUri().toASCIIString() + "!/other/file");
|
||||
assertExpandURI(scenario, "jar:${WAR.uri}/deep/ref!/other/file", uri);
|
||||
}
|
||||
|
||||
public static class Scenario
|
||||
{
|
||||
private final Path jettyHome;
|
||||
private final Path jettyBase;
|
||||
private final Path war;
|
||||
private final String arch;
|
||||
private final String title;
|
||||
private final AttributeNormalizer normalizer;
|
||||
|
||||
public Scenario(String arch, String title, Path jettyHome, Path jettyBase, Path war)
|
||||
{
|
||||
this.arch = arch;
|
||||
this.title = title;
|
||||
|
||||
// Grab specific values of interest in general
|
||||
this.jettyHome = jettyHome;
|
||||
this.jettyBase = jettyBase;
|
||||
this.war = war;
|
||||
|
||||
assertTrue(Files.exists(this.jettyHome));
|
||||
assertTrue(Files.exists(this.jettyBase));
|
||||
assertTrue(Files.exists(this.war));
|
||||
|
||||
// Set some System Properties that AttributeNormalizer expects
|
||||
System.setProperty("jetty.home", jettyHome.toString());
|
||||
System.setProperty("jetty.base", jettyBase.toString());
|
||||
|
||||
// Setup normalizer
|
||||
Resource webresource = resourceFactory.newResource(war);
|
||||
this.normalizer = new AttributeNormalizer(webresource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s [%s]", this.title, this.arch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +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.ee10.quickstart;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class AttributeNormalizerToCanonicalUriTest
|
||||
{
|
||||
public static Stream<String[]> sampleUris()
|
||||
{
|
||||
List<String[]> data = new ArrayList<>();
|
||||
|
||||
// root without authority
|
||||
data.add(new String[]{"file:/", "file:/"});
|
||||
data.add(new String[]{"file:/F:/", "file:/F:"});
|
||||
|
||||
// root with empty authority
|
||||
data.add(new String[]{"file:///", "file:///"});
|
||||
data.add(new String[]{"file:///F:/", "file:///F:"});
|
||||
|
||||
// deep directory - no authority
|
||||
data.add(new String[]{"file:/home/user/code/", "file:/home/user/code"});
|
||||
data.add(new String[]{"file:/C:/code/", "file:/C:/code"});
|
||||
|
||||
// deep directory - with authority
|
||||
data.add(new String[]{"file:///home/user/code/", "file:///home/user/code"});
|
||||
data.add(new String[]{"file:///C:/code/", "file:///C:/code"});
|
||||
|
||||
// Some non-file tests
|
||||
data.add(new String[]{"http://webtide.com/", "http://webtide.com/"});
|
||||
data.add(new String[]{"http://webtide.com/cometd/", "http://webtide.com/cometd"});
|
||||
|
||||
return data.stream();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("sampleUris")
|
||||
public void testCanonicalURI(String input, String expected)
|
||||
{
|
||||
URI inputURI = URI.create(input);
|
||||
URI actual = AttributeNormalizer.toCanonicalURI(inputURI);
|
||||
assertThat(input, actual.toASCIIString(), is(expected));
|
||||
}
|
||||
}
|
|
@ -723,16 +723,13 @@ 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 resolve the extra classpath jars
|
||||
*/
|
||||
protected List<Resource> findExtraClasspathJars(WebAppContext context)
|
||||
throws Exception
|
||||
{
|
||||
if (context == null || context.getExtraClasspath() == null)
|
||||
return null;
|
||||
|
||||
return context.getExtraClasspath()
|
||||
.getResources()
|
||||
.stream()
|
||||
.filter(this::isFileSupported)
|
||||
.collect(Collectors.toList());
|
||||
|
@ -775,7 +772,6 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
return List.of();
|
||||
|
||||
return context.getExtraClasspath()
|
||||
.getResources()
|
||||
.stream()
|
||||
.filter(Resource::isDirectory)
|
||||
.collect(Collectors.toList());
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
@ -43,7 +44,6 @@ 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;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||
import org.eclipse.jetty.util.resource.Resources;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -113,7 +113,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
*/
|
||||
boolean isParentLoaderPriority();
|
||||
|
||||
ResourceCollection getExtraClasspath();
|
||||
List<Resource> getExtraClasspath();
|
||||
|
||||
boolean isServerResource(String name, URL parentUrl);
|
||||
|
||||
|
@ -193,13 +193,8 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
}
|
||||
}
|
||||
|
||||
if (context.getExtraClasspath() != null)
|
||||
{
|
||||
for (Resource resource : context.getExtraClasspath().getResources())
|
||||
{
|
||||
addClassPath(resource);
|
||||
}
|
||||
}
|
||||
for (Resource extra : context.getExtraClasspath())
|
||||
addClassPath(extra);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -224,16 +219,23 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
}
|
||||
|
||||
/**
|
||||
* @param resources The resources to add to the classpath
|
||||
* @throws IOException if unable to add classpath from resource
|
||||
* @param resource The resources to add to the classpath
|
||||
*/
|
||||
public void addClassPath(Resource resources)
|
||||
throws IOException
|
||||
public void addClassPath(Resource resource)
|
||||
{
|
||||
for (Resource resource: resources)
|
||||
for (Resource r : resource)
|
||||
{
|
||||
if (Resources.exists(resource))
|
||||
addURL(resource.getURI().toURL());
|
||||
if (resource.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
addURL(r.getURI().toURL());
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
throw new IllegalArgumentException("File not resolvable or incompatible with URLClassloader: " + resource);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
|
|
|
@ -30,6 +30,7 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.ServletRegistration.Dynamic;
|
||||
|
@ -59,7 +60,6 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
|
|||
import org.eclipse.jetty.util.component.ClassLoaderDump;
|
||||
import org.eclipse.jetty.util.component.DumpableCollection;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||
import org.eclipse.jetty.util.resource.Resources;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -132,7 +132,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
private boolean _persistTmpDir = false;
|
||||
|
||||
private String _war;
|
||||
private ResourceCollection _extraClasspath;
|
||||
private List<Resource> _extraClasspath;
|
||||
private Throwable _unavailableException;
|
||||
|
||||
private Map<String, String> _resourceAliases;
|
||||
|
@ -1235,29 +1235,29 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
*/
|
||||
@Override
|
||||
@ManagedAttribute(value = "extra classpath for context classloader", readonly = true)
|
||||
public ResourceCollection getExtraClasspath()
|
||||
public List<Resource> getExtraClasspath()
|
||||
{
|
||||
return _extraClasspath;
|
||||
return _extraClasspath == null ? Collections.emptyList() : _extraClasspath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Extra ClassPath via delimited String.
|
||||
* <p>
|
||||
* This is a convenience method for {@link #setExtraClasspath(ResourceCollection)}
|
||||
* 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 '/'.
|
||||
* @see #setExtraClasspath(ResourceCollection)
|
||||
* @see #setExtraClasspath(List)
|
||||
*/
|
||||
public void setExtraClasspath(String extraClasspath)
|
||||
{
|
||||
List<URI> uris = URIUtil.split(extraClasspath);
|
||||
setExtraClasspath(this.getResourceFactory().newResource(uris));
|
||||
setExtraClasspath(uris.stream().map(this.getResourceFactory()::newResource).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public void setExtraClasspath(ResourceCollection extraClasspath)
|
||||
public void setExtraClasspath(List<Resource> extraClasspath)
|
||||
{
|
||||
_extraClasspath = extraClasspath;
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ import org.eclipse.jetty.ee9.quickstart.QuickStartConfiguration;
|
|||
import org.eclipse.jetty.ee9.webapp.Configuration;
|
||||
import org.eclipse.jetty.ee9.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.resource.CombinedResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -47,9 +47,9 @@ public class MavenQuickStartConfiguration extends QuickStartConfiguration
|
|||
//Iterate over all of the resource bases and ignore any that were original bases, just
|
||||
//deleting the overlays
|
||||
Resource res = context.getBaseResource();
|
||||
if (res instanceof ResourceCollection)
|
||||
if (res instanceof CombinedResource)
|
||||
{
|
||||
for (Resource r : ((ResourceCollection)res).getResources())
|
||||
for (Resource r : ((CombinedResource)res).getResources())
|
||||
{
|
||||
if (originalBaseStr.contains(r.toString()))
|
||||
continue;
|
||||
|
|
|
@ -40,8 +40,8 @@ import org.eclipse.jetty.ee9.webapp.WebAppContext;
|
|||
import org.eclipse.jetty.util.FileID;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.resource.CombinedResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||
import org.eclipse.jetty.util.resource.Resources;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -220,7 +220,7 @@ public class MavenWebAppContext extends WebAppContext
|
|||
* configuration
|
||||
*
|
||||
* @param resourceBases Array of resources strings to set as a
|
||||
* {@link ResourceCollection}.
|
||||
* {@link CombinedResource}.
|
||||
*/
|
||||
public void setResourceBases(String[] resourceBases)
|
||||
{
|
||||
|
|
|
@ -29,8 +29,8 @@ import org.eclipse.jetty.ee9.quickstart.QuickStartConfiguration;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.resource.CombinedResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.eclipse.jetty.xml.XmlConfiguration;
|
||||
|
||||
/**
|
||||
|
@ -105,8 +105,8 @@ public class WebAppPropertyConverter
|
|||
|
||||
//send over the calculated resource bases that includes unpacked overlays
|
||||
Resource baseResource = webApp.getBaseResource();
|
||||
if (baseResource instanceof ResourceCollection)
|
||||
props.put(BASE_DIRS, toCSV(((ResourceCollection)webApp.getBaseResource()).getResources()));
|
||||
if (baseResource instanceof CombinedResource)
|
||||
props.put(BASE_DIRS, toCSV(((CombinedResource)webApp.getBaseResource()).getResources()));
|
||||
else if (baseResource instanceof Resource)
|
||||
props.put(BASE_DIRS, webApp.getBaseResource().toString());
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@ import org.eclipse.jetty.ee9.webapp.WebAppContext;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.resource.CombinedResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
|
@ -152,10 +152,10 @@ public class TestWebAppPropertyConverter
|
|||
assertEquals(true, webApp.isPersistTempDirectory());
|
||||
assertEquals(war.getAbsolutePath(), webApp.getWar());
|
||||
assertEquals(webXml.getAbsolutePath(), webApp.getDescriptor());
|
||||
assertThat(webApp.getBaseResource(), instanceOf(ResourceCollection.class));
|
||||
assertThat(webApp.getBaseResource(), instanceOf(CombinedResource.class));
|
||||
|
||||
ResourceCollection resourceCollection = (ResourceCollection)webApp.getBaseResource();
|
||||
List<URI> actual = resourceCollection.getResources().stream().filter(Objects::nonNull).map(Resource::getURI).toList();
|
||||
CombinedResource combinedResource = (CombinedResource)webApp.getBaseResource();
|
||||
List<URI> actual = combinedResource.getResources().stream().filter(Objects::nonNull).map(Resource::getURI).toList();
|
||||
URI[] expected = new URI[]{base1.toURI(), base2.toURI()};
|
||||
assertThat(actual, containsInAnyOrder(expected));
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.eclipse.jetty.util.IO;
|
|||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.resource.AttributeNormalizer;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||
import org.eclipse.jetty.util.resource.Resources;
|
||||
|
|
|
@ -57,6 +57,7 @@ import org.eclipse.jetty.ee9.webapp.WebInfConfiguration;
|
|||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.resource.AttributeNormalizer;
|
||||
import org.eclipse.jetty.util.security.Constraint;
|
||||
import org.eclipse.jetty.xml.XmlAppendable;
|
||||
import org.slf4j.Logger;
|
||||
|
|
|
@ -729,7 +729,6 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
return null;
|
||||
|
||||
return context.getExtraClasspath()
|
||||
.getResources()
|
||||
.stream()
|
||||
.filter(this::isFileSupported)
|
||||
.collect(Collectors.toList());
|
||||
|
@ -773,7 +772,6 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
return List.of();
|
||||
|
||||
return context.getExtraClasspath()
|
||||
.getResources()
|
||||
.stream()
|
||||
.filter(Resource::isDirectory)
|
||||
.collect(Collectors.toList());
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
@ -43,7 +44,6 @@ 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;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||
import org.eclipse.jetty.util.resource.Resources;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -113,7 +113,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
*/
|
||||
boolean isParentLoaderPriority();
|
||||
|
||||
ResourceCollection getExtraClasspath();
|
||||
List<Resource> getExtraClasspath();
|
||||
|
||||
boolean isServerResource(String name, URL parentUrl);
|
||||
|
||||
|
@ -193,13 +193,8 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
}
|
||||
}
|
||||
|
||||
if (context.getExtraClasspath() != null)
|
||||
{
|
||||
for (Resource resource : context.getExtraClasspath().getResources())
|
||||
{
|
||||
addClassPath(resource);
|
||||
}
|
||||
}
|
||||
for (Resource extra : context.getExtraClasspath())
|
||||
addClassPath(extra);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -224,23 +219,30 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
}
|
||||
|
||||
/**
|
||||
* @param resources The resources to add to the classpath
|
||||
* @throws IOException if unable to add classpath from resource
|
||||
* @param resource The resources to add to the classpath
|
||||
*/
|
||||
public void addClassPath(Resource resources)
|
||||
throws IOException
|
||||
public void addClassPath(Resource resource)
|
||||
{
|
||||
for (Resource resource: resources)
|
||||
for (Resource r : resource)
|
||||
{
|
||||
if (Resources.exists(resource))
|
||||
addURL(resource.getURI().toURL());
|
||||
if (resource.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
addURL(r.getURI().toURL());
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
throw new IllegalArgumentException("File not resolvable or incompatible with URLClassloader: " + resource);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Check resource exists and is not a nested jar: {}", resource);
|
||||
throw new IllegalArgumentException("File not resolvable or incompatible with URLClassloader: " + resource);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,6 +30,7 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.ServletRegistration.Dynamic;
|
||||
|
@ -63,7 +64,6 @@ import org.eclipse.jetty.util.component.ClassLoaderDump;
|
|||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.component.DumpableCollection;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||
import org.eclipse.jetty.util.resource.Resources;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -135,7 +135,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
private boolean _persistTmpDir = false;
|
||||
|
||||
private String _war;
|
||||
private ResourceCollection _extraClasspath;
|
||||
private List<Resource> _extraClasspath;
|
||||
private Throwable _unavailableException;
|
||||
|
||||
private Map<String, String> _resourceAliases;
|
||||
|
@ -1233,30 +1233,30 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
*/
|
||||
@Override
|
||||
@ManagedAttribute(value = "extra classpath for context classloader", readonly = true)
|
||||
public ResourceCollection getExtraClasspath()
|
||||
public List<Resource> getExtraClasspath()
|
||||
{
|
||||
return _extraClasspath;
|
||||
return _extraClasspath == null ? Collections.emptyList() : _extraClasspath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Extra ClassPath via delimited String.
|
||||
* <p>
|
||||
* This is a convenience method for {@link #setExtraClasspath(ResourceCollection)}
|
||||
* 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(ResourceCollection)
|
||||
* @see #setExtraClasspath(List)
|
||||
*/
|
||||
public void setExtraClasspath(String extraClasspath) throws IOException
|
||||
{
|
||||
List<URI> uris = URIUtil.split(extraClasspath);
|
||||
setExtraClasspath(this.getResourceFactory().newResource(uris));
|
||||
setExtraClasspath(uris.stream().map(this.getResourceFactory()::newResource).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public void setExtraClasspath(ResourceCollection extraClasspath)
|
||||
public void setExtraClasspath(List<Resource> extraClasspath)
|
||||
{
|
||||
_extraClasspath = extraClasspath;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue