## Metagnostic home
| # JNIPort for Dolphin Smalltalk
Overview Contents Players Layers Examples Configuration InFAQ Changes Licence Back to Goodies | # Basic JNIPort The basic level of JNIPort, in package 'CU Java Base', uses the raw JNI functions exposed via package 'CU JNI' and builds a framework for manipulating Java objects and classes via Smalltalk proxies. The proxies for Java objects don't have a special name in JNIPort, I tend to refer to them as [instance] wrappers, or occasionally as proxies. They are instances of subclasses of `JavaInstance`. The “proxies” for Java classes are called class statics; of course they aren't really proxies, in the normal sense of the word, because there is no object in Java that they are a proxy for. Still, they provide an Facade that exposes the static members of Java classes as if they were attached to some object (which, in fact, they probably are — hidden away inside the Java runtime somewhere). They are instances of subclasses of `JavaStatic`. For more information on instance wrappers and class statics, see the description of the main players of JNIPort. For an example of using this level of JNIPort, see here . ## Class `JavaObject` All JNIPort proxies, both for the class- and instance-sides, are subclassed from `JavaObject`. This class provides a uniform framework for calling methods, and accessing Java fields, (collectively called the “members”) of a Java class. All these objects are owned by a single JVM object, which stands for the Java runtime. Instances respond to the `#jvm` message that, unsurprisingly, answers that object. The JVM object has some configuration options that determine, for instance, which Java runtime it will start, and what options it will pass to that runtime. The JVM object's role is also described in the Players section. Instances obviously depend on the Java runtime; as long as the runtime is, er, running they are considered to be “live”; after the runtime has shut down, any surviving instances will be “dead” (as will their owning JVM). When you save and restart the image, the Java runtime is not restarted, and so all the Smalltalk objects that referred to it become dead. The simplest way to configure and start/stop Java runtimes is via the GUI tool, the Status Monitor. The remainder of this sub-section discusses the common features of all `JavaObjects`. ### Reading and Writing Java Fields You can safely skip this sub-section if you are using ghost classes or the Wrapper Wizard. Unfortunately, due to the way that Java/JNI work, you have to use different methods to access a Java field depending on its type. There are specific methods for each of Java's primitive types, and another for use when it holds object references. The 'Java field access' method category of `JavaObject` contains methods for reading or writing the value of a field that holds a primitive type. A slew of “getter” methods like, `#getBooleanField:`, `#getByteField:`, `#getIntField:`, and so on, read a field which holds one of Java's primitive types. Then another slew of “setters”, `#getBooleanField:to:`, etc, write to the field. The values are represented by the corresponding Smalltalk types according to the rules below. When you access a reference field in a Java object (or class), you must supply both the name of the field, and its signature. The signature of a field is a string that describes its statically-declared type. Signatures are described more fully here, but, for example, the signature of a field that is defined as:
    	String	m_String;
is 'Ljava/lang/String;'. You can read the value of a field that holds an object reference with the `JavaObject>>getObjectField:signature:` method. For instance, to read the `m_String` field of some object, the Smalltalk code would be:
    	aProxy
    		getObjectField: 'm_field'
    		signature: 'Ljava/lang/String;'
The 'Java field access' method category of `JavaObject` contains a couple of convenience methods for use in cases where you know that the field is declared to hold a `java.lang.Object` or a `java.lang.String`. Since our field does hold a string, we could recode the example as:
    	aProxy getStringField: 'm_field'
Please note that the value of either expression would be a Smalltalk object wrapping an instance of `java.lang.String`, not a Smalltalk `String`. (You can convert it with `#asString`.) You can change the value of a reference field with the `JavaObject>>setObjectField:signature:to:` method. For instance, to set the `m_String` field to some pre-existing Java string, the Smalltalk code would be:
    	aJavaString := ...
    	aProxy
    		setObjectField: 'm_field'
    		signature: 'Ljava/lang/String;'
    		to: aJavaString.
Again, there are a few convenience methods for handling the commonest classes. In this case we could have written:
    	aJavaString := ...
    	aProxy
    		setStringField: 'm_field'
    		to: aJavaString.
And, in fact, that particular method knows how to convert Smalltalk strings into Java strings, where necessary. So we could also have written:
    	aProxy
    		setStringField: 'm_field'
    		to: 'JNIPort!!'.
Other than that one automatic conversion, any the value read from, or written to, a reference field must either be a reference to a Java object, or must be `nil`, which stands for Java's `null`. A warning (or possibly it's an opportunity): since JNIPort works via JNI, the normal Java protection mechanisms are simply ignored. That means that you can read/write “private“ fields as easily as “public“ ones. You can also change the value of “final“ fields. Changing the value of a “static final int“ field in an interface may seem fun, but it probably isn't a very good idea… ### Calling Java Methods You can safely skip this sub-section if you are using ghost classes or the Wrapper Wizard. When you call a method of a Java object (or class), you must supply both the name of the method, and its signature. The signature of a method is a string that describes its argument types and return type. Signatures are described here, but, for example, the signature of a method that is defined as:
    	String	toString(Double fuzz) { ... };
is '(D)Ljava/lang/String;'. As with reading the value of a field, there are a number of different ways of invoking a Java method, and which you use depends on the Java method's return type. The methods are in the 'Java method calls' category of class `JavaObject`. They have names, `#callBooleanMethod:signature:withArguments:`, `#callByteMethod:signature:withArguments:`, and so on through all the Java primitive types (including `void`), plus `#callObjectMethod:signature:withArguments:`, which is used for any method that returns an object reference. If the method takes no parameters then the signature is determined completely by the return type. There are a number of convenience methods that help with this special case; for instance `#callByteMethod:` which “knows” that the named method takes no arguments and so that the signature must by '()B'. If the method does take parameters, then you must supply a signature yourself. You must also build up a `JNIValueArray` to hold the parameters you want to pass to the call. A `JNIValueArray` is an array-like object that holds a list of arguments; you create one of the appropriate size, and then fill in each argument with typed versions of `#at:put:` before passing it to the Java method. For instance, given a reference to a Java string, this code will invoke its `substring(int, int)` method:
    	jString := ...
    	args := (JNIValueArray new: 2)
    			intAt: 1 put: 5;
    			intAt: 2 put: 10;
    			yourself.
    	subString := jString
    			callObjectMethod: 'substring'
    			signature: '(II)Ljava/lang/String;'
    			withArguments: args.
Primitive types are added to the list with `#booleanAtPut:`, `#byteAtPut:`, and so on. The values are represented by the corresponding Smalltalk types according to the rules below. Object references are added with `#objectAtPut:`, and must either be a reference to a Java object, or `nil`. There are many examples of using `JNIValueArrays` in JNIPort, browse for references to the class. A warning: `JNIValueArray` is a kind of `ExternalStructure` and does not protect references from the garbage collector. For instance, if you are passing a temporary Java string to a method, then you must not code it like this:
    	args := (JNIValueArray new: 1)
    			objectAt: 1 put: ('test' asJavaString: jvm);
    			yourself.
    	subString := jString
    			callObjectMethod: 'setName'
    			signature: '(Ljava/lang/String;)V'
    			withArguments: args.
because the temporary Java string, created by `('test' asJavaString: jvm)`, would quite probably be garbage collected before it could be used. You have to ensure that there is a separate reference to any object stored in a `JNIValueArray`. For instance:
    	tmpString := 'test' asJavaString: jvm.
    	args := (JNIValueArray new: 1)
    			objectAt: 1 put: tmpString;
    			yourself.
    	subString := jString
    			callObjectMethod: 'setName'
    			signature: '(Ljava/lang/String;)V'
    			withArguments: args.
