|
JNIPort for Dolphin Smalltalk |
||||||||||||||||||||||||||||||||
Back to Goodies |
JNIPort LayersJNIPort is built in three layers, each in several packages. The lowest level simply uses Dolphin's external interface feature to talk to Java's JNI; the middle layer provides Smalltalk-flavoured access to Java objects; the topmost level generates wrapper classes that automatically forward Smalltalk-style messages to the underlying Java objects. Here's a rough diagram of the components and how they are layered. The picture is not to-scale.
The rest of this section gives a short overview of each of the layers, and pointers to the more detailed documentation on each. It may be helpful as a kind of map, both of JNIPort and of its documentation. For a different view of how the bits fit together, you might prefer the description of the main players in the community of objects that form JNIPort. Layer 0: Java RuntimeThis is really the bottom layer. This is the software provided by Sun, or whoever implemented the JVM you are using. For some discussion of what JVMs are supported, see JVMs. Layer 1: JNI etc.This layer is all low-level stuff. The things we need to have in order to talk to the JVM, but which we don't want to think or know about when we're programming. Down here in the muck and mire we have raw pointers, objects that don't clean up after themselves, explicit memory allocation, and all the other things that one hopes to avoid by programming in Smalltalk. 1.1 — JNI InterfaceThe lowest layer of JNIPort, in package 'CU JNI', is just a simple wrapper around the raw JNI interface. Unless you know JNI and happen to be a masochist then this will be of very little interest to you. This layer sits directly above the Java runtime environment (i.e. the running JVM) and talks to it via JNI. JNI is a very low-level API, designed primarily for hairy-arsed C-programmers; it works, but is certainly not pretty. For more details see JNI. 1.2 — JNI Helper DLLAt the same level is an optional package, 'CU JNI Helper' that uses a small additional DLL to provide workarounds for a couple of problems caused by the mismatch between JNI's and Dolphin's threading models. This package is optional in that the rest of JNIPort can be used without it; however, layers higher than the raw JNI wrapper will work better (i.e. deadlock less) if it is present too. For more details see JNI-Helper and the problem with threads. 1.3 — Java Callback ClassesThere are also some helper classes written in Java which likewise are workarounds for problems caused by the mismatch between JNI's and Dolphin's threading models. These classes are necessary to allow callbacks from Java to Smalltalk, but are not otherwise needed. For more details see the problem with threads and Callbacks. 1.4 — Quasi-UTF8All String data that passes in/out of the JVM uses a weird encoding of 16-bit characters as 8-bit bytes. The encoding is in some ways similar to UTF8, but is definitely not the same. For some reason, all the Sun documentation tends to call it UTF8, which is irritating as well as confusing. The 'CU Java Quasi-UTF8' package provides the relevant encoders/decoders. Layer 2: Basic FeaturesLayer two puts a level of sanity around the raw access to JNI. Although useable, it is still quite inconvenient, since you still have to type in all the tedious stuff about method parameter types, and so on. This layer exists primarily in order to allow the bootstrapping of the much handier, automatic, facilities at level three. However most of the important concepts you will need to use JNIPort — such as class statics — are defined at this level, so you will need to know about it even if you are using the level three APIs. 2.1 — Java BaseThe lowest-level API that I'd expect anyone sane (who wanted to stay sane) to program against is in package 'CU Java Base'. This wraps the raw JNI references in proper Smalltalk objects. It provides:
The idea is that rather than use the features of Java Base to call methods of Java objects directly (which is messy, repetitive, and error-prone), it's better to write your own Smalltalk-style methods that call the raw Java Base facilities. Usually you create a specific wrapper class (or two if you want to wrap static Java methods too) for each Java class (or interface) that you are interested in. You write whatever wrapper methods you think you'll need. Then the JNIPort runtime will arrange to wrap all references to Java objects of that class (and its subclasses) in instances of your own class. The features in level three provide a way of automating this process. This level does include a fair selection of pre-defined wrappers for commonly occurring Java classes and interfaces. For a more complete description of how it works and how to use it, see Java Base, and also these examples. 2.2 — Additional Wrappers
At about the same level, another package, 'CU Java Additional Wrappers', has some more pre-defined wrappers for other common Java classes and interfaces, such as the Java2 Collections. These are split out from 'CU Java Base' only because they aren't necessary in order to use Java from Smalltalk — especially if you are using dynamic wrapper generation. For more details see Additional Wrappers 2.3 — Java CallbacksThe 'CU Java Callbacks' package supplements the Java Base with the ability to call back from Java into Smalltalk. Since Java has no notion of a function pointer, what we really mean by callbacks is that JNI allows you to define the implementation of Java's “native methods”. JNIPort gives you an extremely limited ability to specify the implementation in Smalltalk. The main reason for the limitation is the problem with threads. In practice you are more-or-less forced to make use of the Java callback helper classes. These define an interface for sending requests (which wait for a reply), or notifications (which don't), from Java into Smalltalk. These features are implemented on top of the raw callbacks and shield you (mostly) from the limitations. For more information on how to use Java callbacks, request, and notifications, not to mention their inherent problems, see Callbacks. Layer 3: Top-level stuffThis level contains the stuff that you'll probably use most. It provides convenient ways to generate Smalltalk wrapper classes for Smalltalk classes. It is also the layer where the GUI tools live (a somewhat arbitrary placement, but they've got to fit in somewhere). However, do remember that this layer still makes very heavy use of the concepts from the Java Base level (layer two), so you'll need to understand that before you can make best use of these features. 3.1 — Static WrappersSince Java has reflection it's natural to use it to generate Smalltalk wrapper classes automatically; in fact JNIPort includes two ways to do that. One way is statically, via the 'CU Java Wrapper Generation' package. This provides facilities for generating a class- or instance-side wrapper for a Java class or interface. This part of JNIPort is a framework that uses introspection to ask a Java class what methods, fields, and constructors it has; the framework then uses that to generate Smalltalk methods that will call those methods or act as accessors for the fields. The JNIPort runtime automatically wraps references to Java objects in the most specific available wrapper class (just as it would for manually created wrapper classes written using the Java Base features in layer two). Since, in Java, the signature of a method is part of its “name”, and overloading of method names is, in fact, very common in Java libraries, we have to make the (Smalltalk) names of the wrapper methods reflect the parameter types in some way. The automatically generated wrappers use a specific naming scheme, which is an attempt (not always successful) to find a good compromise between frequent ambiguity and insufferable verbosity. For a more complete description see Static Wrappers and also these examples. There is GUI tool to help with generating these wrapper classes; see the Wrapper Wizard. 3.2 — Dynamic Wrappers (Ghost classes)The other way to generate wrapper classes is to do it dynamically, and on demand. The approach I've chosen to take is to use what I call ghost classes. Ghost classes are dynamically generated ephemeral classes. They do not appear in the “official” class hierarchy (so you don't see them in the browsers) and they only live for as long as the connection to a JVM. Ghost classes use exactly the same naming scheme as statically generated wrappers. Using ghost classes is the highest level of JNIPort, and is my preferred way of driving Java from Smalltalk. The ghost class implementation is in the package 'CU Java Ghost Classes'. More details about ghost classes and some discussion the relative benefits of static and dynamic wrappers is at Ghost Classes. Also there are these examples. 3.3 — Status MonitoringThe Status Monitor is a GUI tool for starting, stopping, and monitoring the status of running JVMs. Among other things, it can be configured to display:
It is also the entry-point for the GUI to generating static wrapper classes. See the Status Monitor and Wrapper Wizard. |
Copyright © Chris Uppal, 2003-2005
Java, JNI (probably), JVM (possibly), and God knows what else, are trademarks of Sun Microsystems, Inc.