Am 31.03.2014 um 21:25 schrieb Tudor Girba <tu...@tudorgirba.com>:
> Now, the next question. I saw that it is possible to enable callbacks, but I 
> could not find an example of how it works.
> 
> For example, could we model this:
> 
> - in a JavaClassOriginator class we have a method like
> execute(JavaClassTargetInterface target) { target.m() }
> - In Pharo we create PharoClassTargetImplementation as an implementation of 
> JavaClassTargetInterface
> - passing an instance of PharoClassTargetImplementation to 
> JavaClassOriginator results in calling PharoClassTargetImplementation>>m
> 
> ?
> 
> 
> Cheers,
> Doru

Hi Doru,

it’s not completely straightforward, but also not really complicated. Chris 
Uppal’s JNIPort documentation contains examples, and you can also get some 
information from the test cases for callbacks. You will need the jniport.jar 
file which can be downloaded from http://jniport.wikispaces.com/Downloads (it 
is contained in JNIPort_Extras.zip). The Java classes needed for callback 
support have names starting with „Dolphin“ - they come directly from Chris’ 
implementation for Dolphin Smalltalk.

A simple example: To set up sending a simple notification without parameters to 
a Smalltalk object, you need a Java class similar to the following:

-----------------
package my.package;
import org.metagnostic.jniport.*;
public class MyClass
{
        private static final Object     notifyTag = new 
String("MyClass.sendNotification()“); 
        public static Object notifyTag () { return notifyTag; }
        public void sendNotification()
                throws RequestNotHandedException
                {
                        new DolphinNotification(notifyTag, this).send();
                }
}
-----------------

You have to install the callback in Smalltalk:

-----------------
NotificationHandler := … "the object which receives the notifications"
classStatic := jvm findClass: 'my.package.MyClass'.
jvm callbackRegistry
        setCallback: classStatic notifyTag "the tag can be an arbitrary String"
        handler: [:this :param | NotificationHandler handleNotificationFor: 
this].
-----------------

When you send sendNotification() to an instance of MyClass, NotificationHandler 
should receive the message #handleNotificationFor:.

In the example code, the tag is a static variable in MyClass. Of course, you 
can also give every instance of MyClass its own tag. In this case, you have to 
set up a callback in Smalltalk for every tag which is used. 

The example is a short version of the test code for handling notifications in 
JNIPortCallbackNotificationsTest and the corresponding Java class 
org.metagnostic.jniport.test.regression.Notifications. The source code of this 
Java class is in the archive JNIPort-Tests.zip which is contained in 
JNIPort_Extras.zip.

If you need an example for a notification which has parameters and where the 
Smalltalk code returns  a result, have a look at 
JNIPortCallbackRequestsTest>>testCallbackWithOneParameterAndReturn and the 
corresponding Java class org.metagnostic.jniport.test.regression.Callbacks.

In the Java class, you need something like this (copied from Callbacks and 
simplified) which enables a callback taking an int parameter and returning an 
instance of the Java class Integer:

         private static final Object callbackTag = new String(„callback with 
parameters"), 

        // callback with return value
        public int callbackAndReturn(int i)
        throws Throwable
        {
                Object returnValue = new DolphinRequest(callbackTag, this, 
i).value();
                return ((Integer) returnValue).intValue();
        }

A DolphinRequest is created with the tag which identifies the callback in 
Smalltalk, the object sending the callback (this), and an arbitrary number of 
parameters. For example, the following creates a DolphinRequest with two 
parameters:

                DolphinRequest dr = new DolphinRequest(
                                        s_callbackTag,
                                        this,
                                        new Integer(i),
                                        new Double(d));

Calling the value() method of the DolphinRequest sends the request to 
VisualWorks and returns the result of the Smalltalk callback block.

In Smalltalk, you can create callbacks accepting parameters like this (see 
JNIPortCallbackRequestsTest):

        "No parameters"
        jvm callbackRegistry setCallback: classStatic callbackTag 
                        handler: [:this :param | self handleCallbackFor: this].

        "One parameter"
        jvm callbackRegistry setCallback: classStatic callbackTag 
                        handler: [:this :param | self handleCallbackFor: this 
with: param].

        "Arbitrary number of parameters"
        jvm callbackRegistry setCallback: classStatic callbackTag 
                        handler: [:this :params | self handleCallbackFor: this 
withArguments: params asArray].

If the callback has a result which should be passed from Smalltalk to Java, it 
must be a Java object. Here is an example from the test class (callbacksStatus 
is just an instance variable of JNIPortCallbackRequestsTest):

handleCallbackFor: aJavaObject with: anotherJavaObject

        | jliClass |

        jliClass := jvm findClass: #'java.lang.Integer'.

        callbacksStatus addLast: (aJavaObject = instance).      "identity test 
on the Java object"
        callbacksStatus addLast: ((anotherJavaObject static = jliClass) and: 
[anotherJavaObject intValue = 1]).

        ^ jliClass new_int: 15. "Do not return a Smalltalk Integer. It must be 
a Java Integer“

Johan Brichau’s JavaConnect had a more direct way of creating callbacks, using 
Java’s dynamic proxies. However, it had an open issue, as it relied on 
different Java objects having different identity hashes, which is not true in 
all cases. JNIPort’s implementation is relatively complicated because it had to 
take into account that the callbacks can be triggered from a different thread 
than the Smalltalk thread. In VisualWorks, that’s not a problem, as the VM 
handles this, and Pharo simply does not support callbacks from foreign threads. 
So, it could be simplified in JNIPort as well.

Does this help?
Joachim


Reply via email to