Another warning: JNI, and hence JNIPort, will allow you to call any method of any object or class, including private ones. ### Representing Primitive Types JNIPort uses Smalltalk's own `Integers` to represent Java's `byte`, `short`, `int` and, `long` types. Integer are are range-checked before being passed to Java. Note that the range of Java's `byte` is -128 to +127 (however converting between a Java `byte[]` array and a Smalltalk `ByteArray` will also do a signed/unsigned conversion). JNIPort uses the Smalltalk class `Float` for Java's `float` and, `double` types. The Smalltalk representation has the same precision as Java's `double`. JNIPort uses `true` and `false` for Java's `boolean` type. Representing Java's 16-bit `char` type is problematical. In general, anywhere where a character is passed to Java, JNIPort will accept either a Smalltalk `Character` or an integer in the range 0 to 65535 (i.e. an unsigned 16-bit representation). When a character is passed from Java to Smalltalk, JNIPort represents it as a `Character` if possible, or as an `Integer` if not. This conversion is not always possible, so, for instance, converting a Java `java.lang.String` to a Smalltalk `String` will throw an exception if it contains any characters with code points over 255. (Unfortunately, this applies to `UnicodeString` as well.) Java's `null` isn't a primitive type (in fact it's difficult to say just what it should be called in Java type system — it's very much a special case), but this is as good a place as any to mention that JNIPort always represents Java `null` by Smalltalk `nil`. ### Introspection All `JavaObjects` are able to reflect on their Java object or class. They know what methods, fields, and possibly constructors, it has or inherits. See the methods in the category 'reflection'. The class static for any class is able to provide more reflective details about its instances than they themselves can. See the same category in `JavaStatic`. It may help to know that the objects that are answered by the reflection methods (wrappers for instances of `java.lang.reflect.Method`, `.Field`, and `.Constructor`) are able to work out their own signature string. The message is `#jniSignature`. ### Java exceptions When a Java method, or any other JNI operation, throws an exception, JNIPort catches it and rethrows it as a Smalltalk exception. In more detail, the Java runtime throws a Java exception, which is a Java object inheriting from `java.lang.lang.Throwable`. JNIPort sees the exception object and wraps it in an instance wrapper as usual for any Java object; this will actually be a `JavaLangThrowable`. It then creates a new instance of `JavaException` (which is a non-resumable subclass of `Exception` via `Error`), and raises that exception with the Java object as its “tag”. That means that uncaught Java exceptions are handled, by default, in the same way (a walkback if it's a development session) as uncaught Smalltalk errors. Also they can be caught and handled by a `#on:do:` expression that lists any of `JavaException`, `Error`, or `Exception` in its filters. A higher degree of specificity is possible; the class `JavaLangThrowable` implements the `#,` and `#handles:` methods necessary to act as an exception filter too. Therefore, if you have specialised subclasses that correspond to Java exception classes, then you can use the wrapper classes in an `#on:do:` expression. A still higher degree of specificity is also possible; the class statics themselves understand the filter protocol, so you can use them (the objects, not their classes) as filters too. ## Class `JavaInstance` `JavaInstance` is one of the two immediate subclasses of `JavaObject` (the other is `JavaStatic`, which is described below). Its instances all stand, in one way or another, for a real Java object. There are three significant subclasses. The most important is `JavaClassInstance` whose instances wrap a JNI reference to a Java object, and which act as proxies for those objects. The other two classes, `JavaInterfaceInstance` and `JavaNonVirtual` take an existing `JavaClassInstance` and wrap it in an Adaptor that presents an alternative view of the underlying Java object as if it were a member of a different class. All three are described below. Every instance of `JavaInstance` has a (shared) class static, corresponding to the object's Java class. The message `#static` will answer the class static. All instances understand all the methods of `java.lang.Object`, exposed in the normal JNIPort naming scheme. I.e. they all understand:
`#equals_Object:` | `#notify_null` | `#wait_long:`
`#getClass_null` | `#notifyAll_null` | `#wait_long:int:`
`#hashCode_null` | `#toString_null` | `#wait_null`
Additionally: `#equals:`, `#getClass`, `#hashCode`, and `#toString`, are Smalltalk-style aliases for the corresponding Java methods (note that `#toString` answers a reference to a Java string). Smalltalk equality (`#=`, and `#hash`) is defined on these object so that it corresponds to an identity comparison of the underlying Java objects ## Class `JavaClassInstance` These objects wrap raw JNI references, manage their lifetimes, and act as proxies for the underlying Java object. They are, with class statics and the JVM object, the most important objects in the JNIPort system. This class establishes a framework for such proxies. It has arbitrarily many subclasses (especially if ghost classes are turned on!) that are used to wrap instances of specific Java classes. Often, such custom wrapper classes would be derived from `JavaLangObject`, since all Java classes inherit from `java.lang.Object`, but that is not a requirement. JNIPort provides a way for subclasses to declare that they correspond to some specific Java class (it is actually the `#javaClassName` method on the class-side, see the section on wrapper classes for more details). When JNIPort encounters a reference to a Java object of the corresponding class (or a subclass) it creates a proxy of the designated Smalltalk class. The idea is that the custom wrapper class will define helper methods that hide the need to send messages like `#callObjectMethod:signature:withArguments:` as described above. You can create these wrapper classes by hand, but that quickly becomes tedious, so the higher level of JNIPort provides two ways of automating the process. The Wrapper Wizard, writes forwarding methods for you, and ghost classes take the idea further and makes the process entirely dynamic and automatic. #### Canonical References In normal operation, JNIPort creates a new `JavaClassInstance` whenever it encounters a new JNI reference to the underlying Java object. That is, more than one instance may be acting as a proxy for the same Java object. Under most circumstances that works perfectly well, but sometime it may be necessary to ensure that a Java object is always represented by one specific proxy. This may be important, for instance, if the wrapper class adds per-instance state, or simply to allow fast comparisons using `#==`). For this reason, JNIPort provides the notion of a canonical proxy. If an instance has been told to `#beCanonical` then JNIPort will ensure that any subsequent references to the Java object will be represented by that specific instance. This is implemented using a weak set held by each class static; see the object lifecycle for more information. This feature can be turned on on a per-class basis as well, see `JavaClassStatic>>haveCanonicalInstances`, `JavaClassStatic>>haveCanonicalInstancesByDefault`, and `JavaClassInstance class>>haveCanonicalInstancesByDefault`. #### Strings and Arrays JNIPort comes with some standard classes for wrapping Java strings and arrays. These are all subclassed from `JavaAggregate`. Which, incidentally, is an example of the way that the hierarchy of wrapper classes does not need to follow the Java hierarchy, if there is some useful commonality that can be exploited. In this case the commonality is that they share code to give them a Smalltalk-style interface. For instance, all Java arrays and strings are wrapped in objects that understand the `#at:` message (which takes a 1-based index). Arrays also understand `#at:put:`, strings don't because Java's strings are immutable. They also understand the basics of the Collections protocols, such as `#do:` and `#size`. There are various ways of creating arrays; you can use the class-side `#new:jvm:` of the corresponding wrapper class, or you can send `#newArray:` to the class static. For instance this code creates an array of 9 `doubles` and 10 `java.lang.Strings`:
    	jvm := JVM current.
    	doubleArray := JavaDoubleArray new: 9 jvm: jvm.
    	stringClass := jvm findClass: #'java.lang.String'.
    	stringArray := stringClass newArray: 10.
