Skip to content

Commit cf62094

Browse files
committed
Use a ClassLoader for locating resources
Before this change, the VirtualFileSystem was using a `Class` as a beacon to find resources. I suppose that the intent was to use `Class#getResource`, but in practice, it's always `clazz.getClassLoader().getResource()` which is used, which makes it redundant to use a class. This commit refactors the code to allow declaring which _classloader_ to use for loading. This is important because in some cases, we don't necessarily have a `Class` to pass, but we _do_ have a classloader. This should also allow more advanced scenarios with filtering classloaders for example. Since the `Class` was actually never directly used, but only its classloader, this change should have 0 impact for backwards compatibility.
1 parent f118f1b commit cf62094

File tree

2 files changed

+46
-28
lines changed

2 files changed

+46
-28
lines changed

org.graalvm.python.embedding/src/main/java/org/graalvm/python/embedding/VirtualFileSystem.java

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ public static final class Builder {
107107
private HostIO allowHostIO = HostIO.READ_WRITE;
108108
private boolean caseInsensitive = VirtualFileSystemImpl.isWindows();
109109

110-
private Class<?> resourceLoadingClass;
110+
private ClassLoader resourceClassLoader;
111+
111112
private String resourceDirectory;
112113

113114
private Builder() {
@@ -238,21 +239,36 @@ public Builder unixMountPoint(String unixMountPoint) {
238239

239240
/**
240241
* By default, virtual filesystem resources are loaded by delegating to
241-
* <code>VirtualFileSystem.class.getResource(name)</code>. Use
242+
* <code>VirtualFileSystem.class.getClassLoader().getResource(name)</code>. Use
242243
* <code>resourceLoadingClass</code> to determine where to locate resources in
243244
* cases when for example <code>VirtualFileSystem</code> is on module path and
244245
* the jar containing the resources is on class path.
245246
*
246-
* @param c
247-
* the class for loading the resources
247+
* @param c the class for loading the resources
248248
* @return the builder
249249
* @since 24.2.0
250250
*/
251251
public Builder resourceLoadingClass(Class<?> c) {
252-
resourceLoadingClass = c;
252+
resourceClassLoader = c.getClassLoader();
253253
return this;
254254
}
255255

256+
/**
257+
* By default, virtual filesystem resources are loaded by delegating to
258+
* <code>VirtualFileSystem.class.getClassLoader().getResource(name)</code>. Use
259+
* <code>resourceClassLoader</code> to determine where to locate resources in
260+
* cases when for example <code>VirtualFileSystem</code> is on module path and
261+
* the jar containing the resources is on class path.
262+
*
263+
* @param cl the classloader used to load resources
264+
* @return this builder
265+
* @since 26.0.0
266+
*/
267+
public Builder resourceClassLoader(ClassLoader cl) {
268+
resourceClassLoader = cl;
269+
return this;
270+
}
271+
256272
/**
257273
* This filter applied to files in the virtual filesystem treats them as
258274
* symlinks to real files in the host filesystem. This is useful, for example,
@@ -292,7 +308,7 @@ public VirtualFileSystem build() {
292308
? Path.of(DEFAULT_WINDOWS_MOUNT_POINT)
293309
: Path.of(DEFAULT_UNIX_MOUNT_POINT);
294310
}
295-
return new VirtualFileSystem(extractFilter, mountPoint, allowHostIO, resourceLoadingClass,
311+
return new VirtualFileSystem(extractFilter, mountPoint, allowHostIO, resourceClassLoader,
296312
resourceDirectory, caseInsensitive);
297313
}
298314
}
@@ -308,10 +324,10 @@ private static Path getMountPointAsPath(String mp) {
308324
}
309325

310326
private VirtualFileSystem(Predicate<Path> extractFilter, Path mountPoint, HostIO allowHostIO,
311-
Class<?> resourceLoadingClass, String resourceDirectory, boolean caseInsensitive) {
327+
ClassLoader resourceClassLoader, String resourceDirectory, boolean caseInsensitive) {
312328

313329
this.impl = new VirtualFileSystemImpl(extractFilter, mountPoint, resourceDirectory, allowHostIO,
314-
resourceLoadingClass, caseInsensitive);
330+
resourceClassLoader, caseInsensitive);
315331
this.delegatingFileSystem = VirtualFileSystemImpl.createDelegatingFileSystem(impl);
316332
}
317333

org.graalvm.python.embedding/src/main/java/org/graalvm/python/embedding/VirtualFileSystemImpl.java

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -169,12 +169,12 @@ private static String absoluteResourcePath(String... components) {
169169
* Maps platform-specific paths to entries.
170170
*/
171171
private final Map<String, BaseEntry> vfsEntries = new HashMap<>();
172-
173-
/**
174-
* Class used to read resources with getResource(name). By default
175-
* VirtualFileSystem.class.
176-
*/
177-
private Class<?> resourceLoadingClass;
172+
173+
/**
174+
* Classloader used to read resources. By defaut, the classloader of
175+
* VirtualFileSystem.class.
176+
*/
177+
private final ClassLoader resourceClassLoader;
178178

179179
static final String PLATFORM_SEPARATOR = Paths.get("").getFileSystem().getSeparator();
180180
private static final char RESOURCE_SEPARATOR_CHAR = '/';
@@ -310,23 +310,25 @@ private void removeExtractDir() {
310310
* argument may be {@code null} causing that no extraction will happen.
311311
*/
312312
VirtualFileSystemImpl(Predicate<Path> extractFilter, Path mountPoint, String resourceDirectory, HostIO allowHostIO,
313-
Class<?> resourceLoadingClass, boolean caseInsensitive) {
314-
if (resourceLoadingClass != null) {
315-
this.resourceLoadingClass = resourceLoadingClass;
316-
} else {
317-
this.resourceLoadingClass = VirtualFileSystem.class;
318-
}
313+
ClassLoader resourceClassLoader, boolean caseInsensitive) {
314+
if (resourceClassLoader != null) {
315+
this.resourceClassLoader = resourceClassLoader;
316+
} else {
317+
this.resourceClassLoader = VirtualFileSystem.class.getClassLoader();
318+
}
319319
this.caseInsensitive = caseInsensitive;
320320
this.mountPoint = mountPoint;
321321
this.mountPointLowerCase = mountPoint.toString().toLowerCase(Locale.ROOT);
322322
this.vfsRoot = resourceDirectory == null ? DEFAULT_VFS_ROOT : resourceDirectory;
323323
this.platformVenvPath = resourcePathToPlatformPath(absoluteResourcePath(vfsRoot, VFS_VENV));
324324
this.platformSrcPath = resourcePathToPlatformPath(absoluteResourcePath(vfsRoot, VFS_SRC));
325325

326-
fine("VirtualFilesystem %s, allowHostIO: %s, resourceLoadingClass: %s, caseInsensitive: %s, extractOnStartup: %s%s",
327-
mountPoint, allowHostIO.toString(), this.resourceLoadingClass.getName(), caseInsensitive,
328-
extractOnStartup, extractFilter != null ? "" : ", extractFilter: null");
329-
326+
if (LOGGER.isLoggable(Level.FINE)) {
327+
var classLoaderLabel = this.resourceClassLoader == VirtualFileSystem.class.getClassLoader() ? "VirtualFileSystem" : "custom";
328+
fine("VirtualFilesystem %s, allowHostIO: %s, resourceClassLoader: %s, caseInsensitive: %s, extractOnStartup: %s%s",
329+
mountPoint, allowHostIO.toString(), classLoaderLabel, caseInsensitive,
330+
extractOnStartup, extractFilter != null ? "" : ", extractFilter: null");
331+
}
330332
this.extractFilter = extractFilter;
331333
if (extractFilter != null) {
332334
try {
@@ -670,7 +672,7 @@ public boolean equals(Object obj) {
670672
private List<URL> getFilelistURLs(String filelistPath) {
671673
List<URL> filelistUrls;
672674
try {
673-
filelistUrls = Collections.list(this.resourceLoadingClass.getClassLoader().getResources(filelistPath));
675+
filelistUrls = Collections.list(resourceClassLoader.getResources(filelistPath));
674676
} catch (IOException e) {
675677
throw new IllegalStateException("IO error during reading the VirtualFileSystem metadata", e);
676678
}
@@ -746,7 +748,7 @@ private void validateMultipleVFSLocations(List<URL> filelistUrls) {
746748
ArrayList<URL> installedUrls;
747749
try {
748750
installedUrls = Collections.list(
749-
this.resourceLoadingClass.getClassLoader().getResources(resourcePath(vfsRoot, INSTALLED_FILE)));
751+
resourceClassLoader.getResources(resourcePath(vfsRoot, INSTALLED_FILE)));
750752
} catch (IOException e) {
751753
warn("Cannot check compatibility of the merged virtual environments. Cannot read list of packages installed in the virtual environments. IOException: "
752754
+ e.getMessage());
@@ -791,7 +793,7 @@ private void validateMultipleVFSLocations(List<URL> filelistUrls) {
791793
ArrayList<URL> contentsUrls;
792794
try {
793795
contentsUrls = Collections.list(
794-
this.resourceLoadingClass.getClassLoader().getResources(resourcePath(vfsRoot, CONTENTS_FILE)));
796+
resourceClassLoader.getResources(resourcePath(vfsRoot, CONTENTS_FILE)));
795797
} catch (IOException e) {
796798
warn("Cannot check compatibility of the merged virtual environments. Cannot read GraalPy version of the virtual environments. IOException: "
797799
+ e.getMessage());
@@ -831,7 +833,7 @@ private void validateMultipleVFSLocations(List<URL> filelistUrls) {
831833
}
832834

833835
private URL getResourceUrl(String path) throws IOException {
834-
List<URL> urls = Collections.list(this.resourceLoadingClass.getClassLoader().getResources(path.substring(1)));
836+
List<URL> urls = Collections.list(resourceClassLoader.getResources(path.substring(1)));
835837
if (vfsRootURL != null) {
836838
urls = getURLInRoot(urls);
837839
}

0 commit comments

Comments
 (0)