`@AutoClose` cannot invoke methods due to strict encapsulation with the module system
bjmi opened this issue · comments
Running following test
class AutoCloseTest {
@AutoClose
InputStream inputStream = InputStream.nullInputStream();
@AutoClose("shutdown")
ExecutorService service = Executors.newSingleThreadExecutor();
@Test
void test() { }
}
results in:
java.lang.reflect.InaccessibleObjectException: Unable to make public void java.io.InputStream$1.close() throws java.io.IOException accessible: module java.base does not "opens java.io" to unnamed module @cc43f62
java.lang.reflect.InaccessibleObjectException: Unable to make public void java.util.concurrent.Executors$AutoShutdownDelegatedExecutorService.shutdown() accessible: module java.base does not "opens java.util.concurrent" to unnamed module @cc43f62
In a nutshell
InputStream.class.getMethod("close").invoke(inputStream); // works
inputStream.getClass().getMethod("close").invoke(inputStream); // fails java.lang.IllegalAccessException: class AutoCloseTest cannot access a member of class java.io.InputStream$1 (in module java.base) with modifiers "public"
Some research revealed:
- SO question https://stackoverflow.com/q/5984776 isn't really answered but a comment suggests
I have to walk the interfaces + subclasses then to find a method that works.
- in Spring Framework the same method is also searched in available interfaces: DisposableBeanAdapter
OpenJDK 21.0.2 for Windows
JUnit 5.11.0-SNAPSHOT
Thanks for bringing this to our attention, @bjmi! 👍
I'm very familiar with Spring's ClassUtils.getInterfaceMethodIfPossible()
approach. We introduced that to be compatible with the Java Module System.
So, it looks like we probably need that in JUnit as well.
I'll look into it!
Please note that I have removed the milestone assignment since @AutoClose
has not yet been released.
Just one consideration: is it sufficient to only search for a corresponding interface method?
Abstract base classes e.g. java.io.InputStream
(< Java 1.5), java.nio.Buffer
, or java.util.ResourceBundle
function in a similar way to interfaces.
Shouldn't they also be supported?
Hi @bjmi,
That's a valid question, so thanks for bringing it up.
I suppose that one might ideally want to find the first matching method which is declared in a public
type (for example, a class, abstract class, or interface).
However, my primary goal was to support the most common use cases for @AutoClose
methods which are likely one of the following.
AutoCloseable#close
- a method defined in a public interface which may potentially reside in a different module
- a method defined in the same module as the test class (or in a module that has opened itself up for reflection to the test's module) which may be invoked via reflection
In that regard, I think we have the major use cases covered currently.
However, if people start complaining about JUnit not being able to invoke @AutoClose
methods on private types in different modules where the method is actually publicly defined in a public superclass, we may consider enhancing the method search/invocation algorithm.
Cheers,
Sam