There are also several ways of creating Java strings. The easiest is to send `#asJavaString:` to a Smalltalk string:
    	jvm := JVM current.
    	javaString := 'Test!' asJavaString: jvm.
You can convert Java arrays into Smalltalk arrays with `#asArray`, and Java strings into Smalltalk strings with `#asString`. Note, however, that not all Java strings can be represented in Dolphin Smalltalk since Dolphin does not yet support 16-bit characters. Java `byte[]` arrays and Smalltalk `ByteArrays` can be converted into each other with `#asByteArray` and `#asJavaByteArray:` respectively. The conversion also maps between Smalltalk's unsigned bytes and Java's signed ones. ## Class `JavaInterfaceInstance` The second subclass of `JavaInstance` can be used to represent Java objects that are known to implement some interface. Java makes heavy use of interfaces; they are one of Java's best features, but they do cause a problem for wrapper classes. For instance, the Java interface `java.util.Iterator` defines two useful methods for looping over Java collections, but there are many Java classes which implement the interface and they are not related by inheritance. The problem is that, while it is easy to generate a wrapper for any one of those classes, you wouldn't want to have to duplicate the wrapper code for all the rest of them. There are several workable solutions to the problem; JNIPort has one built in — interface instances. Roughly, the idea is that, just as for instance wrappers, you tell JNIPort that a certain wrapper class should be used for Java objects that implement a certain interface. It will then arrange to wrap those objects in instances of your class. However, the picture is different in one important aspect: JNIPort always wraps a reference to a Java object in the proxy class that is most appropriate given the Java object's class; it can't do that with interfaces. A Java object has exactly one class, but may implement several interfaces, so JNIPort can't follow the usual pattern in this case. The way that this is handled is that interface instances are more like Adaptors; they take an existing wrapper object (with its own class static and so on), and put it inside another object, with a different class static, that “knows” about the interface and how to invoke its methods. These secondary wrapper objects are created by sending `asInstanceOf:` to an existing wrapper, where the parameter is the class static of the interface you are interested in. Alternatively the `asA:` method takes the Symbol name of the interface. For instance, if you have a reference to a Java object that you know implements `java.util.Iterator`, but which doesn't have a special wrapper class of its own. Because it doesn't have a custom wrapper class, the instance will be wrapped in the fallback `JavaLangObject`, and not know anything about the methods of `Iterator`. You can send `asA: #'java.util.Iterator'` to it to get back an adaptor that not only understands `#hasNext` and `#next`, but is able to understand `#do:`. (This example requires the 'CU Java Additional Wrappers' package.) ## Class `JavaNonVirtual` The last useful subclass of `JavaInstance` is of minor importance, indeed it is more than slightly esoteric. It wouldn't exist at all except that it falls, almost for nothing, out of the implementation, and provides a way of exposing one of JNI's features that otherwise would have been hidden. A `JavaNonVirtual` is an object that pairs a class static with an instance wrapper. It acts as if the underlying object were an instance of the Java class corresponding to the class static, but it bypasses Java's virtual method invocation. For example, sending `#super` to any instance proxy will answer a `JavaNonVirtual` that pretends that the underlying Java object is really an instance of its superclass. Given a Java Point object, calling its `toString()` method will answer a Java string describing the point:
    	class := JVM current findClass: #'java.awt.Point'.
    	point := class new.
    	point toString.
    		"--> a JavaLangString(java.awt.Point[x=0,y=0])"
