RFR: 8335896: Source launcher should set TCCL
Jaikiran Pai
jpai at openjdk.org
Thu Jul 11 07:05:57 UTC 2024
On Tue, 9 Jul 2024 10:52:46 GMT, Christian Stein <cstein at openjdk.org> wrote:
> Please review this change to set the context class loader of the current thread to the in-memory class loader when the `java` launcher is invoked in source mode. Having the source launcher set the TCCL to the in-memory classloader is benefical for scenarious depending on the TCCL being set to the application-loading loader.
>
> For example, the single-argument taking [`ServiceLoader.load(Class)`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/ServiceLoader.html#load(java.lang.Class)) method creates "a new service loader for the given service type, using the current thread's [context class loader](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Thread.html#getContextClassLoader())."
Hello Christian, as far as I can see, in the current mainline, both source mode launch and the regular compiled launch, already have the Thread context classloader set to the application classloader. For example, this trivial example:
public class Foo {
public static void main(final String[] args) throws Exception {
System.out.println("Thread context classloader: " + Thread.currentThread().getContextClassLoader());
}
}
When you run it in compiled mode as follows:
javac Foo.java
java Foo
results in:
Thread context classloader: jdk.internal.loader.ClassLoaders$AppClassLoader at 18ff02e4
When running into source mode:
rm Foo.class
java Foo.java
also results in:
Thread context classloader: jdk.internal.loader.ClassLoaders$AppClassLoader at 5a07e868
As for the example noted in the JBS issue's description - I don't think it is expected to work:
public class Prog {
public static class Enclosed implements Runnable {
@Override
public void run() {
System.out.println(getClass());
}
}
public static void main(String... args) {
// Thread.currentThread().setContextClassLoader(Prog.class.getClassLoader());
java.util.ServiceLoader.load(Runnable.class).forEach(Runnable::run);
}
}
The example tries to load `Runnable` as a service. The ServiceLoader mechanism expects services to deployed either in the classpath or as a module. In the classpath mechanism, the service provider is expected to be declared in a file named `META-INF/services/<service-interface>` containing a line which points to the service provider implementation class.
The JBS description also states that:
> That's why for example the following program doesn't print anything, until either the comment-out line is included or the `ServiceLoader.load(Class, ClassLoader)` variant is used with `Prog.class.getClassLoader()` as the second argument.
I was surprised that it would pick up the `Runnable` implementing classes dynamically as services. So I uncommented that commented line and ran the program (in source launch mode). It doesn't locate the `Runnable` implementation as a service and continues to not print anything. Same with passing `Prog.class.getClassLoader()` as the second argument to the `ServiceLoader.load(...)` call - the `Runnable` doesn't get located. This behaviour is understandable and as expected.
Would you happen to have the additional details about the setup (and JDK version?) where that example ends up returning a Runnable class as a service provider and prints some output?
-------------
PR Comment: https://git.openjdk.org/jdk/pull/20097#issuecomment-2222186891
More information about the compiler-dev
mailing list