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

Rony G. Flatscher Rony.Flatscher at wu.ac.at
Sat Jan 7 20:51:22 UTC 2017


Hi Peter,

again, thank you very much! I have marked your idea/suggestion in order to be able to get back to it
when I get free time on my hands to test and experiment with new alternatives. In this case it will
be interesting to learn about its runtime effects for users who have long-running Rexx scripts in
place (some "end-user" kind of experts run self-written Rexx programs that get invoked by Java
programs wading and analyzing huge amount of data, sometimes such programs may execute for days and
even more).

Regards,

---rony


On 07.01.2017 21:08, Peter Levart wrote:
> 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
>>>>>
>>>>
>>>
>>
>

-- 
--
__________________________________________________________________________________

Prof. Dr. Rony G. Flatscher
Department Informationsverarbeitung und Prozessmanagement
Institut für Betriebswirtschaftslehre und Wirtschaftsinformatik
D2-C 2.086
WU Wien
Welthandelsplatz 1
A-1020  Wien/Vienna, Austria/Europe

http://www.wu.ac.at
__________________________________________________________________________________







More information about the jigsaw-dev mailing list