Using java.awt.Toolkit.getDefaultToolkit().getScreenSize() reflectively causes InaccessibleObjectException
Peter Levart
peter.levart at gmail.com
Sat Jan 7 19:46:13 UTC 2017
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