Jetty-12 : Immutable ResourceCollection and Context based mount management (#8337)
* Immutable ResourceCollection with mount management. * Internal List<Resource> is now immutable so that .getResources() cannot be modified. * Improved Resource.toJarFileUri implementation that keeps the deep archive references * Introducing Resource.mountCollection() methods + ResourceCollection is now a private class + Resource.mountCollection() returns a Mount + Places that use this technique are now putting the Mount in the context's beans for the context to close those mounts. * Cleanup names/comments in FileSystemResourceTest * Adding missing test of attempting to create a Resource from a URI `jar:file:foo.jar!/` while not mounted. * Reworked ResourceCollection behaviors based on feedback. + Eliminated all Resource.mountCollection() methods except the Collection<URI> one. * Eliminated Resource.mountIfNeeded(Resource) * Eliminated Resource.fromList implementations * Introduced @gregw Resource.of() implementations * Introduced Resource.split() to honor old split logic from Jetty 9/10/11, with glob support, but now it only converts to List<URI> * Remove IOException from Mount.root() method * ResourceCollection now flattens and uniques any nested ResourceCollection entries it encounters * Expanded ResourceCollectionTest to cover more code paths * Add ResourceTest for new split() method * Fixing testcase to use a directory that exists on setExtraClasspath * Increase reliability of WebAppContextTests * Updates for working with webapp.extraClasspath * Introduced Resource.unwrapContainer to help in servlet cases where the raw JAR path should be represented in a ServletContext attribute. * Made FileSystemPool.containerUri just use new Resource.unwrapContainer * webapp MetaData updated to use URIs references to Libs (not File objects) * jetty-ee#-maven-plugin use URIs for its classpath tracking to aid in mounting issues later * webapp extraClasspath supports raw JAR references as well as glob now, supported by Resource.split(String)
This commit is contained in:
parent
0f35590ab4
commit
c6c0eb0313
|
@ -69,7 +69,6 @@ import org.eclipse.jetty.server.handler.gzip.GzipHandler;
|
|||
import org.eclipse.jetty.unixdomain.server.UnixDomainServerConnector;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
|
||||
|
@ -779,12 +778,11 @@ public class HTTPServerDocs
|
|||
ResourceHandler handler = new ResourceHandler();
|
||||
|
||||
// For multiple directories, use ResourceCollection.
|
||||
ResourceCollection directories = new ResourceCollection();
|
||||
// TODO: how to add paths to ResourceCollection?
|
||||
// directories.addPath("/path/to/static/resources/");
|
||||
// directories.addPath("/another/path/to/static/resources/");
|
||||
|
||||
handler.setBaseResource(directories);
|
||||
Resource resource = Resource.of(
|
||||
Resource.newResource("/path/to/static/resources/"),
|
||||
Resource.newResource("/another/path/to/static/resources/")
|
||||
);
|
||||
handler.setBaseResource(resource);
|
||||
// end::multipleResourcesHandler[]
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.eclipse.jetty.server.Server;
|
|||
import org.eclipse.jetty.util.Attributes;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
@ -530,6 +531,15 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
|
|||
{
|
||||
// TODO lots of stuff in previous doStart. Some might go here, but most probably goes to the ServletContentHandler ?
|
||||
_context.call(super::doStop, null);
|
||||
|
||||
// cleanup any Mounts associated with the ContextHandler on stop.
|
||||
// TODO: but what if the context is restarted? how do we remount? do we care?
|
||||
java.util.Collection<Resource.Mount> mounts = getBeans(Resource.Mount.class);
|
||||
mounts.forEach((mount) ->
|
||||
{
|
||||
IO.close(mount);
|
||||
removeBean(mount);
|
||||
});
|
||||
}
|
||||
|
||||
public boolean checkVirtualHost(Request request)
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jetty.http.CompressedContentFormat;
|
||||
import org.eclipse.jetty.http.HttpContent;
|
||||
|
@ -31,7 +32,6 @@ import org.eclipse.jetty.toolchain.test.FS;
|
|||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.resource.PathResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
@ -44,6 +44,8 @@ import static org.hamcrest.CoreMatchers.not;
|
|||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(WorkDirExtension.class)
|
||||
|
@ -112,10 +114,12 @@ public class ResourceCacheTest
|
|||
{
|
||||
Path basePath = createUtilTestResources(workDir.getEmptyPathDir());
|
||||
|
||||
ResourceCollection rc = new ResourceCollection(
|
||||
Resource.newResource(basePath.resolve("one")),
|
||||
Resource.newResource(basePath.resolve("two")),
|
||||
Resource.newResource(basePath.resolve("three")));
|
||||
List<Resource> resourceList = Stream.of("one", "two", "three")
|
||||
.map(basePath::resolve)
|
||||
.map(Resource::newResource)
|
||||
.toList();
|
||||
|
||||
ResourceCollection rc = Resource.of(resourceList);
|
||||
|
||||
List<Resource> r = rc.getResources();
|
||||
MimeTypes mime = new MimeTypes();
|
||||
|
@ -132,7 +136,7 @@ public class ResourceCacheTest
|
|||
assertEquals(getContent(rc2, "2.txt"), "2 - two");
|
||||
assertEquals(getContent(rc2, "3.txt"), "3 - three");
|
||||
|
||||
assertEquals(null, getContent(rc3, "1.txt"));
|
||||
assertNull(getContent(rc3, "1.txt"));
|
||||
assertEquals(getContent(rc3, "2.txt"), "2 - three");
|
||||
assertEquals(getContent(rc3, "3.txt"), "3 - three");
|
||||
}
|
||||
|
@ -142,10 +146,12 @@ public class ResourceCacheTest
|
|||
{
|
||||
Path basePath = createUtilTestResources(workDir.getEmptyPathDir());
|
||||
|
||||
ResourceCollection rc = new ResourceCollection(
|
||||
Resource.newResource(basePath.resolve("one")),
|
||||
Resource.newResource(basePath.resolve("two")),
|
||||
Resource.newResource(basePath.resolve("three")));
|
||||
List<Resource> resourceList = Stream.of("one", "two", "three")
|
||||
.map(basePath::resolve)
|
||||
.map(Resource::newResource)
|
||||
.toList();
|
||||
|
||||
ResourceCollection rc = Resource.of(resourceList);
|
||||
|
||||
List<Resource> r = rc.getResources();
|
||||
MimeTypes mime = new MimeTypes();
|
||||
|
@ -170,7 +176,7 @@ public class ResourceCacheTest
|
|||
assertEquals(getContent(rc2, "2.txt"), "2 - two");
|
||||
assertEquals(getContent(rc2, "3.txt"), "3 - three");
|
||||
|
||||
assertEquals(null, getContent(rc3, "1.txt"));
|
||||
assertNull(getContent(rc3, "1.txt"));
|
||||
assertEquals(getContent(rc3, "2.txt"), "2 - three");
|
||||
assertEquals(getContent(rc3, "3.txt"), "3 - three");
|
||||
}
|
||||
|
@ -208,9 +214,9 @@ public class ResourceCacheTest
|
|||
cache.setMaxCachedFileSize(85);
|
||||
cache.setMaxCachedFiles(4);
|
||||
|
||||
assertTrue(cache.getContent("does not exist", 4096) == null);
|
||||
assertNull(cache.getContent("does not exist", 4096));
|
||||
assertTrue(cache.getContent(names[9], 4096) instanceof ResourceHttpContent);
|
||||
assertTrue(cache.getContent(names[9], 4096).getBuffer() != null);
|
||||
assertNotNull(cache.getContent(names[9], 4096).getBuffer());
|
||||
|
||||
HttpContent content;
|
||||
content = cache.getContent(names[8], 4096);
|
||||
|
|
|
@ -17,8 +17,11 @@ import java.io.UnsupportedEncodingException;
|
|||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
|
@ -981,6 +984,36 @@ public class StringUtil
|
|||
return csvSplit(s, 1, s.length() - 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Present the results of a {@link StringTokenizer} as an {@link Iterator} of type {@link String}
|
||||
* @param input the StringTokenizer input
|
||||
* @param delim the StringTokenizer delim
|
||||
* @return the resulting iterator for the StringTokenizer
|
||||
* @see StringTokenizer#StringTokenizer(java.lang.String, java.lang.String)
|
||||
*/
|
||||
public static Iterator<String> tokenizerAsIterator(String input, String delim)
|
||||
{
|
||||
if (isBlank(input))
|
||||
return Collections.emptyIterator();
|
||||
|
||||
return new Iterator<>()
|
||||
{
|
||||
StringTokenizer tokenizer = new StringTokenizer(input, delim);
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return tokenizer.hasMoreTokens();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String next()
|
||||
{
|
||||
return tokenizer.nextToken();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a CSV string using {@link #csvSplit(List, String, int, int)}
|
||||
*
|
||||
|
|
|
@ -196,15 +196,7 @@ public class FileSystemPool implements Dumpable
|
|||
|
||||
static URI containerUri(URI uri)
|
||||
{
|
||||
String scheme = uri.getScheme();
|
||||
if ((scheme == null) || !scheme.equalsIgnoreCase("jar"))
|
||||
return null;
|
||||
|
||||
String spec = uri.getRawSchemeSpecificPart();
|
||||
int sep = spec.indexOf("!/");
|
||||
if (sep != -1)
|
||||
spec = spec.substring(0, sep);
|
||||
return URI.create(spec);
|
||||
return Resource.unwrapContainer(uri);
|
||||
}
|
||||
|
||||
private static class Bucket
|
||||
|
@ -266,7 +258,7 @@ public class FileSystemPool implements Dumpable
|
|||
private final URI uri;
|
||||
private final Resource root;
|
||||
|
||||
private Mount(URI uri) throws IOException
|
||||
private Mount(URI uri)
|
||||
{
|
||||
this.uri = uri;
|
||||
this.root = Resource.newResource(uri);
|
||||
|
@ -291,7 +283,7 @@ public class FileSystemPool implements Dumpable
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return getClass().getSimpleName() + " uri=" + uri;
|
||||
return String.format("%s[uri=%s,root=%s]", getClass().getSimpleName(), uri, root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import java.nio.file.DirectoryIteratorException;
|
|||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.ProviderNotFoundException;
|
||||
|
@ -37,13 +36,16 @@ import java.text.DateFormat;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.Index;
|
||||
|
@ -77,8 +79,58 @@ public abstract class Resource implements ResourceFactory
|
|||
.with("jar:")
|
||||
.build();
|
||||
|
||||
/**
|
||||
* <p>Create a ResourceCollection from an unknown set of URIs.</p>
|
||||
*
|
||||
* <p>
|
||||
* Use this if you are working with URIs from an unknown source,
|
||||
* such as a user configuration. As some of the entries
|
||||
* might need mounting, but we cannot determine that yet.
|
||||
* </p>
|
||||
*
|
||||
* @param uris collection of URIs to mount into a {@code ResourceCollection}
|
||||
* @return the {@link Mount} with a root pointing to the {@link ResourceCollection}
|
||||
*/
|
||||
public static Resource.Mount mountCollection(Collection<URI> uris)
|
||||
{
|
||||
List<Resource> resources = new ArrayList<>();
|
||||
List<Resource.Mount> mounts = new ArrayList<>();
|
||||
|
||||
try
|
||||
{
|
||||
// track URIs that have been seen, to avoid adding duplicates.
|
||||
Set<URI> seenUris = new HashSet<>();
|
||||
|
||||
for (URI uri : uris)
|
||||
{
|
||||
if (seenUris.contains(uri))
|
||||
continue; // skip this one
|
||||
Resource.Mount mount = Resource.mountIfNeeded(uri);
|
||||
if (mount != null)
|
||||
{
|
||||
mounts.add(mount);
|
||||
resources.add(mount.root()); // use mounted resource that has Path with proper FileSystem reference in it.
|
||||
}
|
||||
else
|
||||
{
|
||||
resources.add(Resource.newResource(uri));
|
||||
}
|
||||
seenUris.add(uri);
|
||||
}
|
||||
|
||||
return new ResourceCollection.Mount(resources, mounts);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
// can't create ResourceCollection.Mount, so let's unmount and rethrow.
|
||||
mounts.forEach(IO::close);
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Mount a URI if it is needed.</p>
|
||||
*
|
||||
* @param uri The URI to mount that may require a FileSystem (e.g. "jar:file://tmp/some.jar!/directory/file.txt")
|
||||
* @return A reference counted {@link Mount} for that file system or null. Callers should call {@link Mount#close()} once
|
||||
* they no longer require any resources from a mounted resource.
|
||||
|
@ -87,11 +139,21 @@ public abstract class Resource implements ResourceFactory
|
|||
*/
|
||||
public static Resource.Mount mountIfNeeded(URI uri)
|
||||
{
|
||||
if (uri == null || uri.getScheme() == null)
|
||||
if (uri == null)
|
||||
return null;
|
||||
String scheme = uri.getScheme();
|
||||
if (scheme == null)
|
||||
return null;
|
||||
if (!isArchive(uri))
|
||||
return null;
|
||||
try
|
||||
{
|
||||
return (uri.getScheme().equalsIgnoreCase("jar")) ? FileSystemPool.INSTANCE.mount(uri) : null;
|
||||
if (scheme.equalsIgnoreCase("jar"))
|
||||
{
|
||||
return FileSystemPool.INSTANCE.mount(uri);
|
||||
}
|
||||
// TODO: review contract, should this be null, or an empty mount?
|
||||
return null;
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
|
@ -109,6 +171,8 @@ public abstract class Resource implements ResourceFactory
|
|||
*/
|
||||
public static Resource.Mount mount(URI uri) throws IOException
|
||||
{
|
||||
if (!isArchive(uri))
|
||||
throw new IllegalArgumentException("URI is not a Java Archive: " + uri);
|
||||
if (!uri.getScheme().equalsIgnoreCase("jar"))
|
||||
throw new IllegalArgumentException("not an allowed URI: " + uri);
|
||||
return FileSystemPool.INSTANCE.mount(uri);
|
||||
|
@ -122,27 +186,179 @@ public abstract class Resource implements ResourceFactory
|
|||
*/
|
||||
public static Resource.Mount mountJar(Path path) throws IOException
|
||||
{
|
||||
if (!isArchive(path))
|
||||
throw new IllegalArgumentException("Path is not a Java Archive: " + path);
|
||||
URI pathUri = path.toUri();
|
||||
if (!pathUri.getScheme().equalsIgnoreCase("file"))
|
||||
throw new IllegalArgumentException("not an allowed path: " + path);
|
||||
URI jarUri = URI.create(toJarRoot(pathUri.toString()));
|
||||
throw new IllegalArgumentException("Not an allowed path: " + path);
|
||||
URI jarUri = toJarFileUri(pathUri);
|
||||
if (jarUri == null)
|
||||
throw new IllegalArgumentException("Not a mountable archive: " + path);
|
||||
return FileSystemPool.INSTANCE.mount(jarUri);
|
||||
}
|
||||
|
||||
public static String toJarRoot(String jarFile)
|
||||
/**
|
||||
* <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
|
||||
*/
|
||||
public static ResourceCollection of(List<Resource> resources)
|
||||
{
|
||||
return "jar:" + jarFile + "!/";
|
||||
if (resources == null || resources.isEmpty())
|
||||
throw new IllegalArgumentException("No resources");
|
||||
|
||||
return new ResourceCollection(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
|
||||
*/
|
||||
public static ResourceCollection of(Resource... resources)
|
||||
{
|
||||
if (resources == null || resources.length == 0)
|
||||
throw new IllegalArgumentException("No resources");
|
||||
|
||||
return new ResourceCollection(List.of(resources));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if Path is a Java Archive (ends in {@code .jar}, {@code .war}, or {@code .zip}).
|
||||
*
|
||||
* @param path the path to test
|
||||
* @return true if path is a {@link Files#isRegularFile(Path, LinkOption...)} and name ends with {@code .jar}, {@code .war}, or {@code .zip}
|
||||
*/
|
||||
public static boolean isArchive(Path path)
|
||||
{
|
||||
if (path == null)
|
||||
return false;
|
||||
if (!Files.isRegularFile(path))
|
||||
return false;
|
||||
String filename = path.getFileName().toString().toLowerCase(Locale.ENGLISH);
|
||||
return (filename.endsWith(".jar") || filename.endsWith(".war") || filename.endsWith(".zip"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if URI is a Java Archive. (ends with {@code .jar}, {@code .war}, or {@code .zip}).
|
||||
*
|
||||
* @param uri the URI to test
|
||||
* @return true if the URI has a path that seems to point to a ({@code .jar}, {@code .war}, or {@code .zip}).
|
||||
*/
|
||||
public static boolean isArchive(URI uri)
|
||||
{
|
||||
if (uri == null)
|
||||
return false;
|
||||
if (uri.getScheme() == null)
|
||||
return false;
|
||||
String path = uri.getPath();
|
||||
int idxEnd = path == null ? -1 : path.length();
|
||||
if (uri.getScheme().equalsIgnoreCase("jar"))
|
||||
{
|
||||
String ssp = uri.getRawSchemeSpecificPart();
|
||||
path = URI.create(ssp).getPath();
|
||||
idxEnd = path.length();
|
||||
// look for `!/` split
|
||||
int jarEnd = path.indexOf("!/");
|
||||
if (jarEnd >= 0)
|
||||
idxEnd = jarEnd;
|
||||
}
|
||||
if (path == null)
|
||||
return false;
|
||||
int idxLastSlash = path.lastIndexOf('/', idxEnd);
|
||||
if (idxLastSlash < 0)
|
||||
return false; // no last slash, can't possibly be a valid jar/war/zip
|
||||
// look for filename suffix
|
||||
int idxSuffix = path.lastIndexOf('.', idxEnd);
|
||||
if (idxSuffix < 0)
|
||||
return false; // no suffix found, can't possibly be a jar/war/zip
|
||||
if (idxSuffix < idxLastSlash)
|
||||
return false; // last dot is before last slash, eg ("/path.to/something")
|
||||
String suffix = path.substring(idxSuffix, idxEnd).toLowerCase(Locale.ENGLISH);
|
||||
return suffix.equals(".jar") || suffix.equals(".war") || suffix.equals(".zip");
|
||||
}
|
||||
|
||||
/**
|
||||
* Take an arbitrary URI and provide a URI that is suitable for mounting the URI as a Java FileSystem.
|
||||
*
|
||||
* The resulting URI will point to the {@code jar:file://foo.jar!/} said Java Archive (jar, war, or zip)
|
||||
*
|
||||
* @param uri the URI to mutate to a {@code jar:file:...} URI.
|
||||
* @return the <code>jar:${uri_to_java_archive}!/${internal-reference}</code> URI or null if not a Java Archive.
|
||||
* @see #isArchive(URI)
|
||||
*/
|
||||
public static URI toJarFileUri(URI uri)
|
||||
{
|
||||
Objects.requireNonNull(uri, "URI");
|
||||
String scheme = Objects.requireNonNull(uri.getScheme(), "URI scheme");
|
||||
|
||||
if (!isArchive(uri))
|
||||
return null;
|
||||
|
||||
boolean hasInternalReference = uri.getRawSchemeSpecificPart().indexOf("!/") > 0;
|
||||
|
||||
if (scheme.equalsIgnoreCase("jar"))
|
||||
{
|
||||
if (uri.getRawSchemeSpecificPart().startsWith("file:"))
|
||||
{
|
||||
// Looking good as a jar:file: URI
|
||||
if (hasInternalReference)
|
||||
return uri; // is all good, no changes needed.
|
||||
else
|
||||
// add the internal reference indicator to the root of the archive
|
||||
return URI.create(uri.toASCIIString() + "!/");
|
||||
}
|
||||
}
|
||||
else if (scheme.equalsIgnoreCase("file"))
|
||||
{
|
||||
String rawUri = uri.toASCIIString();
|
||||
if (hasInternalReference)
|
||||
return URI.create("jar:" + rawUri);
|
||||
else
|
||||
return URI.create("jar:" + rawUri + "!/");
|
||||
}
|
||||
|
||||
// shouldn't be possible to reach this point
|
||||
throw new IllegalArgumentException("Cannot make %s into `jar:file:` URI".formatted(uri));
|
||||
}
|
||||
|
||||
// TODO: will be removed in MultiReleaseJarFile PR, as AnnotationParser is the only thing using this,
|
||||
// and it doesn't need to recreate the URI that it will already have.
|
||||
public static String toJarPath(String jarFile, String pathInJar)
|
||||
{
|
||||
return "jar:" + jarFile + URIUtil.addPaths("!/", pathInJar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwrap a URI to expose its container path reference.
|
||||
*
|
||||
* Take out the container archive name URI from a {@code jar:file:${container-name}!/} URI.
|
||||
*
|
||||
* @param uri the input URI
|
||||
* @return the container String if a {@code jar} scheme, or just the URI untouched.
|
||||
*/
|
||||
public static URI unwrapContainer(URI uri)
|
||||
{
|
||||
Objects.requireNonNull(uri);
|
||||
|
||||
String scheme = uri.getScheme();
|
||||
if ((scheme == null) || !scheme.equalsIgnoreCase("jar"))
|
||||
return uri;
|
||||
|
||||
String spec = uri.getRawSchemeSpecificPart();
|
||||
int sep = spec.indexOf("!/");
|
||||
if (sep != -1)
|
||||
spec = spec.substring(0, sep);
|
||||
return URI.create(spec);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convert a String into a URI suitable for use as a Resource.</p>
|
||||
*
|
||||
* @param resource If the string starts with one of the ALLOWED_SCHEMES, then it is assumed to be a
|
||||
* representation of a {@link URI}, otherwise it is treated as a {@link Path}.
|
||||
* representation of a {@link URI}, otherwise it is treated as a {@link Path}.
|
||||
* @return The {@link URI} form of the resource.
|
||||
*/
|
||||
public static URI toURI(String resource)
|
||||
|
@ -348,6 +564,7 @@ public abstract class Resource implements ResourceFactory
|
|||
/**
|
||||
* Return true if the Resource r is contained in the Resource containingResource, either because
|
||||
* containingResource is a folder or a jar file or any form of resource capable of containing other resources.
|
||||
*
|
||||
* @param r the contained resource
|
||||
* @param containingResource the containing resource
|
||||
* @return true if the Resource is contained, false otherwise
|
||||
|
@ -360,6 +577,7 @@ public abstract class Resource implements ResourceFactory
|
|||
|
||||
/**
|
||||
* Return the Path corresponding to this resource.
|
||||
*
|
||||
* @return the path.
|
||||
*/
|
||||
public abstract Path getPath();
|
||||
|
@ -367,6 +585,7 @@ public abstract class Resource implements ResourceFactory
|
|||
/**
|
||||
* Return true if this resource is contained in the Resource r, either because
|
||||
* r is a folder or a jar file or any form of resource capable of containing other resources.
|
||||
*
|
||||
* @param r the containing resource
|
||||
* @return true if this Resource is contained, false otherwise
|
||||
* @throws IOException Problem accessing resource
|
||||
|
@ -377,6 +596,7 @@ public abstract class Resource implements ResourceFactory
|
|||
* Return true if the passed Resource represents the same resource as the Resource.
|
||||
* For many resource types, this is equivalent to {@link #equals(Object)}, however
|
||||
* for resources types that support aliasing, this maybe some other check (e.g. {@link java.nio.file.Files#isSameFile(Path, Path)}).
|
||||
*
|
||||
* @param resource The resource to check
|
||||
* @return true if the passed resource represents the same resource.
|
||||
*/
|
||||
|
@ -388,6 +608,7 @@ public abstract class Resource implements ResourceFactory
|
|||
/**
|
||||
* Equivalent to {@link Files#exists(Path, LinkOption...)} with the following parameters:
|
||||
* {@link #getPath()} and {@link LinkOption#NOFOLLOW_LINKS}.
|
||||
*
|
||||
* @return true if the represented resource exists.
|
||||
*/
|
||||
public boolean exists()
|
||||
|
@ -398,6 +619,7 @@ public abstract class Resource implements ResourceFactory
|
|||
/**
|
||||
* Equivalent to {@link Files#isDirectory(Path, LinkOption...)} with the following parameter:
|
||||
* {@link #getPath()}.
|
||||
*
|
||||
* @return true if the represented resource is a container/directory.
|
||||
*/
|
||||
public boolean isDirectory()
|
||||
|
@ -409,6 +631,7 @@ public abstract class Resource implements ResourceFactory
|
|||
* Time resource was last modified.
|
||||
* Equivalent to {@link Files#getLastModifiedTime(Path, LinkOption...)} with the following parameter:
|
||||
* {@link #getPath()} then returning {@link FileTime#toMillis()}.
|
||||
*
|
||||
* @return the last modified time as milliseconds since unix epoch or
|
||||
* 0 if {@link Files#getLastModifiedTime(Path, LinkOption...)} throws {@link IOException}.
|
||||
*/
|
||||
|
@ -430,6 +653,7 @@ public abstract class Resource implements ResourceFactory
|
|||
* Length of the resource.
|
||||
* Equivalent to {@link Files#size(Path)} with the following parameter:
|
||||
* {@link #getPath()}.
|
||||
*
|
||||
* @return the length of the resource or 0 if {@link Files#size(Path)} throws {@link IOException}.
|
||||
*/
|
||||
public long length()
|
||||
|
@ -495,6 +719,7 @@ public abstract class Resource implements ResourceFactory
|
|||
* Deletes the given resource
|
||||
* Equivalent to {@link Files#deleteIfExists(Path)} with the following parameter:
|
||||
* {@link #getPath()}.
|
||||
*
|
||||
* @return true if the resource was deleted by this method; false if the file could not be deleted because it did not exist
|
||||
* or if {@link Files#deleteIfExists(Path)} throws {@link IOException}.
|
||||
*/
|
||||
|
@ -516,6 +741,7 @@ public abstract class Resource implements ResourceFactory
|
|||
* Equivalent to {@link Files#move(Path, Path, CopyOption...)} with the following parameter:
|
||||
* {@link #getPath()}, {@code dest.getPath()} then returning the result of {@link Files#exists(Path, LinkOption...)}
|
||||
* on the {@code Path} returned by {@code move()}.
|
||||
*
|
||||
* @param dest the destination name for the resource
|
||||
* @return true if the resource was renamed, false if the resource didn't exist or was unable to be renamed.
|
||||
*/
|
||||
|
@ -544,7 +770,7 @@ public abstract class Resource implements ResourceFactory
|
|||
* {@link IOException} was thrown while building the filename list.
|
||||
* Note: The resource names are not URL encoded.
|
||||
*/
|
||||
public List<String> list()
|
||||
public List<String> list() // TODO: should return Path's
|
||||
{
|
||||
try (DirectoryStream<Path> dir = Files.newDirectoryStream(getPath()))
|
||||
{
|
||||
|
@ -593,7 +819,9 @@ public abstract class Resource implements ResourceFactory
|
|||
// where default resolve behavior would be to treat
|
||||
// that like an absolute path.
|
||||
while (subUriPath.startsWith(URIUtil.SLASH))
|
||||
{
|
||||
subUriPath = subUriPath.substring(1);
|
||||
}
|
||||
|
||||
URI uri = getURI();
|
||||
URI resolvedUri;
|
||||
|
@ -660,7 +888,7 @@ public abstract class Resource implements ResourceFactory
|
|||
* @return String of HTML
|
||||
* @throws IOException on failure to generate a list.
|
||||
*/
|
||||
public String getListHTML(String base, boolean parent, String query) throws IOException
|
||||
public String getListHTML(String base, boolean parent, String query) throws IOException // TODO: move to helper class
|
||||
{
|
||||
// This method doesn't check aliases, so it is OK to canonicalize here.
|
||||
base = URIUtil.normalizePath(base);
|
||||
|
@ -1099,96 +1327,72 @@ public abstract class Resource implements ResourceFactory
|
|||
}
|
||||
|
||||
/**
|
||||
* Parse a list of String delimited resources and
|
||||
* return the List of Resources instances it represents.
|
||||
* Split a string of references, that may be split with {@code ,}, or {@code ;}, or {@code |} into URIs.
|
||||
* <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.
|
||||
* Each part of the input string could be path references (unix or windows style), or string URI references.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the result of processing the input segment is a java archive, then its resulting URI will be a mountable URI as `jar:file:...!/`.
|
||||
* </p>
|
||||
*
|
||||
* @param resources the comma {@code ,} or semicolon {@code ;} delimited
|
||||
* String of resource references.
|
||||
* @param globDirs true to return directories in addition to files at the level of the glob
|
||||
* @return the list of resources parsed from input string.
|
||||
* @param str the input string of references
|
||||
* @see #toJarFileUri(URI)
|
||||
*/
|
||||
public static List<Resource> fromList(String resources, boolean globDirs) throws IOException
|
||||
public static List<URI> split(String str)
|
||||
{
|
||||
return fromList(resources, globDirs, Resource::newResource);
|
||||
}
|
||||
List<URI> uris = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 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 resources the comma {@code ,} or semicolon {@code ;} delimited
|
||||
* String of resource references.
|
||||
* @param globDirs true to return directories in addition to files at the level of the glob
|
||||
* @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 resources, boolean globDirs, ResourceFactory resourceFactory) throws IOException
|
||||
{
|
||||
if (StringUtil.isBlank(resources))
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Resource> returnedResources = new ArrayList<>();
|
||||
|
||||
StringTokenizer tokenizer = new StringTokenizer(resources, StringUtil.DEFAULT_DELIMS);
|
||||
StringTokenizer tokenizer = new StringTokenizer(str, ",;|");
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
String token = tokenizer.nextToken().trim();
|
||||
|
||||
// Is this a glob reference?
|
||||
if (token.endsWith("/*") || token.endsWith("\\*"))
|
||||
String reference = tokenizer.nextToken();
|
||||
try
|
||||
{
|
||||
String dir = token.substring(0, token.length() - 2);
|
||||
// Use directory
|
||||
Resource dirResource = resourceFactory.resolve(dir);
|
||||
if (dirResource.exists() && dirResource.isDirectory())
|
||||
// Is this a glob reference?
|
||||
if (reference.endsWith("/*") || reference.endsWith("\\*"))
|
||||
{
|
||||
// To obtain the list of entries
|
||||
List<String> entries = dirResource.list();
|
||||
if (entries != null)
|
||||
String dir = reference.substring(0, reference.length() - 2);
|
||||
Path pathDir = Paths.get(dir);
|
||||
// Use directory
|
||||
if (Files.exists(pathDir) && Files.isDirectory(pathDir))
|
||||
{
|
||||
entries.sort(Comparator.naturalOrder());
|
||||
for (String entry : entries)
|
||||
// To obtain the list of entries
|
||||
try (Stream<Path> listStream = Files.list(pathDir))
|
||||
{
|
||||
try
|
||||
{
|
||||
Resource resource = dirResource.resolve(entry);
|
||||
if (!resource.isDirectory())
|
||||
{
|
||||
returnedResources.add(resource);
|
||||
}
|
||||
else if (globDirs)
|
||||
{
|
||||
returnedResources.add(resource);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LOG.warn("Bad glob [{}] entry: {}", token, entry, ex);
|
||||
}
|
||||
listStream
|
||||
.filter(Files::isRegularFile)
|
||||
.filter(Resource::isArchive)
|
||||
.sorted(Comparator.naturalOrder())
|
||||
.forEach(path -> uris.add(toJarFileUri(path.toUri())));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException("Unable to process directory glob listing: " + reference, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Simple reference
|
||||
URI refUri = toURI(reference);
|
||||
// Is this a Java Archive that can be mounted?
|
||||
URI jarFileUri = toJarFileUri(refUri);
|
||||
if (jarFileUri != null)
|
||||
// add as mountable URI
|
||||
uris.add(jarFileUri);
|
||||
else
|
||||
// add as normal URI
|
||||
uris.add(refUri);
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception e)
|
||||
{
|
||||
// Simple reference, add as-is
|
||||
returnedResources.add(resourceFactory.resolve(token));
|
||||
LOG.warn("Invalid Resource Reference: " + reference);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return returnedResources;
|
||||
return uris;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1196,6 +1400,7 @@ public abstract class Resource implements ResourceFactory
|
|||
* of such mount allowing the use of more {@link Resource}s.
|
||||
* Mounts are {@link Closeable} because they always contain resources (like file descriptors) that must eventually
|
||||
* be released.
|
||||
*
|
||||
* @see #mount(URI)
|
||||
* @see #mountJar(Path)
|
||||
*/
|
||||
|
@ -1203,9 +1408,9 @@ public abstract class Resource implements ResourceFactory
|
|||
{
|
||||
/**
|
||||
* Return the root {@link Resource} made available by this mount.
|
||||
*
|
||||
* @return the resource.
|
||||
* @throws IOException Problem accessing resource
|
||||
*/
|
||||
Resource root() throws IOException;
|
||||
Resource root();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,124 +21,99 @@ import java.net.URI;
|
|||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
||||
/**
|
||||
* A collection of resources (dirs).
|
||||
* Allows webapps to have multiple (static) sources.
|
||||
* 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 resources were constructed.
|
||||
* the order the provided in the constructors
|
||||
*/
|
||||
public class ResourceCollection extends Resource
|
||||
{
|
||||
private List<Resource> _resources;
|
||||
|
||||
/**
|
||||
* Instantiates an empty resource collection.
|
||||
* <p>
|
||||
* This constructor is used when configuring jetty-maven-plugin.
|
||||
*/
|
||||
public ResourceCollection()
|
||||
static class Mount implements Resource.Mount
|
||||
{
|
||||
_resources = new ArrayList<>();
|
||||
private final List<Resource.Mount> _mounts;
|
||||
private final ResourceCollection _root;
|
||||
|
||||
Mount(Collection<Resource> resources, List<Resource.Mount> mounts)
|
||||
{
|
||||
_root = new ResourceCollection(resources);
|
||||
_mounts = mounts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
_mounts.forEach(IO::close);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource root()
|
||||
{
|
||||
return _root;
|
||||
}
|
||||
}
|
||||
|
||||
private final List<Resource> _resources;
|
||||
|
||||
/**
|
||||
* Instantiates a new resource collection.
|
||||
*
|
||||
* @param resources the resources to be added to collection
|
||||
*/
|
||||
public ResourceCollection(Resource... resources)
|
||||
ResourceCollection(Collection<Resource> resources)
|
||||
{
|
||||
this(Arrays.asList(resources));
|
||||
List<Resource> res = new ArrayList<>();
|
||||
gatherUniqueFlatResourceList(res, resources);
|
||||
_resources = Collections.unmodifiableList(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new resource collection.
|
||||
*
|
||||
* @param resources the resources to be added to collection
|
||||
*/
|
||||
public ResourceCollection(Collection<Resource> resources)
|
||||
private static void gatherUniqueFlatResourceList(List<Resource> unique, Collection<Resource> resources)
|
||||
{
|
||||
_resources = new ArrayList<>();
|
||||
if (resources == null || resources.isEmpty())
|
||||
throw new IllegalArgumentException("Empty Resource collection");
|
||||
|
||||
for (Resource r : resources)
|
||||
{
|
||||
if (r == null)
|
||||
{
|
||||
continue;
|
||||
throw new IllegalArgumentException("Null Resource entry encountered");
|
||||
}
|
||||
if (r instanceof ResourceCollection)
|
||||
|
||||
if (r instanceof ResourceCollection resourceCollection)
|
||||
{
|
||||
_resources.addAll(((ResourceCollection)r).getResources());
|
||||
gatherUniqueFlatResourceList(unique, resourceCollection.getResources());
|
||||
}
|
||||
else
|
||||
{
|
||||
assertResourceValid(r);
|
||||
_resources.add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new resource collection.
|
||||
*
|
||||
* @param resources the resource strings to be added to collection
|
||||
*/
|
||||
public ResourceCollection(String[] resources)
|
||||
{
|
||||
_resources = new ArrayList<>();
|
||||
|
||||
if (resources == null || resources.length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
for (String strResource : resources)
|
||||
{
|
||||
if (strResource == null || strResource.length() == 0)
|
||||
if (unique.contains(r))
|
||||
{
|
||||
throw new IllegalArgumentException("empty/null resource path not supported");
|
||||
// skip, already seen
|
||||
continue;
|
||||
}
|
||||
Resource resource = Resource.newResource(strResource);
|
||||
assertResourceValid(resource);
|
||||
_resources.add(resource);
|
||||
}
|
||||
|
||||
if (_resources.isEmpty())
|
||||
{
|
||||
throw new IllegalArgumentException("resources cannot be empty or null");
|
||||
if (!r.exists())
|
||||
{
|
||||
throw new IllegalArgumentException("Does not exist: " + r);
|
||||
}
|
||||
|
||||
if (!r.isDirectory())
|
||||
{
|
||||
throw new IllegalArgumentException("Not a directory: " + r);
|
||||
}
|
||||
unique.add(r);
|
||||
}
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) throws IOException
|
||||
{
|
||||
setResources(csvResources);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,76 +127,8 @@ public class ResourceCollection extends Resource
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the resource collection's resources.
|
||||
* Resolves a path against the resource collection.
|
||||
*
|
||||
* @param res the resources to set
|
||||
*/
|
||||
public void setResources(List<Resource> res)
|
||||
{
|
||||
_resources = new ArrayList<>();
|
||||
if (res.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_resources.addAll(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resource collection's resources.
|
||||
*
|
||||
* @param resources the new resource array
|
||||
*/
|
||||
public void setResources(Resource[] resources)
|
||||
{
|
||||
if (resources == null || resources.length == 0)
|
||||
{
|
||||
_resources = null;
|
||||
return;
|
||||
}
|
||||
|
||||
List<Resource> res = new ArrayList<>();
|
||||
for (Resource resource : resources)
|
||||
{
|
||||
assertResourceValid(resource);
|
||||
res.add(resource);
|
||||
}
|
||||
|
||||
setResources(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resources as string of comma-separated values.
|
||||
* This method should be used when configuring jetty-maven-plugin.
|
||||
*
|
||||
* @param resources the comma-separated string containing
|
||||
* one or more resource strings.
|
||||
* @throws IOException if unable resource declared is not valid
|
||||
* @see Resource#fromList(String, boolean)
|
||||
*/
|
||||
public void setResources(String resources) throws IOException
|
||||
{
|
||||
if (StringUtil.isBlank(resources))
|
||||
{
|
||||
throw new IllegalArgumentException("String is blank");
|
||||
}
|
||||
|
||||
List<Resource> list = Resource.fromList(resources, false);
|
||||
if (list.isEmpty())
|
||||
{
|
||||
throw new IllegalArgumentException("String contains no entries");
|
||||
}
|
||||
List<Resource> ret = new ArrayList<>();
|
||||
for (Resource resource : list)
|
||||
{
|
||||
assertResourceValid(resource);
|
||||
ret.add(resource);
|
||||
}
|
||||
setResources(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a path to the resource collection.
|
||||
* @param subUriPath The path segment to add
|
||||
* @return The resulting resource(s) :
|
||||
* <ul>
|
||||
|
@ -235,8 +142,6 @@ public class ResourceCollection extends Resource
|
|||
@Override
|
||||
public Resource resolve(String subUriPath) throws IOException
|
||||
{
|
||||
assertResourcesSet();
|
||||
|
||||
if (subUriPath == null)
|
||||
{
|
||||
throw new MalformedURLException("null path");
|
||||
|
@ -281,8 +186,6 @@ public class ResourceCollection extends Resource
|
|||
@Override
|
||||
public boolean exists()
|
||||
{
|
||||
assertResourcesSet();
|
||||
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
if (r.exists())
|
||||
|
@ -297,7 +200,6 @@ public class ResourceCollection extends Resource
|
|||
@Override
|
||||
public Path getPath()
|
||||
{
|
||||
assertResourcesSet();
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
Path p = r.getPath();
|
||||
|
@ -310,8 +212,6 @@ public class ResourceCollection extends Resource
|
|||
@Override
|
||||
public InputStream newInputStream() throws IOException
|
||||
{
|
||||
assertResourcesSet();
|
||||
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
if (!r.exists())
|
||||
|
@ -332,8 +232,6 @@ public class ResourceCollection extends Resource
|
|||
@Override
|
||||
public ReadableByteChannel newReadableByteChannel() throws IOException
|
||||
{
|
||||
assertResourcesSet();
|
||||
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
ReadableByteChannel channel = r.newReadableByteChannel();
|
||||
|
@ -348,8 +246,6 @@ public class ResourceCollection extends Resource
|
|||
@Override
|
||||
public String getName()
|
||||
{
|
||||
assertResourcesSet();
|
||||
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
String name = r.getName();
|
||||
|
@ -364,8 +260,6 @@ public class ResourceCollection extends Resource
|
|||
@Override
|
||||
public URI getURI()
|
||||
{
|
||||
assertResourcesSet();
|
||||
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
URI uri = r.getURI();
|
||||
|
@ -380,15 +274,12 @@ public class ResourceCollection extends Resource
|
|||
@Override
|
||||
public boolean isDirectory()
|
||||
{
|
||||
assertResourcesSet();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified()
|
||||
{
|
||||
assertResourcesSet();
|
||||
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
long lm = r.lastModified();
|
||||
|
@ -412,7 +303,6 @@ public class ResourceCollection extends Resource
|
|||
@Override
|
||||
public List<String> list()
|
||||
{
|
||||
assertResourcesSet();
|
||||
HashSet<String> set = new HashSet<>();
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
|
@ -435,8 +325,6 @@ public class ResourceCollection extends Resource
|
|||
@Override
|
||||
public void copyTo(Path destination) throws IOException
|
||||
{
|
||||
assertResourcesSet();
|
||||
|
||||
// Copy in reverse order
|
||||
for (int r = _resources.size(); r-- > 0; )
|
||||
{
|
||||
|
@ -445,17 +333,14 @@ public class ResourceCollection extends Resource
|
|||
}
|
||||
|
||||
/**
|
||||
* @return the list of resources separated by a path separator
|
||||
* @return the list of resources
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
if (_resources.isEmpty())
|
||||
{
|
||||
return "[]";
|
||||
}
|
||||
|
||||
return String.valueOf(_resources);
|
||||
return _resources.stream()
|
||||
.map(Resource::getName)
|
||||
.collect(Collectors.joining(", ", "[", "]"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -464,25 +349,4 @@ public class ResourceCollection extends Resource
|
|||
// TODO could look at implementing the semantic of is this collection a subset of the Resource r?
|
||||
return false;
|
||||
}
|
||||
|
||||
private void assertResourcesSet()
|
||||
{
|
||||
if (_resources == null || _resources.isEmpty())
|
||||
{
|
||||
throw new IllegalStateException("*resources* not set.");
|
||||
}
|
||||
}
|
||||
|
||||
private void assertResourceValid(Resource resource)
|
||||
{
|
||||
if (resource == null)
|
||||
{
|
||||
throw new IllegalStateException("Null resource not supported");
|
||||
}
|
||||
|
||||
if (!resource.exists() || !resource.isDirectory())
|
||||
{
|
||||
throw new IllegalArgumentException(resource + " is not an existing directory.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
import static org.junit.jupiter.api.condition.OS.LINUX;
|
||||
import static org.junit.jupiter.api.condition.OS.MAC;
|
||||
|
@ -190,7 +189,7 @@ public class FileSystemResourceTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testAddPathClass() throws Exception
|
||||
public void testResolvePathClass() throws Exception
|
||||
{
|
||||
Path dir = workDir.getEmptyPathDir();
|
||||
|
||||
|
@ -206,13 +205,13 @@ public class FileSystemResourceTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testAddRootPath() throws Exception
|
||||
public void testResolveRootPath() throws Exception
|
||||
{
|
||||
Path dir = workDir.getEmptyPathDir();
|
||||
Path subdir = dir.resolve("sub");
|
||||
Files.createDirectories(subdir);
|
||||
|
||||
String readableRootDir = findRootDir(dir.getFileSystem());
|
||||
String readableRootDir = findAnyDirectoryOffRoot(dir.getFileSystem());
|
||||
assumeTrue(readableRootDir != null, "Readable Root Dir found");
|
||||
|
||||
Resource base = Resource.newResource(dir);
|
||||
|
@ -236,7 +235,7 @@ public class FileSystemResourceTest
|
|||
{
|
||||
Path dir = workDir.getEmptyPathDir();
|
||||
|
||||
String readableRootDir = findRootDir(dir.getFileSystem());
|
||||
String readableRootDir = findAnyDirectoryOffRoot(dir.getFileSystem());
|
||||
assumeTrue(readableRootDir != null, "Readable Root Dir found");
|
||||
|
||||
Path subdir = dir.resolve("sub");
|
||||
|
@ -267,9 +266,14 @@ public class FileSystemResourceTest
|
|||
assertThat("Ref O1 contents", toString(refO1), is("hi o-with-two-dots"));
|
||||
}
|
||||
|
||||
private String findRootDir(FileSystem fs)
|
||||
/**
|
||||
* Best effort discovery a directory off the provided FileSystem.
|
||||
* @param fs the provided FileSystem.
|
||||
* @return a directory off the root FileSystem.
|
||||
*/
|
||||
private String findAnyDirectoryOffRoot(FileSystem fs)
|
||||
{
|
||||
// look for a directory off of a root path
|
||||
// look for anything that's a directory off of any root paths of the provided FileSystem
|
||||
for (Path rootDir : fs.getRootDirectories())
|
||||
{
|
||||
try (DirectoryStream<Path> dir = Files.newDirectoryStream(rootDir))
|
||||
|
@ -284,7 +288,9 @@ public class FileSystemResourceTest
|
|||
}
|
||||
catch (Exception ignored)
|
||||
{
|
||||
// FIXME why ignoring exceptions??
|
||||
// Don't care if there's an error, we'll just try the next possible root directory.
|
||||
// if no directories are found, then that means the users test environment is
|
||||
// super odd, and we cannot continue these tests anyway, and are skipped with an assume().
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1055,7 +1061,7 @@ public class FileSystemResourceTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testAddPathWindowsSlash() throws Exception
|
||||
public void testResolveWindowsSlash() throws Exception
|
||||
{
|
||||
Path dir = workDir.getEmptyPathDir();
|
||||
Files.createDirectories(dir);
|
||||
|
@ -1098,7 +1104,7 @@ public class FileSystemResourceTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testAddPathWindowsExtensionLess() throws Exception
|
||||
public void testResolveWindowsExtensionLess() throws Exception
|
||||
{
|
||||
Path dir = workDir.getEmptyPathDir();
|
||||
Files.createDirectories(dir);
|
||||
|
@ -1140,7 +1146,7 @@ public class FileSystemResourceTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testAddInitialSlash() throws Exception
|
||||
public void testResolveInitialSlash() throws Exception
|
||||
{
|
||||
Path dir = workDir.getEmptyPathDir();
|
||||
Files.createDirectories(dir);
|
||||
|
@ -1170,7 +1176,7 @@ public class FileSystemResourceTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testAddInitialDoubleSlash() throws Exception
|
||||
public void testResolveInitialDoubleSlash() throws Exception
|
||||
{
|
||||
Path dir = workDir.getEmptyPathDir();
|
||||
Files.createDirectories(dir);
|
||||
|
@ -1200,7 +1206,7 @@ public class FileSystemResourceTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testAddDoubleSlash() throws Exception
|
||||
public void testResolveDoubleSlash() throws Exception
|
||||
{
|
||||
Path dir = workDir.getEmptyPathDir();
|
||||
Files.createDirectories(dir);
|
||||
|
|
|
@ -124,24 +124,26 @@ public class JarResourceTest
|
|||
Path testZip = Files.copy(originalTestZip, tempDir.resolve("test.zip"));
|
||||
String s = "jar:" + testZip.toUri().toASCIIString() + "!/subdir/";
|
||||
URI uri = URI.create(s);
|
||||
Resource.Mount mount = Resource.mount(uri);
|
||||
Resource resource = mount.root();
|
||||
assertTrue(resource.exists());
|
||||
try (Resource.Mount mount = Resource.mount(uri))
|
||||
{
|
||||
Resource resource = mount.root();
|
||||
assertTrue(resource.exists());
|
||||
|
||||
String dump = FileSystemPool.INSTANCE.dump();
|
||||
assertThat(dump, containsString("FileSystemPool"));
|
||||
assertThat(dump, containsString("mounts size=1"));
|
||||
assertThat(dump, containsString("Mount uri=jar:file:/"));
|
||||
assertThat(dump, containsString("test.zip!/subdir"));
|
||||
String dump = FileSystemPool.INSTANCE.dump();
|
||||
assertThat(dump, containsString("FileSystemPool"));
|
||||
assertThat(dump, containsString("mounts size=1"));
|
||||
assertThat(dump, containsString("Mount[uri=jar:file:/"));
|
||||
assertThat(dump, containsString("test.zip!/subdir"));
|
||||
|
||||
Files.delete(testZip);
|
||||
FileSystemPool.INSTANCE.sweep();
|
||||
Files.delete(testZip);
|
||||
FileSystemPool.INSTANCE.sweep();
|
||||
|
||||
dump = FileSystemPool.INSTANCE.dump();
|
||||
assertThat(dump, containsString("FileSystemPool"));
|
||||
assertThat(dump, containsString("mounts size=0"));
|
||||
dump = FileSystemPool.INSTANCE.dump();
|
||||
assertThat(dump, containsString("FileSystemPool"));
|
||||
assertThat(dump, containsString("mounts size=0"));
|
||||
|
||||
assertThrows(ClosedFileSystemException.class, resource::exists);
|
||||
assertThrows(ClosedFileSystemException.class, resource::exists);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -13,17 +13,23 @@
|
|||
|
||||
package org.eclipse.jetty.util.resource;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
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 org.eclipse.jetty.toolchain.test.FS;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
|
@ -32,236 +38,196 @@ import static org.hamcrest.Matchers.contains;
|
|||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(WorkDirExtension.class)
|
||||
public class ResourceCollectionTest
|
||||
{
|
||||
public WorkDir workdir;
|
||||
|
||||
@Test
|
||||
public void testUnsetCollectionThrowsISE()
|
||||
{
|
||||
ResourceCollection coll = new ResourceCollection();
|
||||
|
||||
assertThrowIllegalStateException(coll);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyResourceArrayThrowsISE()
|
||||
{
|
||||
ResourceCollection coll = new ResourceCollection(new Resource[0]);
|
||||
|
||||
assertThrowIllegalStateException(coll);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceArrayWithNullThrowsISE()
|
||||
{
|
||||
ResourceCollection coll = new ResourceCollection(new Resource[]{null});
|
||||
|
||||
assertThrowIllegalStateException(coll);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyStringArrayThrowsISE()
|
||||
{
|
||||
ResourceCollection coll = new ResourceCollection(new String[0]);
|
||||
|
||||
assertThrowIllegalStateException(coll);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringArrayWithNullThrowsIAE()
|
||||
{
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new ResourceCollection(new String[]{null}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullCsvThrowsIAE()
|
||||
{
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
{
|
||||
String csv = null;
|
||||
new ResourceCollection(csv); // throws IAE
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyCsvThrowsIAE()
|
||||
{
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
{
|
||||
String csv = "";
|
||||
new ResourceCollection(csv); // throws IAE
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlankCsvThrowsIAE()
|
||||
{
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
{
|
||||
String csv = ",,,,";
|
||||
new ResourceCollection(csv); // throws IAE
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetResourceArrayNullThrowsISE()
|
||||
{
|
||||
// Create a ResourceCollection with one valid entry
|
||||
Path path = MavenTestingUtils.getTargetPath();
|
||||
Resource resource = Resource.newResource(path);
|
||||
ResourceCollection coll = new ResourceCollection(resource);
|
||||
|
||||
// Reset collection to invalid state
|
||||
coll.setResources((Resource[])null);
|
||||
|
||||
assertThrowIllegalStateException(coll);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetResourceEmptyThrowsISE()
|
||||
{
|
||||
// Create a ResourceCollection with one valid entry
|
||||
Path path = MavenTestingUtils.getTargetPath();
|
||||
Resource resource = Resource.newResource(path);
|
||||
ResourceCollection coll = new ResourceCollection(resource);
|
||||
|
||||
// Reset collection to invalid state
|
||||
coll.setResources(new Resource[0]);
|
||||
|
||||
assertThrowIllegalStateException(coll);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetResourceAllNullsThrowsISE()
|
||||
{
|
||||
// Create a ResourceCollection with one valid entry
|
||||
Path path = MavenTestingUtils.getTargetPath();
|
||||
Resource resource = Resource.newResource(path);
|
||||
ResourceCollection coll = new ResourceCollection(resource);
|
||||
|
||||
// Reset collection to invalid state
|
||||
assertThrows(IllegalStateException.class, () -> coll.setResources(new Resource[]{null, null, null}));
|
||||
|
||||
// Ensure not modified.
|
||||
assertThat(coll.getResources().size(), is(1));
|
||||
}
|
||||
|
||||
private void assertThrowIllegalStateException(ResourceCollection coll)
|
||||
{
|
||||
assertThrows(IllegalStateException.class, () -> coll.resolve("foo"));
|
||||
assertThrows(IllegalStateException.class, coll::exists);
|
||||
assertThrows(IllegalStateException.class, coll::getPath);
|
||||
assertThrows(IllegalStateException.class, coll::newInputStream);
|
||||
assertThrows(IllegalStateException.class, coll::newReadableByteChannel);
|
||||
assertThrows(IllegalStateException.class, coll::getURI);
|
||||
assertThrows(IllegalStateException.class, coll::getName);
|
||||
assertThrows(IllegalStateException.class, coll::isDirectory);
|
||||
assertThrows(IllegalStateException.class, coll::lastModified);
|
||||
assertThrows(IllegalStateException.class, coll::list);
|
||||
assertThrows(IllegalStateException.class, () ->
|
||||
{
|
||||
Path destPath = workdir.getPathFile("bar");
|
||||
coll.copyTo(destPath);
|
||||
});
|
||||
}
|
||||
public WorkDir workDir;
|
||||
|
||||
@Test
|
||||
public void testList() throws Exception
|
||||
{
|
||||
ResourceCollection rc1 = new ResourceCollection(
|
||||
Resource.newResource("src/test/resources/org/eclipse/jetty/util/resource/one/"),
|
||||
Resource.newResource("src/test/resources/org/eclipse/jetty/util/resource/two/"),
|
||||
Resource.newResource("src/test/resources/org/eclipse/jetty/util/resource/three/"));
|
||||
Path one = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/one");
|
||||
Path two = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/two");
|
||||
Path three = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/three");
|
||||
|
||||
assertThat(rc1.list(), contains("1.txt", "2.txt", "3.txt", "dir/"));
|
||||
assertThat(rc1.resolve("dir").list(), contains("1.txt", "2.txt", "3.txt"));
|
||||
assertThat(rc1.resolve("unknown").list(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleSources1() throws Exception
|
||||
{
|
||||
ResourceCollection rc1 = new ResourceCollection(new String[]{
|
||||
"src/test/resources/org/eclipse/jetty/util/resource/one/",
|
||||
"src/test/resources/org/eclipse/jetty/util/resource/two/",
|
||||
"src/test/resources/org/eclipse/jetty/util/resource/three/"
|
||||
});
|
||||
assertEquals(getContent(rc1, "1.txt"), "1 - one");
|
||||
assertEquals(getContent(rc1, "2.txt"), "2 - two");
|
||||
assertEquals(getContent(rc1, "3.txt"), "3 - three");
|
||||
|
||||
ResourceCollection rc2 = new ResourceCollection(
|
||||
"src/test/resources/org/eclipse/jetty/util/resource/one/," +
|
||||
"src/test/resources/org/eclipse/jetty/util/resource/two/," +
|
||||
"src/test/resources/org/eclipse/jetty/util/resource/three/"
|
||||
ResourceCollection rc = Resource.of(
|
||||
Resource.newResource(one),
|
||||
Resource.newResource(two),
|
||||
Resource.newResource(three)
|
||||
);
|
||||
assertEquals(getContent(rc2, "1.txt"), "1 - one");
|
||||
assertEquals(getContent(rc2, "2.txt"), "2 - two");
|
||||
assertEquals(getContent(rc2, "3.txt"), "3 - three");
|
||||
}
|
||||
assertThat(rc.list(), contains("1.txt", "2.txt", "3.txt", "dir/"));
|
||||
assertThat(rc.resolve("dir").list(), contains("1.txt", "2.txt", "3.txt"));
|
||||
assertThat(rc.resolve("unknown").list(), nullValue());
|
||||
|
||||
@Test
|
||||
public void testMergedDir() throws Exception
|
||||
{
|
||||
ResourceCollection rc = new ResourceCollection(new String[]{
|
||||
"src/test/resources/org/eclipse/jetty/util/resource/one/",
|
||||
"src/test/resources/org/eclipse/jetty/util/resource/two/",
|
||||
"src/test/resources/org/eclipse/jetty/util/resource/three/"
|
||||
});
|
||||
|
||||
Resource r = rc.resolve("dir");
|
||||
assertTrue(r instanceof ResourceCollection);
|
||||
rc = (ResourceCollection)r;
|
||||
assertEquals(getContent(rc, "1.txt"), "1 - one");
|
||||
assertEquals(getContent(rc, "2.txt"), "2 - two");
|
||||
assertEquals(getContent(rc, "3.txt"), "3 - three");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergedDir() throws Exception
|
||||
{
|
||||
Path one = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/one");
|
||||
Path two = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/two");
|
||||
Path three = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/three");
|
||||
|
||||
ResourceCollection rc = Resource.of(
|
||||
Resource.newResource(one),
|
||||
Resource.newResource(two),
|
||||
Resource.newResource(three)
|
||||
);
|
||||
|
||||
// This should return a ResourceCollection with 3 `/dir/` sub-directories.
|
||||
Resource r = rc.resolve("dir");
|
||||
assertTrue(r instanceof ResourceCollection);
|
||||
rc = (ResourceCollection)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)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyTo() throws Exception
|
||||
{
|
||||
ResourceCollection rc = new ResourceCollection(new String[]{
|
||||
"src/test/resources/org/eclipse/jetty/util/resource/one/",
|
||||
"src/test/resources/org/eclipse/jetty/util/resource/two/",
|
||||
"src/test/resources/org/eclipse/jetty/util/resource/three/"
|
||||
});
|
||||
Path one = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/one");
|
||||
Path two = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/two");
|
||||
Path three = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/three");
|
||||
|
||||
File dest = MavenTestingUtils.getTargetTestingDir("copyto");
|
||||
FS.ensureDirExists(dest);
|
||||
rc.copyTo(dest.toPath());
|
||||
ResourceCollection rc = Resource.of(
|
||||
Resource.newResource(one),
|
||||
Resource.newResource(two),
|
||||
Resource.newResource(three)
|
||||
);
|
||||
Path destDir = workDir.getEmptyPathDir();
|
||||
rc.copyTo(destDir);
|
||||
|
||||
Resource r = Resource.newResource(dest.toURI());
|
||||
Resource r = Resource.newResource(destDir);
|
||||
assertEquals(getContent(r, "1.txt"), "1 - one");
|
||||
assertEquals(getContent(r, "2.txt"), "2 - two");
|
||||
assertEquals(getContent(r, "3.txt"), "3 - three");
|
||||
r = r.resolve("dir");
|
||||
assertEquals(getContent(r, "1.txt"), "1 - one");
|
||||
assertEquals(getContent(r, "2.txt"), "2 - two");
|
||||
assertEquals(getContent(r, "3.txt"), "3 - three");
|
||||
assertEquals(getContent(r, "1.txt"), "1 - one (in dir)");
|
||||
assertEquals(getContent(r, "2.txt"), "2 - two (in dir)");
|
||||
assertEquals(getContent(r, "3.txt"), "3 - three (in dir)");
|
||||
}
|
||||
|
||||
IO.delete(dest);
|
||||
@Test
|
||||
public void testResourceCollectionInResourceCollection()
|
||||
{
|
||||
Path one = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/one");
|
||||
Path two = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/two");
|
||||
Path three = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/three");
|
||||
Path twoDir = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/two/dir");
|
||||
|
||||
ResourceCollection rc1 = Resource.of(
|
||||
List.of(
|
||||
Resource.newResource(one),
|
||||
Resource.newResource(two),
|
||||
Resource.newResource(three)
|
||||
)
|
||||
);
|
||||
|
||||
ResourceCollection rc2 = Resource.of(
|
||||
List.of(
|
||||
// the original ResourceCollection
|
||||
rc1,
|
||||
// a duplicate entry
|
||||
Resource.newResource(two),
|
||||
// a new entry
|
||||
Resource.newResource(twoDir)
|
||||
)
|
||||
);
|
||||
|
||||
URI[] expected = new URI[] {
|
||||
one.toUri(),
|
||||
two.toUri(),
|
||||
three.toUri(),
|
||||
twoDir.toUri()
|
||||
};
|
||||
|
||||
List<URI> actual = new ArrayList<>();
|
||||
for (Resource res: rc2.getResources())
|
||||
{
|
||||
actual.add(res.getURI());
|
||||
}
|
||||
assertThat(actual, contains(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserSpaceConfigurationNoGlob() throws Exception
|
||||
{
|
||||
Path base = workDir.getEmptyPathDir();
|
||||
Path dir = base.resolve("dir");
|
||||
FS.ensureDirExists(dir);
|
||||
Path foo = dir.resolve("foo");
|
||||
FS.ensureDirExists(foo);
|
||||
Path bar = dir.resolve("bar");
|
||||
FS.ensureDirExists(bar);
|
||||
Path content = foo.resolve("test.txt");
|
||||
Files.writeString(content, "Test");
|
||||
|
||||
// This represents the user-space raw configuration
|
||||
String config = String.format("%s,%s,%s", dir, foo, bar);
|
||||
|
||||
// To use this, we need to split it (and optionally honor globs)
|
||||
List<URI> uris = Resource.split(config);
|
||||
// Now let's create a ResourceCollection from this list of URIs
|
||||
// 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
|
||||
try (Resource.Mount mount = Resource.mountCollection(uris))
|
||||
{
|
||||
ResourceCollection rc = (ResourceCollection)mount.root();
|
||||
assertThat(getContent(rc, "test.txt"), is("Test"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserSpaceConfigurationWithGlob() throws Exception
|
||||
{
|
||||
Path base = workDir.getEmptyPathDir();
|
||||
Path dir = base.resolve("dir");
|
||||
FS.ensureDirExists(dir);
|
||||
Path foo = dir.resolve("foo");
|
||||
FS.ensureDirExists(foo);
|
||||
Path bar = dir.resolve("bar");
|
||||
FS.ensureDirExists(bar);
|
||||
createJar(bar.resolve("lib-foo.jar"), "/test.txt", "Test inside lib-foo.jar");
|
||||
createJar(bar.resolve("lib-zed.jar"), "/testZed.txt", "TestZed inside lib-zed.jar");
|
||||
|
||||
// This represents the user-space raw configuration with a glob
|
||||
String config = String.format("%s;%s;%s%s*", dir, foo, bar, File.separator);
|
||||
|
||||
// To use this, we need to split it (and optionally honor globs)
|
||||
List<URI> uris = Resource.split(config);
|
||||
// Now let's create a ResourceCollection from this list of URIs
|
||||
// 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
|
||||
try (Resource.Mount mount = Resource.mountCollection(uris))
|
||||
{
|
||||
ResourceCollection rc = (ResourceCollection)mount.root();
|
||||
assertThat(getContent(rc, "test.txt"), is("Test inside lib-foo.jar"));
|
||||
assertThat(getContent(rc, "testZed.txt"), is("TestZed inside lib-zed.jar"));
|
||||
}
|
||||
}
|
||||
|
||||
private void createJar(Path outputJar, String entryName, String entryContents) throws IOException
|
||||
{
|
||||
Map<String, String> env = new HashMap<>();
|
||||
env.put("create", "true");
|
||||
|
||||
URI uri = URI.create("jar:" + outputJar.toUri().toASCIIString());
|
||||
try (FileSystem zipfs = FileSystems.newFileSystem(uri, env))
|
||||
{
|
||||
Files.writeString(zipfs.getPath(entryName), entryContents, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
static String getContent(Resource r, String path) throws Exception
|
||||
{
|
||||
Resource resource = r.resolve(path);
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
try (InputStream in = resource.newInputStream();
|
||||
InputStreamReader reader = new InputStreamReader(in);
|
||||
BufferedReader br = new BufferedReader(reader))
|
||||
{
|
||||
String line;
|
||||
while ((line = br.readLine()) != null)
|
||||
{
|
||||
buffer.append(line);
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
return Files.readString(r.resolve(path).getPath(), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ import java.util.stream.Stream;
|
|||
|
||||
import org.eclipse.jetty.toolchain.test.FS;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
|
@ -35,19 +37,25 @@ import org.junit.jupiter.api.Disabled;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledOnOs;
|
||||
import org.junit.jupiter.api.condition.OS;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
@ExtendWith(WorkDirExtension.class)
|
||||
public class ResourceTest
|
||||
{
|
||||
private static final boolean DIR = true;
|
||||
|
@ -251,6 +259,8 @@ public class ResourceTest
|
|||
return cases.stream();
|
||||
}
|
||||
|
||||
public WorkDir workDir;
|
||||
|
||||
@AfterAll
|
||||
public static void tearDown()
|
||||
{
|
||||
|
@ -393,4 +403,210 @@ public class ResourceTest
|
|||
assertNotNull(same);
|
||||
assertTrue(same.isAlias());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJarReferenceAsURINotYetMounted() throws Exception
|
||||
{
|
||||
Path jar = MavenTestingUtils.getTestResourcePathFile("example.jar");
|
||||
URI jarFileUri = Resource.toJarFileUri(jar.toUri());
|
||||
assertNotNull(jarFileUri);
|
||||
assertThrows(IllegalStateException.class, () -> Resource.newResource(jarFileUri));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"file:/home/user/.m2/repository/com/company/1.0/company-1.0.jar",
|
||||
"jar:file:/home/user/.m2/repository/com/company/1.0/company-1.0.jar!/",
|
||||
"jar:file:/home/user/.m2/repository/com/company/1.0/company-1.0.jar",
|
||||
"file:/home/user/install/jetty-home-12.0.0.zip",
|
||||
"file:/opt/websites/webapps/company.war",
|
||||
"jar:file:/home/user/.m2/repository/jakarta/servlet/jakarta.servlet-api/6.0.0/jakarta.servlet-api-6.0.0.jar!/META-INF/resources"
|
||||
})
|
||||
public void testIsArchiveUriTrue(String rawUri)
|
||||
{
|
||||
assertTrue(Resource.isArchive(URI.create(rawUri)), "Should be detected as a JAR URI: " + rawUri);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"jar:file:/home/user/project/with.jar/in/path/name",
|
||||
"file:/home/user/project/directory/",
|
||||
"file:/home/user/hello.ear",
|
||||
"/home/user/hello.jar",
|
||||
"/home/user/app.war"
|
||||
})
|
||||
public void testIsArchiveUriFalse(String rawUri)
|
||||
{
|
||||
assertFalse(Resource.isArchive(URI.create(rawUri)), "Should be detected as a JAR URI: " + rawUri);
|
||||
}
|
||||
|
||||
public static Stream<Arguments> jarFileUriCases()
|
||||
{
|
||||
List<Arguments> cases = new ArrayList<>();
|
||||
|
||||
String expected = "jar:file:/path/company-1.0.jar!/";
|
||||
cases.add(Arguments.of("file:/path/company-1.0.jar", expected));
|
||||
cases.add(Arguments.of("jar:file:/path/company-1.0.jar", expected));
|
||||
cases.add(Arguments.of("jar:file:/path/company-1.0.jar!/", expected));
|
||||
cases.add(Arguments.of("jar:file:/path/company-1.0.jar!/META-INF/services", expected + "META-INF/services"));
|
||||
|
||||
expected = "jar:file:/opt/jetty/webapps/app.war!/";
|
||||
cases.add(Arguments.of("file:/opt/jetty/webapps/app.war", expected));
|
||||
cases.add(Arguments.of("jar:file:/opt/jetty/webapps/app.war", expected));
|
||||
cases.add(Arguments.of("jar:file:/opt/jetty/webapps/app.war!/", expected));
|
||||
cases.add(Arguments.of("jar:file:/opt/jetty/webapps/app.war!/WEB-INF/classes", expected + "WEB-INF/classes"));
|
||||
|
||||
return cases.stream();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("jarFileUriCases")
|
||||
public void testToJarFileUri(String inputRawUri, String expectedRawUri)
|
||||
{
|
||||
URI actual = Resource.toJarFileUri(URI.create(inputRawUri));
|
||||
assertNotNull(actual);
|
||||
assertThat(actual.toASCIIString(), is(expectedRawUri));
|
||||
}
|
||||
|
||||
public static Stream<Arguments> unwrapContainerCases()
|
||||
{
|
||||
return Stream.of(
|
||||
Arguments.of("/path/to/foo.jar", "file:///path/to/foo.jar"),
|
||||
Arguments.of("/path/to/bogus.txt", "file:///path/to/bogus.txt"),
|
||||
Arguments.of("file:///path/to/zed.jar", "file:///path/to/zed.jar"),
|
||||
Arguments.of("jar:file:///path/to/bar.jar!/internal.txt", "file:///path/to/bar.jar")
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("unwrapContainerCases")
|
||||
public void testUnwrapContainer(String inputRawUri, String expected)
|
||||
{
|
||||
URI input = Resource.toURI(inputRawUri);
|
||||
URI actual = Resource.unwrapContainer(input);
|
||||
assertThat(actual.toASCIIString(), is(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitSingleJar()
|
||||
{
|
||||
// Bad java file.uri syntax
|
||||
String input = "file:/home/user/lib/acme.jar";
|
||||
List<URI> uris = Resource.split(input);
|
||||
String expected = String.format("jar:%s!/", input);
|
||||
assertThat(uris.get(0).toString(), is(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitSinglePath()
|
||||
{
|
||||
String input = "/home/user/lib/acme.jar";
|
||||
List<URI> uris = Resource.split(input);
|
||||
String expected = String.format("jar:file://%s!/", input);
|
||||
assertThat(uris.get(0).toString(), is(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitOnComma()
|
||||
{
|
||||
Path base = workDir.getEmptyPathDir();
|
||||
Path dir = base.resolve("dir");
|
||||
FS.ensureDirExists(dir);
|
||||
Path foo = dir.resolve("foo");
|
||||
FS.ensureDirExists(foo);
|
||||
Path bar = dir.resolve("bar");
|
||||
FS.ensureDirExists(bar);
|
||||
|
||||
// This represents the user-space raw configuration
|
||||
String config = String.format("%s,%s,%s", dir, foo, bar);
|
||||
|
||||
// Split using commas
|
||||
List<URI> uris = Resource.split(config);
|
||||
|
||||
URI[] expected = new URI[] {
|
||||
dir.toUri(),
|
||||
foo.toUri(),
|
||||
bar.toUri()
|
||||
};
|
||||
assertThat(uris, contains(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitOnPipe()
|
||||
{
|
||||
Path base = workDir.getEmptyPathDir();
|
||||
Path dir = base.resolve("dir");
|
||||
FS.ensureDirExists(dir);
|
||||
Path foo = dir.resolve("foo");
|
||||
FS.ensureDirExists(foo);
|
||||
Path bar = dir.resolve("bar");
|
||||
FS.ensureDirExists(bar);
|
||||
|
||||
// This represents the user-space raw configuration
|
||||
String config = String.format("%s|%s|%s", dir, foo, bar);
|
||||
|
||||
// Split using commas
|
||||
List<URI> uris = Resource.split(config);
|
||||
|
||||
URI[] expected = new URI[] {
|
||||
dir.toUri(),
|
||||
foo.toUri(),
|
||||
bar.toUri()
|
||||
};
|
||||
assertThat(uris, contains(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitOnSemicolon()
|
||||
{
|
||||
Path base = workDir.getEmptyPathDir();
|
||||
Path dir = base.resolve("dir");
|
||||
FS.ensureDirExists(dir);
|
||||
Path foo = dir.resolve("foo");
|
||||
FS.ensureDirExists(foo);
|
||||
Path bar = dir.resolve("bar");
|
||||
FS.ensureDirExists(bar);
|
||||
|
||||
// This represents the user-space raw configuration
|
||||
String config = String.format("%s;%s;%s", dir, foo, bar);
|
||||
|
||||
// Split using commas
|
||||
List<URI> uris = Resource.split(config);
|
||||
|
||||
URI[] expected = new URI[] {
|
||||
dir.toUri(),
|
||||
foo.toUri(),
|
||||
bar.toUri()
|
||||
};
|
||||
assertThat(uris, contains(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitOnPipeWithGlob() throws IOException
|
||||
{
|
||||
Path base = workDir.getEmptyPathDir();
|
||||
Path dir = base.resolve("dir");
|
||||
FS.ensureDirExists(dir);
|
||||
Path foo = dir.resolve("foo");
|
||||
FS.ensureDirExists(foo);
|
||||
Path bar = dir.resolve("bar");
|
||||
FS.ensureDirExists(bar);
|
||||
FS.touch(bar.resolve("lib-foo.jar"));
|
||||
FS.touch(bar.resolve("lib-zed.zip"));
|
||||
|
||||
// This represents the user-space raw configuration with a glob
|
||||
String config = String.format("%s;%s;%s%s*", dir, foo, bar, File.separator);
|
||||
|
||||
// Split using commas
|
||||
List<URI> uris = Resource.split(config);
|
||||
|
||||
URI[] expected = new URI[] {
|
||||
dir.toUri(),
|
||||
foo.toUri(),
|
||||
// Should see the two archives as `jar:file:` URI entries
|
||||
Resource.toJarFileUri(bar.resolve("lib-foo.jar").toUri()),
|
||||
Resource.toJarFileUri(bar.resolve("lib-zed.zip").toUri())
|
||||
};
|
||||
assertThat(uris, contains(expected));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
1 - one
|
||||
1 - one (in dir)
|
|
@ -1 +1 @@
|
|||
3 - three
|
||||
3 - three (in dir)
|
|
@ -1 +1 @@
|
|||
2 - two
|
||||
2 - two (in dir)
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
package org.eclipse.jetty.ee10.annotations;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.eclipse.jetty.ee10.plus.annotation.LifeCycleCallbackCollection;
|
||||
|
@ -21,10 +22,13 @@ import org.eclipse.jetty.ee10.servlet.Source;
|
|||
import org.eclipse.jetty.ee10.webapp.MetaData;
|
||||
import org.eclipse.jetty.ee10.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.ee10.webapp.WebDescriptor;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
|
||||
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.xml.XmlParser;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
@ -32,13 +36,16 @@ import static org.junit.jupiter.api.Assertions.assertNull;
|
|||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(WorkDirExtension.class)
|
||||
public class TestAnnotationDecorator
|
||||
{
|
||||
public WorkDir workDir;
|
||||
|
||||
public class TestWebDescriptor extends WebDescriptor
|
||||
{
|
||||
public TestWebDescriptor(MetaData.Complete metadata)
|
||||
public TestWebDescriptor(Resource resource, MetaData.Complete metadata)
|
||||
{
|
||||
super(Resource.newResource(Path.of(".")));
|
||||
super(resource);
|
||||
_metaDataComplete = metadata;
|
||||
}
|
||||
|
||||
|
@ -78,6 +85,10 @@ public class TestAnnotationDecorator
|
|||
@Test
|
||||
public void testAnnotationDecorator() throws Exception
|
||||
{
|
||||
Path dummyXml = workDir.getEmptyPathDir().resolve("dummy.xml");
|
||||
Files.createFile(dummyXml);
|
||||
Resource dummyXmlResource = Resource.newResource(dummyXml);
|
||||
|
||||
assertThrows(NullPointerException.class, () ->
|
||||
{
|
||||
new AnnotationDecorator(null);
|
||||
|
@ -96,7 +107,7 @@ public class TestAnnotationDecorator
|
|||
context.removeAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
|
||||
|
||||
//test with BaseHolder metadata, should not introspect with metdata-complete==true
|
||||
context.getMetaData().setWebDescriptor(new TestWebDescriptor(MetaData.Complete.True));
|
||||
context.getMetaData().setWebDescriptor(new TestWebDescriptor(dummyXmlResource, MetaData.Complete.True));
|
||||
assertTrue(context.getMetaData().isMetaDataComplete());
|
||||
ServletHolder holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, ""));
|
||||
holder.setHeldClass(ServletE.class);
|
||||
|
@ -112,7 +123,7 @@ public class TestAnnotationDecorator
|
|||
context.removeAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
|
||||
|
||||
//test with BaseHolder metadata, should introspect with metadata-complete==false
|
||||
context.getMetaData().setWebDescriptor(new TestWebDescriptor(MetaData.Complete.False));
|
||||
context.getMetaData().setWebDescriptor(new TestWebDescriptor(dummyXmlResource, MetaData.Complete.False));
|
||||
DecoratedObjectFactory.associateInfo(holder);
|
||||
decorator = new AnnotationDecorator(context);
|
||||
decorator.decorate(servlet);
|
||||
|
|
|
@ -13,22 +13,32 @@
|
|||
|
||||
package org.eclipse.jetty.ee10.annotations;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.eclipse.jetty.ee10.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.ee10.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.ee10.webapp.WebDescriptor;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@ExtendWith(WorkDirExtension.class)
|
||||
public class TestRunAsAnnotation
|
||||
{
|
||||
public WorkDir workDir;
|
||||
|
||||
@Test
|
||||
public void testRunAsAnnotation() throws Exception
|
||||
{
|
||||
Path dummyXml = workDir.getEmptyPathDir().resolve("dummy.xml");
|
||||
Files.createFile(dummyXml);
|
||||
Resource dummyXmlResource = Resource.newResource(dummyXml);
|
||||
|
||||
WebAppContext wac = new WebAppContext();
|
||||
|
||||
//pre-add a servlet but not by descriptor
|
||||
|
@ -44,8 +54,7 @@ public class TestRunAsAnnotation
|
|||
holder2.setHeldClass(ServletC.class);
|
||||
holder2.setInitOrder(1);
|
||||
wac.getServletHandler().addServletWithMapping(holder2, "/foo2/*");
|
||||
Resource fakeXml = Resource.newResource(new File(MavenTestingUtils.getTargetTestingDir("run-as"), "fake.xml").toPath());
|
||||
wac.getMetaData().setOrigin(holder2.getName() + ".servlet.run-as", new WebDescriptor(fakeXml));
|
||||
wac.getMetaData().setOrigin(holder2.getName() + ".servlet.run-as", new WebDescriptor(dummyXmlResource));
|
||||
|
||||
AnnotationIntrospector parser = new AnnotationIntrospector(wac);
|
||||
RunAsAnnotationHandler handler = new RunAsAnnotationHandler(wac);
|
||||
|
|
|
@ -56,7 +56,7 @@ public class ProxyWebAppTest
|
|||
// So, open up server classes here, for purposes of this testcase.
|
||||
webapp.getServerClassMatcher().add("-org.eclipse.jetty.ee10.proxy.");
|
||||
webapp.setWar(MavenTestingUtils.getProjectDirPath("src/main/webapp").toString());
|
||||
webapp.setExtraClasspath(MavenTestingUtils.getTargetPath().resolve("classes").toString());
|
||||
webapp.setExtraClasspath(MavenTestingUtils.getTargetPath().resolve("test-classes").toString());
|
||||
server.setHandler(webapp);
|
||||
|
||||
server.start();
|
||||
|
|
|
@ -176,6 +176,8 @@ public abstract class AbstractUnassembledWebAppMojo extends AbstractWebAppMojo
|
|||
//Still don't have a web.xml file: try the resourceBase of the webapp, if it is set
|
||||
if (webApp.getDescriptor() == null && webApp.getResourceBase() != null)
|
||||
{
|
||||
// TODO: should never return from WEB-INF/lib/foo.jar!/WEB-INF/web.xml
|
||||
// TODO: should also never return from a META-INF/versions/#/WEB-INF/web.xml location
|
||||
Resource r = webApp.getResourceBase().resolve("WEB-INF/web.xml");
|
||||
if (r.exists() && !r.isDirectory())
|
||||
{
|
||||
|
@ -186,6 +188,7 @@ public abstract class AbstractUnassembledWebAppMojo extends AbstractWebAppMojo
|
|||
//Still don't have a web.xml file: finally try the configured static resource directory if there is one
|
||||
if (webApp.getDescriptor() == null && (webAppSourceDirectory != null))
|
||||
{
|
||||
// TODO: fix, use Resource or Path
|
||||
File f = new File(new File(webAppSourceDirectory, "WEB-INF"), "web.xml");
|
||||
if (f.exists() && f.isFile())
|
||||
{
|
||||
|
|
|
@ -17,12 +17,15 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jetty.ee10.plus.webapp.EnvConfiguration;
|
||||
import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration;
|
||||
|
@ -34,6 +37,7 @@ import org.eclipse.jetty.ee10.webapp.Configuration;
|
|||
import org.eclipse.jetty.ee10.webapp.Configurations;
|
||||
import org.eclipse.jetty.ee10.webapp.MetaInfConfiguration;
|
||||
import org.eclipse.jetty.ee10.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
|
@ -69,7 +73,7 @@ public class MavenWebAppContext extends WebAppContext
|
|||
|
||||
private final Map<String, File> _webInfJarMap = new HashMap<String, File>();
|
||||
|
||||
private List<File> _classpathFiles; // webInfClasses+testClasses+webInfJars
|
||||
private List<URI> _classpathUris; // webInfClasses+testClasses+webInfJars
|
||||
|
||||
private String _jettyEnvXml;
|
||||
|
||||
|
@ -96,6 +100,12 @@ public class MavenWebAppContext extends WebAppContext
|
|||
*/
|
||||
private boolean _baseAppFirst = true;
|
||||
|
||||
/**
|
||||
* Used to track any resource bases that are mounted
|
||||
* as a result of calling {@link #setResourceBases(String[])}
|
||||
*/
|
||||
private Resource.Mount _mountedResourceBases;
|
||||
|
||||
public MavenWebAppContext() throws Exception
|
||||
{
|
||||
super();
|
||||
|
@ -123,9 +133,9 @@ public class MavenWebAppContext extends WebAppContext
|
|||
_webInfIncludeJarPattern = pattern;
|
||||
}
|
||||
|
||||
public List<File> getClassPathFiles()
|
||||
public List<URI> getClassPathUris()
|
||||
{
|
||||
return this._classpathFiles;
|
||||
return this._classpathUris;
|
||||
}
|
||||
|
||||
public void setJettyEnvXml(String jettyEnvXml)
|
||||
|
@ -214,21 +224,27 @@ public class MavenWebAppContext extends WebAppContext
|
|||
* configuration
|
||||
*
|
||||
* @param resourceBases Array of resources strings to set as a
|
||||
* {@link ResourceCollection}. Each resource string may be a
|
||||
* comma separated list of resources
|
||||
* {@link ResourceCollection}.
|
||||
*/
|
||||
public void setResourceBases(String[] resourceBases)
|
||||
{
|
||||
List<String> resources = new ArrayList<String>();
|
||||
for (String rl : resourceBases)
|
||||
try
|
||||
{
|
||||
String[] rs = StringUtil.csvSplit(rl);
|
||||
for (String r : rs)
|
||||
{
|
||||
resources.add(r);
|
||||
}
|
||||
// TODO: what happens if this is called more than once?
|
||||
|
||||
// This is a user provided list of configurations.
|
||||
// We have to assume that mounting can happen.
|
||||
List<URI> uris = Stream.of(resourceBases)
|
||||
.map(URI::create)
|
||||
.toList();
|
||||
_mountedResourceBases = Resource.mountCollection(uris);
|
||||
|
||||
setBaseResource(_mountedResourceBases.root());
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
throw new IllegalArgumentException("Bad resourceBases: [" + String.join(", ", resourceBases) + "]", t);
|
||||
}
|
||||
setBaseResource(new ResourceCollection(resources.toArray(new String[resources.size()])));
|
||||
}
|
||||
|
||||
public List<File> getWebInfLib()
|
||||
|
@ -267,15 +283,21 @@ public class MavenWebAppContext extends WebAppContext
|
|||
|
||||
// Set up the classes dirs that comprises the equivalent of
|
||||
// WEB-INF/classes
|
||||
if (_testClasses != null)
|
||||
if (_testClasses != null && _testClasses.exists())
|
||||
_webInfClasses.add(_testClasses);
|
||||
if (_classes != null)
|
||||
if (_classes != null && _classes.exists())
|
||||
_webInfClasses.add(_classes);
|
||||
|
||||
// Set up the classpath
|
||||
_classpathFiles = new ArrayList<>();
|
||||
_classpathFiles.addAll(_webInfClasses);
|
||||
_classpathFiles.addAll(_webInfJars);
|
||||
_classpathUris = new ArrayList<>();
|
||||
_webInfClasses.forEach(f -> _classpathUris.add(f.toURI()));
|
||||
_webInfJars.forEach(f ->
|
||||
{
|
||||
// ensure our JAR file references are `jar:file:...` URI references
|
||||
URI jarFileUri = Resource.toJarFileUri(f.toURI());
|
||||
// else use file uri as-is
|
||||
_classpathUris.add(Objects.requireNonNullElseGet(jarFileUri, f::toURI));
|
||||
});
|
||||
|
||||
// Initialize map containing all jars in /WEB-INF/lib
|
||||
_webInfJarMap.clear();
|
||||
|
@ -321,9 +343,9 @@ public class MavenWebAppContext extends WebAppContext
|
|||
@Override
|
||||
public void doStop() throws Exception
|
||||
{
|
||||
if (_classpathFiles != null)
|
||||
_classpathFiles.clear();
|
||||
_classpathFiles = null;
|
||||
if (_classpathUris != null)
|
||||
_classpathUris.clear();
|
||||
_classpathUris = null;
|
||||
|
||||
_classes = null;
|
||||
_testClasses = null;
|
||||
|
@ -348,6 +370,8 @@ public class MavenWebAppContext extends WebAppContext
|
|||
getServletHandler().setFilterMappings(new FilterMapping[0]);
|
||||
getServletHandler().setServlets(new ServletHolder[0]);
|
||||
getServletHandler().setServletMappings(new ServletMapping[0]);
|
||||
|
||||
IO.close(_mountedResourceBases);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
package org.eclipse.jetty.ee10.maven.plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
|
||||
import org.eclipse.jetty.ee10.webapp.Configuration;
|
||||
import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
|
||||
|
@ -52,14 +52,13 @@ public class MavenWebInfConfiguration extends WebInfConfiguration
|
|||
MavenWebAppContext jwac = (MavenWebAppContext)context;
|
||||
|
||||
//put the classes dir and all dependencies into the classpath
|
||||
if (jwac.getClassPathFiles() != null && context.getClassLoader() instanceof WebAppClassLoader)
|
||||
if (jwac.getClassPathUris() != null && context.getClassLoader() instanceof WebAppClassLoader loader)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Setting up classpath ...");
|
||||
WebAppClassLoader loader = (WebAppClassLoader)context.getClassLoader();
|
||||
for (File classpath : jwac.getClassPathFiles())
|
||||
for (URI uri : jwac.getClassPathUris())
|
||||
{
|
||||
loader.addClassPath(classpath.getCanonicalPath());
|
||||
loader.addClassPath(uri.toASCIIString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ import java.util.Set;
|
|||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
|
||||
/**
|
||||
* OverlayManager
|
||||
|
@ -63,7 +62,8 @@ public class OverlayManager
|
|||
else
|
||||
resourceBases.add(webApp.getResourceBase());
|
||||
}
|
||||
webApp.setBaseResource(new ResourceCollection(resourceBases.toArray(new Resource[resourceBases.size()])));
|
||||
|
||||
webApp.setBaseResource(Resource.of(resourceBases));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,6 +16,7 @@ package org.eclipse.jetty.ee10.maven.plugin;
|
|||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
|
@ -222,9 +223,13 @@ public class WebAppPropertyConverter
|
|||
str = webAppProperties.getProperty(BASE_DIRS);
|
||||
if (!StringUtil.isBlank(str))
|
||||
{
|
||||
ResourceCollection bases = new ResourceCollection(StringUtil.csvSplit(str));
|
||||
// This is a use provided list of overlays, which could have mountable entries.
|
||||
List<URI> uris = Resource.split(str);
|
||||
// TODO: need a better place to close/release this mount.
|
||||
Resource.Mount mount = Resource.mountCollection(uris);
|
||||
webApp.addBean(mount); // let jetty-core ContextHandler.doStop() release mount
|
||||
webApp.setWar(null);
|
||||
webApp.setBaseResource(bases);
|
||||
webApp.setBaseResource(mount.root());
|
||||
}
|
||||
|
||||
str = webAppProperties.getProperty(WAR_FILE);
|
||||
|
|
|
@ -15,7 +15,10 @@ package org.eclipse.jetty.ee10.maven.plugin;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.eclipse.jetty.ee10.webapp.WebAppContext;
|
||||
|
@ -30,6 +33,7 @@ import org.junit.jupiter.api.BeforeAll;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
@ -149,7 +153,10 @@ public class TestWebAppPropertyConverter
|
|||
assertEquals(war.getAbsolutePath(), webApp.getWar());
|
||||
assertEquals(webXml.getAbsolutePath(), webApp.getDescriptor());
|
||||
assertThat(webApp.getResourceBase(), instanceOf(ResourceCollection.class));
|
||||
assertThat(webApp.getResourceBase().toString(), Matchers.containsString(Resource.newResource(base1.toPath()).toString()));
|
||||
assertThat(webApp.getResourceBase().toString(), Matchers.containsString(Resource.newResource(base2.toPath()).toString()));
|
||||
|
||||
ResourceCollection resourceCollection = (ResourceCollection)webApp.getResourceBase();
|
||||
List<URI> actual = resourceCollection.getResources().stream().filter(Objects::nonNull).map(Resource::getURI).toList();
|
||||
URI[] expected = new URI[]{base1.toURI(), base2.toURI()};
|
||||
assertThat(actual, containsInAnyOrder(expected));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -263,8 +263,8 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration
|
|||
Resource[] resources = new Resource[1 + prependedResourcesPath.size()];
|
||||
System.arraycopy(prependedResourcesPath.values().toArray(new Resource[prependedResourcesPath.size()]), 0, resources, 0, prependedResourcesPath.size());
|
||||
resources[resources.length - 1] = context.getBaseResource();
|
||||
//TODO needs WebAppContext ResourceCollection fixed
|
||||
//context.setBaseResource(new ResourceCollection(resources));
|
||||
|
||||
context.setBaseResource(Resource.of(resources));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -94,6 +94,8 @@ public class EnvConfiguration extends AbstractConfiguration
|
|||
org.eclipse.jetty.util.resource.Resource webInf = context.getWebInf();
|
||||
if (webInf != null && webInf.isDirectory())
|
||||
{
|
||||
// TODO: should never return from WEB-INF/lib/foo.jar!/WEB-INF/jetty-env.xml
|
||||
// TODO: should also never return from a META-INF/versions/#/WEB-INF/jetty-env.xml location
|
||||
org.eclipse.jetty.util.resource.Resource jettyEnv = webInf.resolve("jetty-env.xml");
|
||||
if (jettyEnv.exists())
|
||||
{
|
||||
|
|
|
@ -247,6 +247,8 @@ public class QuickStartConfiguration extends AbstractConfiguration
|
|||
Resource qstart;
|
||||
if (attr == null || StringUtil.isBlank(attr.toString()))
|
||||
{
|
||||
// TODO: should never return from WEB-INF/lib/foo.jar!/WEB-INF/quickstart-web.xml
|
||||
// TODO: should also never return from a META-INF/versions/#/WEB-INF/quickstart-web.xml location
|
||||
qstart = webInf.resolve("quickstart-web.xml");
|
||||
}
|
||||
else
|
||||
|
|
|
@ -38,7 +38,6 @@ import org.eclipse.jetty.util.IO;
|
|||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.eclipse.jetty.xml.XmlParser;
|
||||
|
||||
/**
|
||||
|
@ -271,18 +270,15 @@ public class QuickStartDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
Collection<Resource> metaInfResources = (Collection<Resource>)context.getAttribute(MetaInfConfiguration.METAINF_RESOURCES);
|
||||
if (metaInfResources == null)
|
||||
{
|
||||
metaInfResources = new HashSet<Resource>();
|
||||
metaInfResources = new HashSet<>();
|
||||
context.setAttribute(MetaInfConfiguration.METAINF_RESOURCES, metaInfResources);
|
||||
}
|
||||
metaInfResources.add(dir);
|
||||
|
||||
//also add to base resource of webapp
|
||||
Resource[] collection = new Resource[metaInfResources.size() + 1];
|
||||
int i = 0;
|
||||
collection[i++] = context.getResourceBase();
|
||||
for (Resource resource : metaInfResources)
|
||||
{
|
||||
collection[i++] = resource;
|
||||
}
|
||||
context.setBaseResource(new ResourceCollection(collection));
|
||||
List<Resource> collection = new ArrayList<>();
|
||||
collection.add(context.getResourceBase());
|
||||
collection.addAll(metaInfResources);
|
||||
context.setBaseResource(Resource.of(collection));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Jetty Logging using jetty-slf4j-impl
|
||||
org.eclipse.jetty.LEVEL=INFO
|
||||
# org.eclipse.jetty.ee10.webapp.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.ee10.quickstart.LEVEL=DEBUG
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
package org.eclipse.jetty.ee10.webapp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
@ -20,9 +21,13 @@ import java.util.Objects;
|
|||
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.xml.XmlParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class Descriptor
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Descriptor.class);
|
||||
|
||||
protected Resource _xml;
|
||||
protected XmlParser.Node _root;
|
||||
protected String _dtd;
|
||||
|
@ -30,12 +35,15 @@ public abstract class Descriptor
|
|||
public Descriptor(Resource xml)
|
||||
{
|
||||
_xml = Objects.requireNonNull(xml);
|
||||
if (!_xml.exists())
|
||||
throw new IllegalArgumentException("Descriptor does not exist: " + xml);
|
||||
if (_xml.isDirectory())
|
||||
throw new IllegalArgumentException("Descriptor is not a file: " + xml);
|
||||
}
|
||||
|
||||
public void parse(XmlParser parser)
|
||||
throws Exception
|
||||
{
|
||||
|
||||
if (_root == null)
|
||||
{
|
||||
Objects.requireNonNull(parser);
|
||||
|
@ -44,6 +52,11 @@ public abstract class Descriptor
|
|||
_root = parser.parse(is);
|
||||
_dtd = parser.getDTD();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.warn("Unable to parse {}", _xml, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
package org.eclipse.jetty.ee10.webapp;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -455,13 +456,10 @@ public class MetaData
|
|||
{
|
||||
orderedWebInfJars = getWebInfResources(true);
|
||||
List<String> orderedLibs = new ArrayList<>();
|
||||
for (Resource webInfJar : orderedWebInfJars)
|
||||
for (Resource jar: orderedWebInfJars)
|
||||
{
|
||||
//get just the name of the jar file
|
||||
String fullname = webInfJar.getName();
|
||||
int i = fullname.indexOf(".jar");
|
||||
int j = fullname.lastIndexOf("/", i);
|
||||
orderedLibs.add(fullname.substring(j + 1, i + 4));
|
||||
URI uri = Resource.unwrapContainer(jar.getURI());
|
||||
orderedLibs.add(uri.getPath());
|
||||
}
|
||||
context.setAttribute(ServletContext.ORDERED_LIBS, Collections.unmodifiableList(orderedLibs));
|
||||
}
|
||||
|
|
|
@ -45,7 +45,6 @@ import org.eclipse.jetty.util.IO;
|
|||
import org.eclipse.jetty.util.PatternMatcher;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -337,14 +336,10 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
Set<Resource> resources = (Set<Resource>)context.getAttribute(RESOURCE_DIRS);
|
||||
if (resources != null && !resources.isEmpty())
|
||||
{
|
||||
Resource[] collection = new Resource[resources.size() + 1];
|
||||
int i = 0;
|
||||
collection[i++] = context.getResourceBase();
|
||||
for (Resource resource : resources)
|
||||
{
|
||||
collection[i++] = resource;
|
||||
}
|
||||
context.setBaseResource(new ResourceCollection(collection));
|
||||
List<Resource> collection = new ArrayList<>();
|
||||
collection.add(context.getResourceBase());
|
||||
collection.addAll(resources);
|
||||
context.setBaseResource(Resource.of(collection));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -847,6 +842,7 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
return null;
|
||||
|
||||
return context.getExtraClasspath()
|
||||
.getResources()
|
||||
.stream()
|
||||
.filter(this::isFileSupported)
|
||||
.collect(Collectors.toList());
|
||||
|
@ -892,6 +888,7 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
return null;
|
||||
|
||||
return context.getExtraClasspath()
|
||||
.getResources()
|
||||
.stream()
|
||||
.filter(Resource::isDirectory)
|
||||
.collect(Collectors.toList());
|
||||
|
@ -912,24 +909,6 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
|
||||
private boolean isFileSupported(Resource resource)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (resource.isDirectory())
|
||||
return false;
|
||||
|
||||
if (resource.getPath() == 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));
|
||||
return (extension != null && (extension.equals(".jar") || extension.equals(".zip")));
|
||||
return Resource.isArchive(resource.getURI());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,10 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.CodeSource;
|
||||
import java.security.PermissionCollection;
|
||||
|
@ -33,12 +35,12 @@ import java.util.Locale;
|
|||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -78,6 +80,8 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
private String _name = String.valueOf(hashCode());
|
||||
private final List<ClassFileTransformer> _transformers = new CopyOnWriteArrayList<>();
|
||||
|
||||
private Resource.Mount _mountedExtraClassPath;
|
||||
|
||||
/**
|
||||
* The Context in which the classloader operates.
|
||||
*/
|
||||
|
@ -107,7 +111,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
*/
|
||||
boolean isParentLoaderPriority();
|
||||
|
||||
List<Resource> getExtraClasspath();
|
||||
ResourceCollection getExtraClasspath();
|
||||
|
||||
boolean isServerResource(String name, URL parentUrl);
|
||||
|
||||
|
@ -189,7 +193,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
|
||||
if (context.getExtraClasspath() != null)
|
||||
{
|
||||
for (Resource resource : context.getExtraClasspath())
|
||||
for (Resource resource : context.getExtraClasspath().getResources())
|
||||
{
|
||||
addClassPath(resource);
|
||||
}
|
||||
|
@ -267,7 +271,11 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
if (classPath == null)
|
||||
return;
|
||||
|
||||
for (Resource resource : Resource.fromList(classPath, false, _context::newResource))
|
||||
List<URI> uris = Resource.split(classPath);
|
||||
_mountedExtraClassPath = Resource.mountCollection(uris);
|
||||
|
||||
ResourceCollection rc = (ResourceCollection)_mountedExtraClassPath.root();
|
||||
for (Resource resource : rc.getResources())
|
||||
{
|
||||
addClassPath(resource);
|
||||
}
|
||||
|
@ -275,11 +283,19 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
|
||||
/**
|
||||
* @param file Checks if this file type can be added to the classpath.
|
||||
* TODO: move to FileID in later PR
|
||||
*/
|
||||
|
||||
private boolean isFileSupported(String file)
|
||||
{
|
||||
int dot = file.lastIndexOf('.');
|
||||
return dot != -1 && _extensions.contains(file.substring(dot));
|
||||
return dot != -1 && _extensions.contains(file.substring(dot).toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
// TODO: move to FileID in later PR
|
||||
private boolean isFileSupported(Path path)
|
||||
{
|
||||
return isFileSupported(path.getFileName().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -292,32 +308,35 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
{
|
||||
if (lib.exists() && lib.isDirectory())
|
||||
{
|
||||
List<String> entries = lib.list();
|
||||
if (entries != null)
|
||||
{
|
||||
entries.sort(Comparator.naturalOrder());
|
||||
Path dir = lib.getPath();
|
||||
|
||||
for (String entry : entries)
|
||||
try (Stream<Path> streamEntries = Files.list(dir))
|
||||
{
|
||||
List<Path> jars = streamEntries
|
||||
.filter(Files::isRegularFile)
|
||||
.filter(this::isFileSupported)
|
||||
.sorted(Comparator.naturalOrder())
|
||||
.toList();
|
||||
|
||||
for (Path jar: jars)
|
||||
{
|
||||
try
|
||||
{
|
||||
Resource resource = lib.resolve(entry);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("addJar - {}", resource);
|
||||
String fnlc = resource.getName().toLowerCase(Locale.ENGLISH);
|
||||
// don't check if this is a directory (prevents use of symlinks), see Bug 353165
|
||||
if (isFileSupported(fnlc))
|
||||
{
|
||||
String jar = URIUtil.encodeSpecific(resource.toString(), ",;");
|
||||
addClassPath(jar);
|
||||
}
|
||||
LOG.debug("addJar - {}", jar);
|
||||
URI jarUri = Resource.toJarFileUri(jar.toUri());
|
||||
addClassPath(jarUri.toASCIIString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LOG.warn("Unable to load WEB-INF/lib JAR {}", entry, ex);
|
||||
LOG.warn("Unable to load WEB-INF/lib JAR {}", jar, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.warn("Unable to load WEB-INF/lib JARs: {}", dir, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -636,6 +655,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
public void close() throws IOException
|
||||
{
|
||||
super.close();
|
||||
IO.close(_mountedExtraClassPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,7 @@ package org.eclipse.jetty.ee10.webapp;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Path;
|
||||
|
@ -129,7 +130,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
private boolean _persistTmpDir = false;
|
||||
|
||||
private String _war;
|
||||
private List<Resource> _extraClasspath;
|
||||
private ResourceCollection _extraClasspath;
|
||||
private Throwable _unavailableException;
|
||||
|
||||
private Map<String, String> _resourceAliases;
|
||||
|
@ -803,10 +804,12 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
return null;
|
||||
|
||||
// Iw there a WEB-INF directory?
|
||||
Resource webInf = getResourceBase().resolve("WEB-INF/");
|
||||
Resource webInf = getResourceBase().resolve("WEB-INF/"); // TODO: what does this do in a collection?
|
||||
if (!webInf.exists() || !webInf.isDirectory())
|
||||
return null;
|
||||
|
||||
// TODO: should never return from WEB-INF/lib/foo.jar!/WEB-INF
|
||||
// TODO: should also never return from a META-INF/versions/#/WEB-INF location
|
||||
return webInf;
|
||||
}
|
||||
|
||||
|
@ -1223,7 +1226,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
*/
|
||||
@Override
|
||||
@ManagedAttribute(value = "extra classpath for context classloader", readonly = true)
|
||||
public List<Resource> getExtraClasspath()
|
||||
public ResourceCollection getExtraClasspath()
|
||||
{
|
||||
return _extraClasspath;
|
||||
}
|
||||
|
@ -1231,21 +1234,23 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
/**
|
||||
* Set the Extra ClassPath via delimited String.
|
||||
* <p>
|
||||
* This is a convenience method for {@link #setExtraClasspath(List)}
|
||||
* This is a convenience method for {@link #setExtraClasspath(ResourceCollection)}
|
||||
* </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)
|
||||
* @see #setExtraClasspath(ResourceCollection)
|
||||
*/
|
||||
public void setExtraClasspath(String extraClasspath) throws IOException
|
||||
public void setExtraClasspath(String extraClasspath)
|
||||
{
|
||||
setExtraClasspath(Resource.fromList(extraClasspath, false, this::newResource));
|
||||
List<URI> uris = Resource.split(extraClasspath);
|
||||
Resource.Mount mount = Resource.mountCollection(uris);
|
||||
addBean(mount); // let doStop() cleanup mount
|
||||
setExtraClasspath((ResourceCollection)mount.root());
|
||||
}
|
||||
|
||||
public void setExtraClasspath(List<Resource> extraClasspath)
|
||||
public void setExtraClasspath(ResourceCollection extraClasspath)
|
||||
{
|
||||
_extraClasspath = extraClasspath;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ import org.eclipse.jetty.util.StringUtil;
|
|||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.resource.MountedPathResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -452,10 +451,10 @@ public class WebInfConfiguration extends AbstractConfiguration
|
|||
|
||||
webInf = Resource.newResource(extractedWebInfDir.getCanonicalPath());
|
||||
|
||||
ResourceCollection rc = new ResourceCollection(webInf, webApp);
|
||||
Resource rc = Resource.of(webInf, webApp);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("context.resourcebase={}", rc);
|
||||
LOG.debug("context.baseResource={}", rc);
|
||||
|
||||
context.setBaseResource(rc);
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ public class OrderingTest
|
|||
@Override
|
||||
public boolean exists()
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -743,12 +743,12 @@ public class OrderingTest
|
|||
public void testRelativeOrderingWithPlainJars()
|
||||
throws Exception
|
||||
{
|
||||
//B,A,C other jars with no fragments
|
||||
// B,A,C other jars with no fragments
|
||||
List<Resource> resources = new ArrayList<Resource>();
|
||||
MetaData metaData = new MetaData();
|
||||
metaData._ordering = new RelativeOrdering(metaData);
|
||||
|
||||
//A: after others, before C
|
||||
// A: after others, before C
|
||||
TestResource jar1 = new TestResource("A");
|
||||
resources.add(jar1);
|
||||
TestResource r1 = new TestResource("A/web-fragment.xml");
|
||||
|
|
|
@ -14,12 +14,13 @@
|
|||
package org.eclipse.jetty.ee10.webapp;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -58,6 +59,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -486,7 +488,7 @@ public class WebAppContextTest
|
|||
/**
|
||||
* Test using WebAppContext.setExtraClassPath(String) with a reference to a glob
|
||||
*/
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@ParameterizedTest
|
||||
@MethodSource("extraClasspathGlob")
|
||||
public void testExtraClasspathGlob(String description, String extraClasspathGlobReference) throws Exception
|
||||
{
|
||||
|
@ -510,32 +512,25 @@ public class WebAppContextTest
|
|||
WebAppClassLoader webAppClassLoader = (WebAppClassLoader)contextClassLoader;
|
||||
Path extLibsDir = MavenTestingUtils.getTestResourcePathDir("ext");
|
||||
extLibsDir = extLibsDir.toAbsolutePath();
|
||||
List<Path> expectedPaths;
|
||||
List<URI> expectedUris;
|
||||
try (Stream<Path> s = Files.list(extLibsDir))
|
||||
{
|
||||
expectedPaths = s
|
||||
expectedUris = s
|
||||
.filter(Files::isRegularFile)
|
||||
.filter((path) -> path.toString().endsWith(".jar"))
|
||||
.filter((path) -> path.getFileName().toString().endsWith(".jar"))
|
||||
.sorted(Comparator.naturalOrder())
|
||||
.map(Path::toUri)
|
||||
.map(Resource::toJarFileUri)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
List<Path> actualPaths = new ArrayList<>();
|
||||
List<URI> actualURIs = new ArrayList<>();
|
||||
for (URL url : webAppClassLoader.getURLs())
|
||||
{
|
||||
actualPaths.add(Paths.get(url.toURI()));
|
||||
}
|
||||
assertThat("[" + description + "] WebAppClassLoader.urls.length", actualPaths.size(), is(expectedPaths.size()));
|
||||
for (Path expectedPath : expectedPaths)
|
||||
{
|
||||
boolean found = false;
|
||||
for (Path actualPath : actualPaths)
|
||||
{
|
||||
if (Files.isSameFile(actualPath, expectedPath))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
assertTrue(found, "[" + description + "] Not able to find expected jar in WebAppClassLoader: " + expectedPath);
|
||||
actualURIs.add(url.toURI());
|
||||
}
|
||||
assertThat("[" + description + "] WebAppClassLoader.urls.length", actualURIs.size(), is(expectedUris.size()));
|
||||
|
||||
assertThat(actualURIs, contains(expectedUris.toArray()));
|
||||
}
|
||||
|
||||
public static Stream<Arguments> extraClasspathDir()
|
||||
|
|
|
@ -17,12 +17,15 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jetty.ee9.plus.webapp.EnvConfiguration;
|
||||
import org.eclipse.jetty.ee9.quickstart.QuickStartConfiguration;
|
||||
|
@ -34,6 +37,7 @@ import org.eclipse.jetty.ee9.webapp.Configuration;
|
|||
import org.eclipse.jetty.ee9.webapp.Configurations;
|
||||
import org.eclipse.jetty.ee9.webapp.MetaInfConfiguration;
|
||||
import org.eclipse.jetty.ee9.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
|
@ -69,7 +73,7 @@ public class MavenWebAppContext extends WebAppContext
|
|||
|
||||
private final Map<String, File> _webInfJarMap = new HashMap<String, File>();
|
||||
|
||||
private List<File> _classpathFiles; // webInfClasses+testClasses+webInfJars
|
||||
private List<URI> _classpathUris; // webInfClasses+testClasses+webInfJars
|
||||
|
||||
private String _jettyEnvXml;
|
||||
|
||||
|
@ -96,6 +100,12 @@ public class MavenWebAppContext extends WebAppContext
|
|||
*/
|
||||
private boolean _baseAppFirst = true;
|
||||
|
||||
/**
|
||||
* Used to track any resource bases that are mounted
|
||||
* as a result of calling {@link #setResourceBases(String[])}
|
||||
*/
|
||||
private Resource.Mount _mountedResourceBases;
|
||||
|
||||
public MavenWebAppContext() throws Exception
|
||||
{
|
||||
super();
|
||||
|
@ -123,9 +133,9 @@ public class MavenWebAppContext extends WebAppContext
|
|||
_webInfIncludeJarPattern = pattern;
|
||||
}
|
||||
|
||||
public List<File> getClassPathFiles()
|
||||
public List<URI> getClassPathUris()
|
||||
{
|
||||
return this._classpathFiles;
|
||||
return this._classpathUris;
|
||||
}
|
||||
|
||||
public void setJettyEnvXml(String jettyEnvXml)
|
||||
|
@ -214,21 +224,27 @@ public class MavenWebAppContext extends WebAppContext
|
|||
* configuration
|
||||
*
|
||||
* @param resourceBases Array of resources strings to set as a
|
||||
* {@link ResourceCollection}. Each resource string may be a
|
||||
* comma separated list of resources
|
||||
* {@link ResourceCollection}.
|
||||
*/
|
||||
public void setResourceBases(String[] resourceBases)
|
||||
{
|
||||
List<String> resources = new ArrayList<String>();
|
||||
for (String rl : resourceBases)
|
||||
try
|
||||
{
|
||||
String[] rs = StringUtil.csvSplit(rl);
|
||||
for (String r : rs)
|
||||
{
|
||||
resources.add(r);
|
||||
}
|
||||
// TODO: what happens if this is called more than once?
|
||||
|
||||
// This is a user provided list of configurations.
|
||||
// We have to assume that mounting can happen.
|
||||
List<URI> uris = Stream.of(resourceBases)
|
||||
.map(URI::create)
|
||||
.toList();
|
||||
_mountedResourceBases = Resource.mountCollection(uris);
|
||||
|
||||
setBaseResource(_mountedResourceBases.root());
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
throw new IllegalArgumentException("Bad resourceBases: [" + String.join(", ", resourceBases) + "]", t);
|
||||
}
|
||||
setBaseResource(new ResourceCollection(resources.toArray(new String[resources.size()])));
|
||||
}
|
||||
|
||||
public List<File> getWebInfLib()
|
||||
|
@ -267,15 +283,21 @@ public class MavenWebAppContext extends WebAppContext
|
|||
|
||||
// Set up the classes dirs that comprises the equivalent of
|
||||
// WEB-INF/classes
|
||||
if (_testClasses != null)
|
||||
if (_testClasses != null && _testClasses.exists())
|
||||
_webInfClasses.add(_testClasses);
|
||||
if (_classes != null)
|
||||
if (_classes != null && _classes.exists())
|
||||
_webInfClasses.add(_classes);
|
||||
|
||||
// Set up the classpath
|
||||
_classpathFiles = new ArrayList<>();
|
||||
_classpathFiles.addAll(_webInfClasses);
|
||||
_classpathFiles.addAll(_webInfJars);
|
||||
_classpathUris = new ArrayList<>();
|
||||
_webInfClasses.forEach(f -> _classpathUris.add(f.toURI()));
|
||||
_webInfJars.forEach(f ->
|
||||
{
|
||||
// ensure our JAR file references are `jar:file:...` URI references
|
||||
URI jarFileUri = Resource.toJarFileUri(f.toURI());
|
||||
// else use file uri as-is
|
||||
_classpathUris.add(Objects.requireNonNullElseGet(jarFileUri, f::toURI));
|
||||
});
|
||||
|
||||
// Initialize map containing all jars in /WEB-INF/lib
|
||||
_webInfJarMap.clear();
|
||||
|
@ -321,9 +343,9 @@ public class MavenWebAppContext extends WebAppContext
|
|||
@Override
|
||||
public void doStop() throws Exception
|
||||
{
|
||||
if (_classpathFiles != null)
|
||||
_classpathFiles.clear();
|
||||
_classpathFiles = null;
|
||||
if (_classpathUris != null)
|
||||
_classpathUris.clear();
|
||||
_classpathUris = null;
|
||||
|
||||
_classes = null;
|
||||
_testClasses = null;
|
||||
|
@ -348,6 +370,8 @@ public class MavenWebAppContext extends WebAppContext
|
|||
getServletHandler().setFilterMappings(new FilterMapping[0]);
|
||||
getServletHandler().setServlets(new ServletHolder[0]);
|
||||
getServletHandler().setServletMappings(new ServletMapping[0]);
|
||||
|
||||
IO.close(_mountedResourceBases);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
package org.eclipse.jetty.ee9.maven.plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
|
||||
import org.eclipse.jetty.ee9.webapp.Configuration;
|
||||
import org.eclipse.jetty.ee9.webapp.WebAppClassLoader;
|
||||
|
@ -51,15 +51,15 @@ public class MavenWebInfConfiguration extends WebInfConfiguration
|
|||
{
|
||||
MavenWebAppContext jwac = (MavenWebAppContext)context;
|
||||
|
||||
//put the classes dir and all dependencies into the classpath
|
||||
if (jwac.getClassPathFiles() != null && context.getClassLoader() instanceof WebAppClassLoader)
|
||||
// put the classes dir and all dependencies into the classpath
|
||||
if (jwac.getClassPathUris() != null && context.getClassLoader() instanceof WebAppClassLoader loader)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Setting up classpath ...");
|
||||
WebAppClassLoader loader = (WebAppClassLoader)context.getClassLoader();
|
||||
for (File classpath : jwac.getClassPathFiles())
|
||||
|
||||
for (URI uri : jwac.getClassPathUris())
|
||||
{
|
||||
loader.addClassPath(classpath.getCanonicalPath());
|
||||
loader.addClassPath(uri.toASCIIString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ import java.util.Set;
|
|||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
|
||||
/**
|
||||
* OverlayManager
|
||||
|
@ -63,8 +62,8 @@ public class OverlayManager
|
|||
else
|
||||
resourceBases.add(webApp.getBaseResource());
|
||||
}
|
||||
|
||||
webApp.setBaseResource(new ResourceCollection(resourceBases.toArray(new Resource[resourceBases.size()])));
|
||||
|
||||
webApp.setBaseResource(Resource.of(resourceBases));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,8 +15,8 @@ package org.eclipse.jetty.ee9.maven.plugin;
|
|||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
|
@ -223,9 +223,13 @@ public class WebAppPropertyConverter
|
|||
str = webAppProperties.getProperty(BASE_DIRS);
|
||||
if (!StringUtil.isBlank(str))
|
||||
{
|
||||
ResourceCollection bases = new ResourceCollection(StringUtil.csvSplit(str));
|
||||
// This is a use provided list of overlays, which could have mountable entries.
|
||||
List<URI> uris = Resource.split(str);
|
||||
// TODO: need a better place to close/release this mount.
|
||||
Resource.Mount mount = Resource.mountCollection(uris);
|
||||
webApp.addBean(mount); // let ee9 ContextHandler.doStop() release mount
|
||||
webApp.setWar(null);
|
||||
webApp.setBaseResource(bases);
|
||||
webApp.setBaseResource(mount.root());
|
||||
}
|
||||
|
||||
str = webAppProperties.getProperty(WAR_FILE);
|
||||
|
|
|
@ -15,7 +15,10 @@ package org.eclipse.jetty.ee9.maven.plugin;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.eclipse.jetty.ee9.webapp.WebAppContext;
|
||||
|
@ -30,6 +33,7 @@ import org.junit.jupiter.api.BeforeAll;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
@ -149,7 +153,10 @@ public class TestWebAppPropertyConverter
|
|||
assertEquals(war.getAbsolutePath(), webApp.getWar());
|
||||
assertEquals(webXml.getAbsolutePath(), webApp.getDescriptor());
|
||||
assertThat(webApp.getBaseResource(), instanceOf(ResourceCollection.class));
|
||||
assertThat(webApp.getBaseResource().toString(), Matchers.containsString(Resource.newResource(base1.toPath()).toString()));
|
||||
assertThat(webApp.getBaseResource().toString(), Matchers.containsString(Resource.newResource(base2.toPath()).toString()));
|
||||
|
||||
ResourceCollection resourceCollection = (ResourceCollection)webApp.getBaseResource();
|
||||
List<URI> actual = resourceCollection.getResources().stream().filter(Objects::nonNull).map(Resource::getURI).toList();
|
||||
URI[] expected = new URI[]{base1.toURI(), base2.toURI()};
|
||||
assertThat(actual, containsInAnyOrder(expected));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ import org.eclipse.jetty.server.handler.ContextRequest;
|
|||
import org.eclipse.jetty.util.Attributes;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.ExceptionUtil;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.Index;
|
||||
import org.eclipse.jetty.util.Loader;
|
||||
import org.eclipse.jetty.util.MultiMap;
|
||||
|
@ -815,6 +816,15 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
}
|
||||
}
|
||||
_programmaticListeners.clear();
|
||||
|
||||
// cleanup any Mounts associated with the ContextHandler on stop.
|
||||
// TODO: but what if the context is restarted? how do we remount? do we care?
|
||||
java.util.Collection<Resource.Mount> mounts = getBeans(Resource.Mount.class);
|
||||
mounts.forEach((mount) ->
|
||||
{
|
||||
IO.close(mount);
|
||||
removeBean(mount);
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
@ -263,7 +263,7 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration
|
|||
Resource[] resources = new Resource[1 + prependedResourcesPath.size()];
|
||||
System.arraycopy(prependedResourcesPath.values().toArray(new Resource[prependedResourcesPath.size()]), 0, resources, 0, prependedResourcesPath.size());
|
||||
resources[resources.length - 1] = context.getBaseResource();
|
||||
context.setBaseResource(new ResourceCollection(resources));
|
||||
context.setBaseResource(Resource.of(resources));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,6 @@ import org.eclipse.jetty.util.IO;
|
|||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.eclipse.jetty.xml.XmlParser;
|
||||
|
||||
/**
|
||||
|
@ -266,6 +265,7 @@ public class QuickStartDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
context.addServletContainerInitializer(sciHolder);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void visitMetaInfResource(WebAppContext context, Resource dir)
|
||||
{
|
||||
Collection<Resource> metaInfResources = (Collection<Resource>)context.getAttribute(MetaInfConfiguration.METAINF_RESOURCES);
|
||||
|
@ -275,14 +275,11 @@ public class QuickStartDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
context.setAttribute(MetaInfConfiguration.METAINF_RESOURCES, metaInfResources);
|
||||
}
|
||||
metaInfResources.add(dir);
|
||||
|
||||
//also add to base resource of webapp
|
||||
Resource[] collection = new Resource[metaInfResources.size() + 1];
|
||||
int i = 0;
|
||||
collection[i++] = context.getBaseResource();
|
||||
for (Resource resource : metaInfResources)
|
||||
{
|
||||
collection[i++] = resource;
|
||||
}
|
||||
context.setBaseResource(new ResourceCollection(collection));
|
||||
List<Resource> collection = new ArrayList<>();
|
||||
collection.add(context.getBaseResource());
|
||||
collection.addAll(metaInfResources);
|
||||
context.setBaseResource(Resource.of(collection));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
package org.eclipse.jetty.ee9.webapp;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -446,7 +447,7 @@ public class MetaData
|
|||
{
|
||||
LOG.debug("metadata resolve {}", context);
|
||||
|
||||
//Ensure origins is fresh
|
||||
// Ensure origins is fresh
|
||||
_origins.clear();
|
||||
|
||||
// Set the ordered lib attribute
|
||||
|
@ -455,13 +456,10 @@ public class MetaData
|
|||
{
|
||||
orderedWebInfJars = getWebInfResources(true);
|
||||
List<String> orderedLibs = new ArrayList<>();
|
||||
for (Resource webInfJar : orderedWebInfJars)
|
||||
for (Resource jar: orderedWebInfJars)
|
||||
{
|
||||
//get just the name of the jar file
|
||||
String fullname = webInfJar.getName();
|
||||
int i = fullname.indexOf(".jar");
|
||||
int j = fullname.lastIndexOf("/", i);
|
||||
orderedLibs.add(fullname.substring(j + 1, i + 4));
|
||||
URI uri = Resource.unwrapContainer(jar.getURI());
|
||||
orderedLibs.add(uri.getPath());
|
||||
}
|
||||
context.setAttribute(ServletContext.ORDERED_LIBS, Collections.unmodifiableList(orderedLibs));
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@ import org.eclipse.jetty.util.IO;
|
|||
import org.eclipse.jetty.util.PatternMatcher;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -293,7 +292,6 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
if (jars != null)
|
||||
{
|
||||
List<URI> uris = new ArrayList<>();
|
||||
int i = 0;
|
||||
for (Resource r : jars)
|
||||
{
|
||||
uris.add(r.getURI());
|
||||
|
@ -325,20 +323,15 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
@Override
|
||||
public void configure(WebAppContext context) throws Exception
|
||||
{
|
||||
|
||||
// Look for extra resource
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<Resource> resources = (Set<Resource>)context.getAttribute(RESOURCE_DIRS);
|
||||
if (resources != null && !resources.isEmpty())
|
||||
{
|
||||
Resource[] collection = new Resource[resources.size() + 1];
|
||||
int i = 0;
|
||||
collection[i++] = context.getBaseResource();
|
||||
for (Resource resource : resources)
|
||||
{
|
||||
collection[i++] = resource;
|
||||
}
|
||||
context.setBaseResource(new ResourceCollection(collection));
|
||||
List<Resource> collection = new ArrayList<>();
|
||||
collection.add(context.getBaseResource());
|
||||
collection.addAll(resources);
|
||||
context.setBaseResource(Resource.of(collection));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -840,6 +833,7 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
return null;
|
||||
|
||||
return context.getExtraClasspath()
|
||||
.getResources()
|
||||
.stream()
|
||||
.filter(this::isFileSupported)
|
||||
.collect(Collectors.toList());
|
||||
|
@ -884,7 +878,7 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
if (context == null || context.getExtraClasspath() == null)
|
||||
return null;
|
||||
|
||||
return context.getExtraClasspath()
|
||||
return context.getExtraClasspath().getResources()
|
||||
.stream()
|
||||
.filter(Resource::isDirectory)
|
||||
.collect(Collectors.toList());
|
||||
|
@ -905,24 +899,6 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
|
||||
private boolean isFileSupported(Resource resource)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (resource.isDirectory())
|
||||
return false;
|
||||
|
||||
if (resource.getPath() == 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));
|
||||
return (extension != null && (extension.equals(".jar") || extension.equals(".zip")));
|
||||
return Resource.isArchive(resource.getURI());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,10 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.CodeSource;
|
||||
import java.security.PermissionCollection;
|
||||
|
@ -33,12 +35,12 @@ import java.util.Locale;
|
|||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -77,6 +79,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
private final Set<String> _extensions = new HashSet<String>();
|
||||
private String _name = String.valueOf(hashCode());
|
||||
private final List<ClassFileTransformer> _transformers = new CopyOnWriteArrayList<>();
|
||||
private Resource.Mount _mountedExtraClassPath;
|
||||
|
||||
/**
|
||||
* The Context in which the classloader operates.
|
||||
|
@ -107,7 +110,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
*/
|
||||
boolean isParentLoaderPriority();
|
||||
|
||||
List<Resource> getExtraClasspath();
|
||||
ResourceCollection getExtraClasspath();
|
||||
|
||||
boolean isServerResource(String name, URL parentUrl);
|
||||
|
||||
|
@ -189,7 +192,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
|
||||
if (context.getExtraClasspath() != null)
|
||||
{
|
||||
for (Resource resource : context.getExtraClasspath())
|
||||
for (Resource resource : context.getExtraClasspath().getResources())
|
||||
{
|
||||
addClassPath(resource);
|
||||
}
|
||||
|
@ -267,19 +270,32 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
if (classPath == null)
|
||||
return;
|
||||
|
||||
for (Resource resource : Resource.fromList(classPath, false, _context::newResource))
|
||||
List<URI> uris = Resource.split(classPath);
|
||||
_mountedExtraClassPath = Resource.mountCollection(uris);
|
||||
|
||||
ResourceCollection rc = (ResourceCollection)_mountedExtraClassPath.root();
|
||||
for (Resource resource : rc.getResources())
|
||||
{
|
||||
addClassPath(resource);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file Checks if this file type can be added to the classpath.
|
||||
* TODO: move to FileID in later PR
|
||||
*/
|
||||
|
||||
private boolean isFileSupported(String file)
|
||||
{
|
||||
int dot = file.lastIndexOf('.');
|
||||
return dot != -1 && _extensions.contains(file.substring(dot));
|
||||
return dot != -1 && _extensions.contains(file.substring(dot).toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
// TODO: move to FileID in later PR
|
||||
private boolean isFileSupported(Path path)
|
||||
{
|
||||
return isFileSupported(path.getFileName().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -292,32 +308,35 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
{
|
||||
if (lib.exists() && lib.isDirectory())
|
||||
{
|
||||
List<String> entries = lib.list();
|
||||
if (entries != null)
|
||||
{
|
||||
entries.sort(Comparator.naturalOrder());
|
||||
Path dir = lib.getPath();
|
||||
|
||||
for (String entry : entries)
|
||||
try (Stream<Path> streamEntries = Files.list(dir))
|
||||
{
|
||||
List<Path> jars = streamEntries
|
||||
.filter(Files::isRegularFile)
|
||||
.filter(this::isFileSupported)
|
||||
.sorted(Comparator.naturalOrder())
|
||||
.toList();
|
||||
|
||||
for (Path jar: jars)
|
||||
{
|
||||
try
|
||||
{
|
||||
Resource resource = lib.resolve(entry);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("addJar - {}", resource);
|
||||
String fnlc = resource.getName().toLowerCase(Locale.ENGLISH);
|
||||
// don't check if this is a directory (prevents use of symlinks), see Bug 353165
|
||||
if (isFileSupported(fnlc))
|
||||
{
|
||||
String jar = URIUtil.encodeSpecific(resource.toString(), ",;");
|
||||
addClassPath(jar);
|
||||
}
|
||||
LOG.debug("addJar - {}", jar);
|
||||
URI jarUri = Resource.toJarFileUri(jar.toUri());
|
||||
addClassPath(jarUri.toASCIIString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LOG.warn("Unable to load WEB-INF/lib JAR {}", entry, ex);
|
||||
LOG.warn("Unable to load WEB-INF/lib JAR {}", jar, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.warn("Unable to load WEB-INF/lib JARs: {}", dir, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -636,6 +655,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
|
|||
public void close() throws IOException
|
||||
{
|
||||
super.close();
|
||||
IO.close(_mountedExtraClassPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,7 @@ package org.eclipse.jetty.ee9.webapp;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.security.PermissionCollection;
|
||||
|
@ -54,6 +55,7 @@ import org.eclipse.jetty.server.Handler;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.Attributes;
|
||||
import org.eclipse.jetty.util.ExceptionUtil;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
|
@ -132,7 +134,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
private boolean _persistTmpDir = false;
|
||||
|
||||
private String _war;
|
||||
private List<Resource> _extraClasspath;
|
||||
private ResourceCollection _extraClasspath;
|
||||
private Throwable _unavailableException;
|
||||
|
||||
private Map<String, String> _resourceAliases;
|
||||
|
@ -144,6 +146,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
private MetaData _metadata = new MetaData();
|
||||
private boolean _defaultContextPath = true;
|
||||
|
||||
private Resource.Mount _mountedExtraClasspath;
|
||||
|
||||
public static WebAppContext getCurrentWebAppContext()
|
||||
{
|
||||
ContextHandler.APIContext context = ContextHandler.getCurrentContext();
|
||||
|
@ -534,6 +538,13 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
super.doStop();
|
||||
IO.close(_mountedExtraClasspath);
|
||||
}
|
||||
|
||||
private void wrapConfigurations()
|
||||
{
|
||||
Collection<Configuration.WrapperFunction> wrappers = getBeans(Configuration.WrapperFunction.class);
|
||||
|
@ -1221,7 +1232,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
*/
|
||||
@Override
|
||||
@ManagedAttribute(value = "extra classpath for context classloader", readonly = true)
|
||||
public List<Resource> getExtraClasspath()
|
||||
public ResourceCollection getExtraClasspath()
|
||||
{
|
||||
return _extraClasspath;
|
||||
}
|
||||
|
@ -1229,21 +1240,23 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
|
|||
/**
|
||||
* Set the Extra ClassPath via delimited String.
|
||||
* <p>
|
||||
* This is a convenience method for {@link #setExtraClasspath(List)}
|
||||
* This is a convenience method for {@link #setExtraClasspath(ResourceCollection)}
|
||||
* </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)
|
||||
* @see #setExtraClasspath(ResourceCollection)
|
||||
*/
|
||||
public void setExtraClasspath(String extraClasspath) throws IOException
|
||||
{
|
||||
setExtraClasspath(Resource.fromList(extraClasspath, false, this::newResource));
|
||||
List<URI> uris = Resource.split(extraClasspath);
|
||||
_mountedExtraClasspath = Resource.mountCollection(uris);
|
||||
setExtraClasspath((ResourceCollection)_mountedExtraClasspath.root());
|
||||
}
|
||||
|
||||
public void setExtraClasspath(List<Resource> extraClasspath)
|
||||
public void setExtraClasspath(ResourceCollection extraClasspath)
|
||||
{
|
||||
_extraClasspath = extraClasspath;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ import org.eclipse.jetty.util.StringUtil;
|
|||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.resource.MountedPathResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -450,10 +449,10 @@ public class WebInfConfiguration extends AbstractConfiguration
|
|||
|
||||
webInf = Resource.newResource(extractedWebInfDir.getCanonicalPath());
|
||||
|
||||
ResourceCollection rc = new ResourceCollection(webInf, webApp);
|
||||
Resource rc = Resource.of(webInf, webApp);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("context.resourcebase={}", rc);
|
||||
LOG.debug("context.baseResource={}", rc);
|
||||
|
||||
context.setBaseResource(rc);
|
||||
}
|
||||
|
|
|
@ -14,12 +14,13 @@
|
|||
package org.eclipse.jetty.ee9.webapp;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -58,6 +59,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -515,32 +517,26 @@ public class WebAppContextTest
|
|||
WebAppClassLoader webAppClassLoader = (WebAppClassLoader)contextClassLoader;
|
||||
Path extLibsDir = MavenTestingUtils.getTargetPath("test-classes/ext");
|
||||
extLibsDir = extLibsDir.toAbsolutePath();
|
||||
List<Path> expectedPaths;
|
||||
|
||||
List<URI> expectedUris;
|
||||
try (Stream<Path> s = Files.list(extLibsDir))
|
||||
{
|
||||
expectedPaths = s
|
||||
expectedUris = s
|
||||
.filter(Files::isRegularFile)
|
||||
.filter((path) -> path.toString().endsWith(".jar"))
|
||||
.filter((path) -> path.getFileName().toString().endsWith(".jar"))
|
||||
.sorted(Comparator.naturalOrder())
|
||||
.map(Path::toUri)
|
||||
.map(Resource::toJarFileUri)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
List<Path> actualPaths = new ArrayList<>();
|
||||
List<URI> actualURIs = new ArrayList<>();
|
||||
for (URL url : webAppClassLoader.getURLs())
|
||||
{
|
||||
actualPaths.add(Paths.get(url.toURI()));
|
||||
}
|
||||
assertThat("[" + description + "] WebAppClassLoader.urls.length", actualPaths.size(), is(expectedPaths.size()));
|
||||
for (Path expectedPath : expectedPaths)
|
||||
{
|
||||
boolean found = false;
|
||||
for (Path actualPath : actualPaths)
|
||||
{
|
||||
if (Files.isSameFile(actualPath, expectedPath))
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
assertTrue(found, "[" + description + "] Not able to find expected jar in WebAppClassLoader: " + expectedPath);
|
||||
actualURIs.add(url.toURI());
|
||||
}
|
||||
assertThat("[" + description + "] WebAppClassLoader.urls.length", actualURIs.size(), is(expectedUris.size()));
|
||||
|
||||
assertThat(actualURIs, contains(expectedUris.toArray()));
|
||||
}
|
||||
|
||||
public static Stream<Arguments> extraClasspathDir()
|
||||
|
|
Loading…
Reference in New Issue