but sending `#super` to it will answer a different view of the same object that does not override the implementation of `toString()` that it inherits from `java.lang.Object`:
    	point super toString.
    		"--> a JavaLangString(java.awt.Point@0)"
Please note the non-virtual object will not be a member of any of the wrapper classes so, except for the methods of `java.lang.Object`, it can only be accessed using the low-level methods of `JavaObject`. ## Class `JavaPrimitiveInstance` The final subclass of `JavaInstance` is used internally by JNIPort. No instances of `JavaPrimitiveInstance` are ever created. ## Class `JavaStatic` `JavaStatic` is second of the two immediate subclasses of `JavaObject`; its instances stand for Java classes and interfaces. They are known as class statics. Class statics provide access to the “static members” of a Java class in the same way that instance wrappers provide access to the non-static members. They also provide a way to call the constructors of a Java class. There are subclasses of `JavaStatic` that correspond to Java classes, interfaces, and primitive types. Although this breakdown resembles that below `JavaInstance`, the parallel is misleading. The subclasses of `JavaInstance` are fundamentally different things with different roles, whereas the subclasses of `JavaStatic` are all very similar. In fact the breakdown is more to allow JNIPort to substitute polymorphism for runtime tests, than it is to reflect real semantic differences. One exception is that only the subclass `JavaClassStatic` defines the methods for calling Java constructors (since you cannot instantiate interfaces or primitive types). Instances are always paired with a `java.lang.Class` object for that class or type. The class object has a JNIPort wrapper in the normal way, actually an instance of `JavaLangClass`. The class object exposes the fixed set of 30 (or more) methods that all instances of `java.lang.Class` understand. The class static understands the programmer-defined fields and methods of the Java class. Class objects know what class static they are paired with (and answer it to `#classStatic`); class statics know what class object they are paired with (and answer it to `#classObject`). Each instance wrapper knows that it “belongs to” the class static that stands for its Java class. The message `#static` will answer the owner. The other common way to find a class static is to look it up by name; the JVM object understands `#findClass:`, which takes the Symbol name of a Java class, interface, or type, and answers the corresponding class static. For instance:
    	jvm := JVM current.
    	jvm findClass: #'java.awt.Point'.
    	jvm findClass: #'int'.
    	jvm findClass: #'java.lang.String[]'.
    	jvm findClass: #'double[][]'.
(Class statics can also be obtained by asking other `<javaClassFinder>`s, such as the new “supplementary classloaders”, but that feature is still experimental, and is not discussed further here.) Class statics know how to wrap JNI references in proxies of the correct class (this is their most important role in the implementation of JNIPort). They work with the class registry to maintain the mapping from Java classes to Smalltalk wrapper classes, See the Players section for more detail on this. Class statics know about their place in the Java class hierarchy. There are a number of methods in the category 'Java class hierarchy' that provide access to this; `#javaSuperclass` is the most basic. Note that you can only ask for the known subclasses, neither JNIPort nor the Java runtime itself can possibly determine how many more subclasses may later be loaded (or even created). Class statics also know how to create Java arrays of the right type to hold values of their Java class. For instance the class static for Java's `int` primitive type knows how to create `int[]` arrays:
    	jvm := JVM current.
    	intClass := jvm findClass: #'int'.
    	array := intClass newArray: 10.
    	     "or"
    	array := intClass newArrayWithAll: #( 1 2 3 4).
See the methods in category 'arrays' for more ways to create arrays. The subclass `JavaClassStatic` has methods for invoking the Java class's constructors. The are in category 'instance creation'. Calling a constructor is like calling a Java method, except that there is no method name. You have to supply a signature string and a `JNIValueArray` to hold any parameters. `JavaStatic` is intended to be a superclass for custom subclasses that wrap the class-side of a Java class, in very much the same way that custom subclasses of `JavaInstance` wrap the instance-side. JNIPort uses the same kind of mechanism for associating a custom subclass with some specific Java class (and its subclasses). Similarly the Wrapper Wizard and ghost classes provide ways of automating the process. See the Wrapper Classes section for more detail. JNIPort has many examples of this.

Copyright © Chris Uppal, 2003-2005

Java, JNI (probably), JVM (possibly), and God knows what else, are trademarks of Sun Microsystems, Inc.