JDK 22+ regression: IgniteUtils.forceEmptyConstructor(List.class) fails with UnsupportedOperationException (JDK‑8315811)
- Dominant language
- Java
- Stars
- 5.1k
- Forks
- 1.9k
- Avg merge
- 3d 2h
- Merged PRs (30d)
- 46
Description
### Affected versions
Ignite: I tested with 2.17.0
JDK: 22+
### Steps to reproduce
Run the following minimal program on JDK 22 with Ignite on the classpath and with `--add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED`:
```
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.util.IgniteUtils;
import java.lang.reflect.Constructor;
import java.util.List;
public class Main {
public static void main(String[] args) throws IgniteCheckedException {
Constructor c = IgniteUtils.forceEmptyConstructor(List.class);
System.out.println(c);
}
}
```
With JDK 21, the following would get printed: `public java.lang.Object()`
With JDK 22+, this exception is raised:
```
Exception in thread "main" class org.apache.ignite.IgniteCheckedException: Failed to get object constructor for class: interface java.util.List
at org.apache.ignite.internal.util.IgniteUtils.forceEmptyConstructor(IgniteUtils.java:1691)
at org.example.Main.main(Main.java:15)
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.apache.ignite.internal.util.IgniteUtils.forceEmptyConstructor(IgniteUtils.java:1688)
... 1 more
Caused by: java.lang.UnsupportedOperationException: public java.lang.Object() not a superclass of java.util.List
at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.newSerializableConstructorAccessor(MethodHandleAccessorFactory.java:122)
at java.base/jdk.internal.reflect.ReflectionFactory.generateConstructor(ReflectionFactory.java:389)
at java.base/jdk.internal.reflect.ReflectionFactory.newConstructorForSerialization(ReflectionFactory.java:288)
at jdk.unsupported/sun.reflect.ReflectionFactory.newConstructorForSerialization(ReflectionFactory.java:100)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
... 3 more
```
### Root cause
JDK 22 reimplemented sun.reflect.ReflectionFactory::newConstructorForSerialization using method handles (JDK‑8315810 / CSR JDK‑8315811). As part of that change, the JDK now fails fast and throws UnsupportedOperationException when newConstructorForSerialization(C, ctor) is invoked with a ctor whose declaring class is not a superclass of C. This is precisely what happens when C is an interface such as java.util.List: Object is not considered a superclass of an interface, hence the failure.
- CSR note: The only behavioral change is the error case when `newConstructorForSerialization(C.class, ctor)` is called with a constructor whose declaring class is not a superclass of `C`. The old implementation returns an ill-formed constructor and if `newInstance` is invoked, the behavior is unspecified. The new implementation of ReflectionFactory::newConstructorForSerialization throws `UnsupportedOperationException` instead to fail fast.
Is this something that should be handled on ignite's end? Can we add a check on the class type to see if it's an interface first?
Contributor guide
Research direction
Start with IgniteUtils.forceEmptyConstructor in IgniteUtils.java at the stack-trace locations around lines 1688-1691, then run the provided Main program on JDK 22+ and compare it with JDK 21. Trace how the interface argument reaches ReflectionFactory.newConstructorForSerialization; done means the JDK 22+ regression is handled consistently for java.util.List and the reported failure is covered or verified.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100