Using java.awt.Toolkit.getDefaultToolkit().getScreenSize() reflectively causes InaccessibleObjectException

Peter Levart peter.levart at gmail.com
Sat Jan 7 20:08:03 UTC 2017


Hi Rony (sent privately to not bother the list with this stuff). Here's 
a modified ReturnTypesList that also keeps track of all the distinct 
methods that can be called:

public class ReturnTypesList extends CopyOnWriteArrayList<Class<?>> {

     private final Object lock = new Object();

     private volatile List<Method> methods = Collections.emptyList();

     public List<Method> getMethods() {
         return methods;
     }

     public void merge(Class<?> newType) {
         for (Class<?> type : this) {
             if (newType.isAssignableFrom(type)) {
                 // already have the same or more specific type
                 return;
             }
         }
         // we need to serialize access when modifying
         synchronized (lock) {
             // re-check under lock as the list might have been modified
             for (Class<?> type : this) {
                 if (newType.isAssignableFrom(type)) {
                     // already have the same or more specific type
                     return;
                 }
             }
             // add newType 1st as it is the most specific type among 
related ones
             // this will make list appear to contain related types for 
a brief moment
             // bu that's not a problem.
             add(newType);
             // construct new list of methods and remove the less 
specific related types
             List<Method> newMethods = new ArrayList<>();
             Iterator<Class<?>> iter = iterator();
             while (iter.hasNext()) {
                 Class<?> type = iter.next();
                 if (type != newType && type.isAssignableFrom(newType)) {
                     // current type is less specific than newType -> 
remove it
                     iter.remove();
                 } else {
                     // current type is unrelated to newType or equal to 
newType
                     // -> collect its methods
newMethods.addAll(Arrays.asList(type.getMethods()));
                 }
             }
             // publish newMethods
             this.methods = newMethods;
         }
     }
}


So all you have to do is call 
ReturnTypesList.merge(method.getReturnType()) every time you (re)-visit 
the registered object returned from the method. When you search for 
appropriate method to call on the object, use 
ReturnTypesList.getMethods() to search for most appropriate one - this 
method returns a list of unique methods (no duplicates).

Regards, Peter

