Skip to content

Revise ClassLoader lookups across the platform #806

Open

Description

Overview

Although current use of ClassLoaderUtils.getDefaultClassLoader() works for the most common class loading scenarios, it is not always appropriate to default to using the thread context ClassLoader (TCCL) when loading user types via reflection.

This issue is inspired by the issues discussed in #805 and serves as an umbrella issue for potential class loading issues across the platform.

Related Issues

Comparison to class loading in Spring

If we take inspiration from the Spring Framework, we can see that org.springframework.util.ClassUtils.forName(String, ClassLoader) accepts a user-supplied ClassLoader and falls back to the "default ClassLoader" only if the supplied ClassLoader is null. For loading services, Spring does not use Java's ServiceLoader mechanism but rather its own SpringFactoriesLoader which performs the same task as Java's ServiceLoader but with explicit control over which ClassLoader is used. Note that SpringFactoriesLoader delegates to org.springframework.util.ClassUtils.forName(String, ClassLoader) to actually load the service class.

Initial Analysis

JUnit does not necessarily need the flexibility of SpringFactoriesLoader, but for certain scenarios we will likely have to supply a custom ClassLoader instead of relying on the "default ClassLoader" which currently is typically the thread context ClassLoader (TCCL).

Supplying getClass().getClassLoader() is likely a viable option. However, we should not change the implementation of ClassLoaderUtils.getDefaultClassLoader(). Rather, we should simply use it less often and then only as a fallback.

FWIW, we actually did have the foresight to create org.junit.platform.commons.util.ReflectionUtils.loadClass(String, ClassLoader), but... we never supply it anything other than the default ClassLoader. 😉

Use Cases to Investigate

The following use cases should be investigated to determine how best to look up the ClassLoader to use.

  • TestEngine loading in ServiceLoaderTestEngineRegistry
  • TestExecutionListener loading in ServiceLoaderTestExecutionListenerRegistry
  • Jupiter Extension loading in ExtensionRegistry#registerAutoDetectedExtensions
  • Additional classpath entries in ConsoleTestExecutor#createCustomClassLoader
  • ClasspathScanner configuration/instantiation in ReflectionUtils
  • ReflectionUtils#loadClass(String)

Deliverables

  • For each of the aforementioned Use Cases to Investigate, determine how best to look up the ClassLoader.
  • Use the ClassLoader for the current framework class (e.g., via getClass().getClassLoader()) instead of ClassLoaderUtils.getDefaultClassLoader() where appropriate.
  • Ensure that tools have the ability to set the thread context ClassLoader (TCCL) where necessary.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions