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