Hi Stephen,
On 09/09/2016 04:30 PM, Stephen Felts wrote:
We have an application that is running into a problem with a utility program.
Below is a standalone reproducer.
The program does not import the SPI package sun.nio.ch - it isn't aware of
it, and SocketChannel.isConnected() is a public method of a public type. In
short, it does not break any law of encapsulation, so call
setAccessible(true) should be OK.
Ok, but...
import java.lang.reflect.Method;
public class JDK9Nio {
public static void main(String args[]) throws Exception {
call();
}
public static void call() throws Exception {
Class clzz = Class.forName("java.nio.channels.SocketChannel");
Method open = clzz.getMethod("open");
Object obj = open.invoke(null);
Method isConn = obj.getClass().getMethod("isConnected");
...This is a classical reflection anti-pattern. What program should be
doing to call the public and exported SocketChannel.isConnected() method
is the following:
Method isConn = SocketChannel.class.getMethod("isConnected");
obj.getClass().getMethod(...) is rarely what is desired. What you get
back is a Method object for method declared in a package-private class.
That's why setAccessible() was needed. And now in JDK 9, this class is
also in a non-exported package, so setAccessible() does not help any
more. But I see your point. It worked before and is not working any more...
isConn.setAccessible(true); // OK with JDK8, fail with JDK9
System.out.println(isConn.invoke(obj));
}
}
java JDK9Nio
Exception in thread "main" java.lang.reflect.InaccessibleObjectException:
Unable to make member of class sun.nio.ch.SocketChannelImpl accessible: module java.base
does not export sun.nio.ch to unnamed module @3857f613
at
jdk.internal.reflect.Reflection.throwInaccessibleObjectException(java.base@9-ea/Reflection.java:414)
at
java.lang.reflect.AccessibleObject.checkCanSetAccessible(java.base@9-ea/AccessibleObject.java:174)
at
java.lang.reflect.Method.checkCanSetAccessible(java.base@9-ea/Method.java:192)
at
java.lang.reflect.Method.setAccessible(java.base@9-ea/Method.java:186)
at JDK9Nio.call(JDK9Nio.java:14)
at JDK9Nio.main(JDK9Nio.java:6)
It's easy to say that the program should be re-written and the setAccessible is
not necessary but this is a utility program over which the application has no
control (a jython script).
What's ugly is that the internal implementation is showing through to the
application.
Maybe there could be a solution in supporting such sloppy programming
though. If the reflective invocation is performed to a virtual method,
then the JVM virtual dispatch chooses the method declared in the most
specific class regardless of what the Method object used is telling
about the method's declaring class. So if there exists at least one
matching method declared in the hierarchy of types comprising the
runtime type of the object upon which the method is being invoked, and
such method is accessible to the invoker, such invocation could be
allowed. The rationale is simple: if the invocation dispatches to the
same method for distinct Method objects, it should also succeed or not
succeed consistently regardless of which Method object was used to
perform the invocation.
But such access check would surely be much slower. It's better to just
fix the program (if you can :-( ).
Regards, Peter
Many people especially tool makers have this problem:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=482318
https://netbeans.org/bugzilla/show_bug.cgi?id=258952
https://netbeans.org/bugzilla/show_bug.cgi?id=262765
http://mail.openjdk.java.net/pipermail/quality-discuss/2015-November/000468.html
https://community.oracle.com/thread/3937249