This is a general problem with utility programs.  This isn't, unlike the 
sample, something where I can just re-code it to use the public interface.  The 
utility program isn't checking to see what is public and what is not.  It looks 
up the class and invokes on it, creating or opening the object.  Then the 
object is used for further operations.  The class is public.  We grep'ed the 
entire code base looking for an import or dynamic reference to the internal API 
but obviously didn't find it.

This is an interpreted language where obj = clzz.open();  obj.method2() is 
invoked in a general purpose way.

 

Class clzz = Class.forName("clzz");

Method m = clzz.getMethod("open");

Object obj = m.invoke(null);

Method m2 = obj.getClass().getMethod("method2");

m.setAccessible(true);

m2.invoke(obj);

 

If this problem isn't fixed in the JDK, then I might have code that works in 
JDK 9 and is broken when someone decides to re-implement something directly 
using an internal package in JDK 10.

 

 

 

 

-----Original Message-----
From: Peter Levart [mailto:peter.lev...@gmail.com] 
Sent: Friday, September 09, 2016 11:54 AM
To: Stephen Felts; jigsaw-dev@openjdk.java.net
Subject: Re: JDK9 encapsulation problem

 

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.

> HYPERLINK "mailto:base@9-ea/Reflection.java:414"base@9-ea/Reflection.java:414)

> 

>          at 

> java.lang.reflect.AccessibleObject.checkCanSetAccessible(HYPERLINK 
> "mailto:java.base@9-e"java.base@9-e

> a/AccessibleObject.java:174)

> 

>          at 

> java.lang.reflect.Method.checkCanSetAccessible(HYPERLINK 
> "mailto:java.base@9-ea/Method.j"java.base@9-ea/Method.j

> ava:192)

> 

>          at 

> java.lang.reflect.Method.setAccessible(HYPERLINK 
> "mailto:java.base@9-ea/Method.java:186"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/0

> 00468.html

> 

> https://community.oracle.com/thread/3937249

> 

>   

> 

>   

 

 

Reply via email to