On 01/07/2017 08:54 PM, Peter Levart wrote:
> Hi Rony,
>
> Implementing explicit cast is easy in this scheme (the Java side):
>
> public static void cast(Object object, ReturnTypesList rtList, 
> Class<?> typeToCastTo) throws ClassCastException {
>     typeToCastTo.cast(object);
>     rtList.merge(typeToCastTo);
> }
>
> Peter
>
> On 01/07/2017 08:46 PM, Peter Levart wrote:
>> Hi Rony,
>>
>> As with all concurrent data structures that try to optimize 
>> something, you can get it wrong in the first try. Here's the 
>> corrected code:
>>
>> public class ReturnTypesList extends CopyOnWriteArrayList<Class<?>> {
>>
>>     private final Object lock = new Object();
>>
>>     public void merge(Class<?> newType) {
>>         for (Class<?> type : this) {
>>             if (newType.isAssignableFrom(type)) {
>>                 // already have the same or more specific type
>>                 return;
>>             }
>>         }
>>         // we need to serialize access when modifying
>>         synchronized (lock) {
>>             // re-check under lock as the list might have been modified
>>             for (Class<?> type : this) {
>>                 if (newType.isAssignableFrom(type)) {
>>                     // already have the same or more specific type
>>                     return;
>>                 }
>>             }
>>             // add newType 1st as it is the most specific type among 
>> related ones
>>             // this may make list appear to contain related types for 
>> a brief moment
>>             // but that's not a problem if the logic that looks up 
>> methods can cope
>>             // with it (it might find duplicate methods)
>>             add(newType);
>>             // 2nd remove the less specific related types
>>             Iterator<Class<?>> iter = iterator();
>>             while (iter.hasNext()) {
>>                 Class<?> type = iter.next();
>>                 if (type != newType && type.isAssignableFrom(newType)) {
>>                     // newType is more specific -> remove less 
>> specific one
>>                     iter.remove();
>>                 }
>>             }
>>         }
>>     }
>> }
>>
>> Regards, Peter
>>
>> On 01/07/2017 08:33 PM, Peter Levart wrote:
>>>
>>>
>>> On 01/07/2017 07:25 PM, Rony G. Flatscher wrote:
>>>>
>>>> Hi Peter,
>>>>
>>>> thank you again for your effort, I really appreciate it!
>>>>
>>>> However, as you note at the end yourself, the problem is that any 
>>>> Java object could be used concurrently in different usages of the 
>>>> Java bridge, so saving the last return type with the returned 
>>>> object is not feasible.
>>>>
>>>
>>> I'm not suggesting that. Saving all most specific non-related return 
>>> types is what would be needed and then using them all in sequence to 
>>> search for method(s).
>>>
>>>> E.g. each new instance of a javax.script.RexxEngine creates a new 
>>>> Rexx interpreter instance. Each Rexx interpreter instance allows 
>>>> any number of Rexx threads to run concurrently and it is possible 
>>>> to use the Java bridge from any of these Rexx threads concurrently 
>>>> and use the (identical) Java object in different use cases (e.g. 
>>>> having different Event handlers implemented in Rexx serving at the 
>>>> same time different Java interfaces). Add to this the possibility 
>>>> that the same is possible at the Java side, where (the same or 
>>>> different) RexxEngines could get exercised in different Java threads.
>>>>
>>>
>>> No problem. What you need is a special concurrent collection 
>>> implementation that keeps all the most specific method return types 
>>> you add to it which are unrelated. When types are related, you just 
>>> keep the most specific one. Like this:
>>>
>>> public class ReturnTypesList extends CopyOnWriteArrayList<Class<?>> {
>>>
>>>     private final Object lock = new Object();
>>>
>>>     public void merge(Class<?> newType) {
>>>         for (Class<?> type : this) {
>>>             if (newType.isAssignableFrom(type)) {
>>>                 // already have the same or more specific type
>>>                 return;
>>>             }
>>>         }
>>>         // we need to serialize access when modifying
>>>         synchronized (lock) {
>>>             Iterator<Class<?>> iter = iterator();
>>>             while (iter.hasNext()) {
>>>                 Class<?> type = iter.next();
>>>                 // re-check under lock as the list might have been 
>>> modified
>>>                 if (newType.isAssignableFrom(type)) {
>>>                     // already have the same or more specific type
>>>                     return;
>>>                 }
>>>                 if (type != newType && type.isAssignableFrom(newType)) {
>>>                     // newType is more specific -> remove less 
>>> specific one
>>>                     iter.remove();
>>>                 }
>>>             }
>>>             // newType is most specific
>>>             add(newType);
>>>         }
>>>     }
>>> }
>>>
>>> ...use merge(method.getReturnType()) to keep the list of most 
>>> specific return types updated - mostly the type will already be 
>>> found in the list and the first for loop will bail out without any 
>>> modification or synchronization, so this is quite scalable. When you 
>>> search for method, iterate the ReturnTypesList registered with the 
>>> object and collect all the methods you find on all types in the list 
>>> to select the most appropriate. I think you will find that most 
>>> objects will register a single method's return type. There will be 
>>> rare occasions where multiple types will be registered.
>>>
>>> Regards, Peter
>>>
>>>> ---rony
>>>>
>>>>
>>>> On 07.01.2017 19:16, Peter Levart wrote:
>>>>> Hi Rony,
>>>>>
>>>>> On 01/07/2017 03:53 PM, Rony G. Flatscher wrote:
>>>>>>
>>>>>> Hi Peter,
>>>>>>
>>>>>> thank you very much for your efforts!
>>>>>>
>>>>>> However, in this context there is a problem at hand, that there 
>>>>>> is no information available what Java method returned what object 
>>>>>> and what cast was carried out, if any. To understand this, maybe 
>>>>>> I should give a little bit more information about the Rexx-Java 
>>>>>> bridge: Rexx/ooRexx (originally developed by IBM, now in 
>>>>>> opensource) is an interpreter for a dynamically typed, caseless 
>>>>>> programming language with a rather easy to learn syntax, yet 
>>>>>> powerful implemented concepts. ooRexx is implemented in C++.
>>>>>>
>>>>>> The Rexx-Java-bridge uses JNI and a Java package (for ooRexx 
>>>>>> programmers it is an external function package called BSF4ooRexx, 
>>>>>> which allows to camouflage all of Java as the dynamically typed, 
>>>>>> caseless ooRexx). It is possible with this package to create Rexx 
>>>>>> proxy objects for Java objects (and the other way around as 
>>>>>> well). This is realized by storing proxied Java objects on the 
>>>>>> Java side in a Map ("registry") and using a common (unique) 
>>>>>> string value as the key.
>>>>>>
>>>>>> So when the Rexx side invokes a Java method, briefly the 
>>>>>> following steps take place (there is much more to this, but not 
>>>>>> important in this context):
>>>>>>
>>>>>>   * the Rexx side uses JNI and supplies the string identifying
>>>>>>     the Java object in the Map, the method name in uppercase
>>>>>>     (caselessness is realized in Rexx by uppercasing all Rexx
>>>>>>     tokens outside of quotes) and the arguments, if any,
>>>>>>
>>>>>>   * the Java side fetches the Java object from the Java registry
>>>>>>     and inspects it for its available methods, picks those that
>>>>>>     have caselessly the same name as the supplied method name,
>>>>>>     then checks whether the arguments are type-compatible and
>>>>>>     invokes the method; any returned Java object will be placed
>>>>>>     in the Java "registry" and its key (a unique string) is
>>>>>>     returned to Rexx.
>>>>>>
>>>>>
>>>>> Couldn't you save also the method's return type besides the result 
>>>>> under the same key into the registry, so next time you have to 
>>>>> invoke a method on such object, you retrive the object and the 
>>>>> type you use to find methods on?
>>>>>
>>>>>> So after returning control to Rexx, there is no information 
>>>>>> available about the Java object in the Java registry other than 
>>>>>> the string serving as the key to fetch that Java object on the 
>>>>>> Java side.
>>>>>>
>>>>>
>>>>> Not on the Rexx side, but on the Java side in the registry. Right 
>>>>> where you need it, right?
>>>>>
>>>>>> Take this Rexx code as an example (the tilde is the message 
>>>>>> operator in ooRexx and can have white space around it):
>>>>>>
>>>>>>     clzToolkit = bsf.import("java.awt.Toolkit")
>>>>>>     dim = clzToolkit ~getDefaultToolkit ~getScreenSize
>>>>>>
>>>>>> will be transformed internally by Rexx into:
>>>>>>
>>>>>>     CLZTOOLKIT=BSF.IMPORT("java.awt.Toolkit")
>>>>>>     DIM=CLZTOOLKIT~GETDEFAULTTOOLKIT~GETSCREENSIZE
>>>>>>
>>>>>> and the Java bridge gets used (via JNI) as follows:
>>>>>>
>>>>>>   * step 1: BSF.IMPORT() is an external Rexx function that will
>>>>>>     use JNI and cause a Java class object to be loaded (and
>>>>>>     stored in the Java registry) and boxed as an ooRexx proxy
>>>>>>     class object upon return and assigned to the Rexx variable
>>>>>>     CLZTOOLKIT,
>>>>>>
>>>>>
>>>>> This time the 'type' to search methods on is the same as the class 
>>>>> object you just "imported".
>>>>>
>>>>>>  *
>>>>>>
>>>>>>
>>>>>>   * step 2: the CLZTOOLKIT~GETDEFAULTTOOLKIT statement contains a
>>>>>>     Rexx message that will cause JNI to be used and the Java
>>>>>>     method GETDEFAULTTOOLKIT to be executed for the Java object
>>>>>>     referenced by CLZTOOLKIT (which incorporates the unique
>>>>>>     string for that proxied Java class object); the returned Java
>>>>>>     object will be stored in the Java registry, its unique key (a
>>>>>>     string) returned, boxed as an ooRexx proxy object value which
>>>>>>     will be the receiver of the next Rexx message,
>>>>>>
>>>>>
>>>>> Right and if you also save the return type of the method you just 
>>>>> called into the registry besides the returned object on the Java 
>>>>> side, you can use it later...
>>>>>
>>>>>>  *
>>>>>>
>>>>>>
>>>>>>   * step 3: the returned value gets the GETSCREENSIZE Rexx
>>>>>>     message sent to it causing JNI to be used and the Java method
>>>>>>     GETSCREENSIZE to be located and executed for the Java object
>>>>>>     returned from the previous step;
>>>>>>
>>>>>
>>>>> The method should then use the saved method return type from 
>>>>> previous step for looking up the GETSCREENSIZE method...
>>>>>
>>>>>>   * the returned Java object will be stored in the Java registry,
>>>>>>     its unique key (a string) returned, boxed as an ooRexx proxy
>>>>>>     object value that gets assigned to the Rexx variable DIM.
>>>>>>
>>>>>
>>>>> You should then store the getScreenSize() method's return type 
>>>>> besides the returned object under the key... You see the pattern?
>>>>>
>>>>>>  *
>>>>>>
>>>>>>
>>>>>> Each step gets carried out contextless, i.e. there is no Java 
>>>>>> context available, that we (or the Java compiler) can see/infer 
>>>>>> when looking at a Java program.
>>>>>>
>>>>>
>>>>> When you invoke them method you not only store the returned object 
>>>>> but also the method's return type.
>>>>>
>>>>>
>>>>> If you need casting, then this would need to be explicit (like in 
>>>>> Java).
>>>>>
>>>>> There's one problem with this scheme. What is the key you use to 
>>>>> register returned object? Is it based on object identity? When 
>>>>> methods return the same instance, is it saved under the same key? 
>>>>> If yes, which is understandable, then there might be a problem 
>>>>> when two methods with different return types return the same 
>>>>> instance. Which return type should you use to find methods for 
>>>>> following invocations then? Maybe the most specific type (if they 
>>>>> are related) or both (all) of them if they are not and then use 
>>>>> them all to search for methods.
>>>>>
>>>>> Regards, Peter
>>>>>
>>>>>> ---rony
>>>>>>
>>>>>> P.S.: Also it might be interesting to know, that with that same 
>>>>>> Rexx-Java bridge it is possible to implement Java methods from 
>>>>>> interface or abstract classes in Rexx! In that case there is a 
>>>>>> Java proxy class available for proxying Rexx objects and on the 
>>>>>> Rexx side there is a Rexx Directory to maintain the proxied Rexx 
>>>>>> objects for their Java proxies. Fun stuff! :)
>>>>>>
>>>>>>
>>>>>>
>>>>>> On 06.01.2017 23:22, Peter Levart wrote:
>>>>>>> Hi Rony,
>>>>>>>
>>>>>>> On 01/06/2017 02:28 PM, Rony G. Flatscher wrote:
>>>>>>>>> >The j.l.r.Method object on which you call invoke() should not be obtained by inspecting the
>>>>>>>>> >methods of the implementation class given by getDefaultToolkit().getClass(). Implementation
>>>>>>>>> >classes (i.e. classes in non-exported packages) cannot be instantiated, nor their members
>>>>>>>>> >manipulated, by code outside their module.
>>>>>>>>> >
>>>>>>>>> >The j.l.r.Method object on which you call invoke() should be obtained by inspecting the methods of
>>>>>>>>> >the "public Java class" java.awt.Toolkit. The first argument that you pass to invoke(), indicating
>>>>>>>>> >the receiver, can still be instanceof the implementation class.
>>>>>>>> As was noted earlier, the information that some Java object xyz was created by some public method
>>>>>>>> "getDefaultToolkit()" and hence knowing that its return type would be that of the java.desktop
>>>>>>>> public class java.awt.Toolkit is not available at runtime.
>>>>>>>
>>>>>>> But it is. The method Toolkit.getDefaultToolkit() has a return 
>>>>>>> type. You can use reflection to find out that return type of 
>>>>>>> that method:
>>>>>>>
>>>>>>> Method getDefKitMeth = Toolkit.class.getMethod("getDefaultToolkit");
>>>>>>> Class<?> tkClass = getDefKitMeth.getReturnType();
>>>>>>>
>>>>>>> // now you can obtain the toolkit instance:
>>>>>>> Object tkInst = getDefKitMeth.invoke(null);
>>>>>>>
>>>>>>> // and obtain a method to be called upon it
>>>>>>> Method getScrSizMeth = tkClass.getMethod("getScreenSize");
>>>>>>>
>>>>>>> // and invoke it:
>>>>>>> Object screenSize = getScrSizMeth.invoke(tkInst);
>>>>>>>
>>>>>>> ... and so on...
>>>>>>>
>>>>>>>
>>>>>>> You see, I never had to mention java.awt.Toolkit type explicitly 
>>>>>>> to invoke getScreenSize on an object of that type (or subtype). 
>>>>>>> If you think what a programmer does when he codes this in 
>>>>>>> straight Java without using reflection, it is the following:
>>>>>>>
>>>>>>> 1. He finds out a factory method on Toolkit class: 
>>>>>>> Toolkit.getDefaultToolkit()
>>>>>>> 2. He looks up the return type of that method (in javadocs).
>>>>>>> 3. He uses that type to declare a local variable to which it 
>>>>>>> assigns the result of the method invocation:
>>>>>>>
>>>>>>> java.awt.Toolkit tkInst = java.awt.Toolkit.getDefaultToolkit();
>>>>>>>
>>>>>>> 4. He looks up and finds an instance method to call in type 
>>>>>>> java.awt.Toolkit: java.awt.Toolkit.getScreenSize() and writes it 
>>>>>>> down:
>>>>>>>
>>>>>>> tkInst.getScreenSize();
>>>>>>>
>>>>>>> Above invocation is using static type java.awt.Toolkit - the 
>>>>>>> return type of Toolkit.getDefaultToolkit().
>>>>>>>
>>>>>>> You can do similar things with reflection. Instead of using 
>>>>>>> anInstance.getClass() to get the runtime class of the instance, 
>>>>>>> you can use Method.getReturnType() of the method that was used 
>>>>>>> to obtain the instance. If API is designed so that no casts are 
>>>>>>> needed when you chain calls, then this should work.
>>>>>>>
>>>>>>>
>>>>>>> Regards, Peter
>>>>
>>>
>>
>



More information about the jigsaw-dev mailing list