Skip to content

Commit a22341d

Browse files
committedNov 27, 2023
In j.u.ServerLoader, use the platform classloader after the module system is initialized but before the VM initialization is completed. Don't use the system classloader to find resources if the loader is null when finding service provider.
There are two issues uncovered when FileSystemProvider.loadInstalledProviders() is called early during system startup before the VM is initialized. The VM is considered in booted state after System.initPhase3() completes. 1) In nextProviderClass(), when the loader is null, ClassLoader.getSystemResources() is called to find the service. The result can include service providers that can only be loaded by the system class loader, e.g. in JAR files on the -classpath. That can cause failure when the null classloader is trying to load the provider class. This issue is addressed by changing to call 'BootLoader.findResources(fullName)' instead, if the 'loader' is null. 2) When trying to load installed FileSystemProvider during early start up before the VM is booted, it fails to find the 'jar' provider. That's because the boot loader (a.k.a. the null classloader) is used by ServiceLoader, which tries to only use the code in java.base at the time. The ZipFileSystemProvider and JarFileSystemProvider are in jdk.zipfs module. The JavaHome is trying to use the JarFileSystem during initPhase3, which is after system module initialization. During that phase, it can use the platform classloader to load the installed provider, which would able to find the ZipFileSystemProvider and JarFileSystemProvider. These issues are found by hermetic Java testing, however I think these are not specific to hermetic Java.
1 parent 4e8b2eb commit a22341d

File tree

1 file changed

+11
-3
lines changed

1 file changed

+11
-3
lines changed
 

‎src/java.base/share/classes/java/util/ServiceLoader.java

+11-3
Original file line numberDiff line numberDiff line change
@@ -516,8 +516,13 @@ private ServiceLoader(Class<?> caller, Class<S> svc, ClassLoader cl) {
516516
fail(svc, "not accessible to " + callerModule + " during VM init");
517517
}
518518

519-
// restricted to boot loader during startup
520-
cl = null;
519+
// If the module system is not initialized yet, restrict to boot
520+
// loader during startup.
521+
if (!VM.isModuleSystemInited()) {
522+
cl = null;
523+
} else {
524+
cl = ClassLoader.getPlatformClassLoader();
525+
}
521526
}
522527

523528
this.service = svc;
@@ -1186,7 +1191,10 @@ private Class<?> nextProviderClass() {
11861191
try {
11871192
String fullName = PREFIX + service.getName();
11881193
if (loader == null) {
1189-
configs = ClassLoader.getSystemResources(fullName);
1194+
// Find the resources using the boot loader. Don't use
1195+
// the system class loader here since we will load the
1196+
// provider class using the null loader below.
1197+
configs = BootLoader.findResources(fullName);
11901198
} else if (loader == ClassLoaders.platformClassLoader()) {
11911199
// The platform classloader doesn't have a class path,
11921200
// but the boot loader might.

2 commit comments

Comments
 (2)

AlanBateman commented on Nov 28, 2023

@AlanBateman
Collaborator

I don't think these changes to ServiceLoader make sense. Would it be possible to track down the service lookup that the changes now trigger during startup?

jianglizhou commented on Nov 29, 2023

@jianglizhou
CollaboratorAuthor

I don't think these changes to ServiceLoader make sense. Would it be possible to track down the service lookup that the changes now trigger during startup?

I looked into my notes on this. The related issue that I ran into on JDK 11 was a java.util.ServiceConfigurationError during VM initialization. In the particular case, java.util.ServiceLoader$LazyClassPathLookupIterator.nextProviderClass() needed to call ClassLoader.getSystemResources(fullName) to find custom provider specified by META-INF/services/java.nio.file.spi.FileSystemProvider. With the early usage of java.nio.file.FileSystems.newFileSystem() due to hermetic JavaHome changes, it triggered the FileSystemProvider.loadInstalledProviders() call path during initPhase3. During initPhase3, ServiceLoader constructor used the null class loader to load the custom provider, hence the failure. Normally (for most of tests), FileSystemProvider.loadInstalledProviders() was triggered after VM.initLevel(4) and the jdk.internal.loader.ClassLoaders$AppClassLoader instance was used for loading the FileSystemProvider, so no issue in those cases.

With the latest code base, I've also changed to delay initializing the jarFileSystem using FileSystems.newFileSystem in JavaHome. That may hide this j.u.ServerLoader issue.

Please sign in to comment.