apache / apache/streampark

[Bug] ClassLoaderUtils.runAsClassLoader restores a stale context classloader captured at class-init time

Open Beginner friendly
#4,489 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
4.3k
Forks
1.1k
Avg merge
13h 35m
Merged PRs (30d)
2

Description

### Search before asking

- [X] I had searched in the [issues](https://github.com/apache/streampark/issues?q=is%3Aissue+label%3A%22bug%22) and found no similar issues referencing `ClassLoaderUtils` / `runAsClassLoader`.

### Java Version

Temurin 21.0.11 (console), built with Microsoft OpenJDK 11.0.28

### Scala Version

2.12.x

### StreamPark Version

3.0.0-SNAPSHOT (`dev` branch, commit `9ddda84c9`)

### Flink Version

1.20.4 and 2.2.1 (official binary distributions, standalone/remote cluster)

### Deploy mode

remote

### What happened

`ClassLoaderUtils.runAsClassLoader(target, supplier)` is meant to run a block under a given classloader and then put the calling thread back the way it found it. It does not: the `finally` restores `ORIGINAL_CLASS_LOADER`, a `static final` field initialised to `Thread.currentThread().getContextClassLoader()` **at class-initialisation time** — i.e. the context classloader of whichever thread first touched this class.

```java
private static final ClassLoader ORIGINAL_CLASS_LOADER = Thread.currentThread().getContextClassLoader();

public static R runAsClassLoader(ClassLoader targetClassLoader, Supplier supplier) {
try {
Thread.currentThread().setContextClassLoader(targetClassLoader);
return supplier.get();
} finally {
Thread.currentThread().setContextClassLoader(ORIGINAL_CLASS_LOADER); // <-- not the caller's
}
}
```

The method is called from shared thread-pool threads, so for any caller whose context classloader was not that same value on entry, the "restore" silently installs a *different* classloader on that thread and leaves it there. Nothing fails at the call site; the damage lands on whatever runs on that pooled thread next.

The correct behaviour is to capture the calling thread's own value on entry and restore that. `ORIGINAL_CLASS_LOADER` is still needed by `cloneClassLoader()` and stays.

This is not a regression from the recent Scala-to-Java migration — `git log -p` shows the Scala version had the same shape.

### Are you willing to submit PR?

- [X] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://www.apache.org/foundation/policies/conduct)

Contributor guide

No contributing guide indexed for this repository

Research direction

Search the repository for ClassLoaderUtils.runAsClassLoader and read its implementation, especially the context-classloader handling and the existing cloneClassLoader use of ORIGINAL_CLASS_LOADER. Verify the change with the relevant existing tests or add a focused test showing that a caller's context classloader is restored after the supplier runs.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.