Caffeinator
An API for caffeinating your native application!

by Michael Fraenkel
Date: 4/25/97, Version: 1.1

Caffeinator

Caffeinator is a developer's tool that allows enables applications written in C++ to create and manipulate Java objects within your native application. The amount of code written in a native language such as C++ is far greater than that of Java. To be able to extend your current code base to take advantage of Java is a win-win situation. Caffeinator uses JNI to expose Java classes as C++ classes.


An Example

Caffeinator is easiest explained first with an example. The example presented here are excerpts from the sample code TestApp.cpp. To embed Java within the example J-Empower is used. The example will show how to add a java.awt.event.MouseMotionListener to a TestPanel.

Write the Java code for the panel called TestPanel.java. Compile TestPanel.java so that we may generate a corresponding C++ class.
  1. Start Caffeinator.
  2. Add TestPanel as a class to generate.
  3. Add java.awt.event.MouseMotionListener so that we may create a C++ event listener to override.
  4. Double-click on TestPanel to display its PEMC.
  5. Switch to the Events page. Notice that no events are present.
  6. Change the end class to java.awt.Component.
  7. Expose the mouseMotion event.
  8. Generate the source code.
  9. Save the session.
We will write some native code to embed the TestPanel and attach a mouse motion listener to the panel. To embed the TestPanel into our native application, we use some of the J-Empower functions. The following will create and embed an instance of TestPanel into our application.
Create a new subclass of cafjava_awt_event_MouseMouseListener called MyMouseMotionListener and override the methods we are interested in. For our example, we are interested in all the methods. Next we will add the listener to our TestPanel using the registration method addMouseMotionListener(). Since java.awt.event.MouseMotionListener is an interface, we must create an interface instance of from the instance of MyMouseMotionListener. Compile all the java code that Caffeinator created, and build the application.


Installing and Running

To install Caffeinator, just place caffeinator.zip in your classpath.

To run Caffeinator, type java Caffeinator [<filename>] 


Classes and Interfaces

Caffeinator deals with Java classes and Java interfaces. The initial window shows a hierarchical view of all the classes and
interfaces defined in the current Caffeinator session.

 
The list of classes is called the class list. As classes and interfaces are added to Caffeinator, all superclasses are automatically added as well. Interfaces that classes implement are not added automatically. These interfaces must be added manually.

Classes and interfaces may be removed by hitting the DELETE key. If a class or interface is a superclass of a class still defined, the class or interface will not be removed. There are some classes like java.lang.Object, java.lang.String and java.lang.Throwable which are considered permanent classes. These permanent classes are necessary for the base code implementation and can never be removed


Properties, Events, Methods, and Constructors

After selecting the initial set of classes and/or interfaces, double-clicking on any of the listed classes/interfaces will display the properties, Events, Methods, and Constructors panel (PEMC).

 

The PEMC will display all properties, events, and methods (features) for a given class. The features displayed will depend on the "visiblity" selected. The "visibility" is determined by the starting and ending class. All public features found between the starting and ending class inclusively are displayed. The starting class is always the class you are viewing and cannot be changed. The ending class can be any class in the superclass hierarchy including the starting class. By default the ending class is the starting class. To change the ending class, select a new class from the drop-down listbox.

Each page displays a list of items on the left called all features and a list on the right called exposed features. All exposed features are accessible in the corresponding C++ class. To add exposed features, select all the items from the all features list you wish to expose and hit the >>>> button. To remove exposed features, select the items from the exposed features list you wish to remove, and hit the <<<< button or hit the DELETE key.

As properties, events, methods, and constructors are added, any classes which they use either as a return value or a parameter are automatically added to the class list. Classes however are never removed automatically when the feature no longer exposed. Classes no longer in use must be removed manually. If a feature is exposed, any classes it requires will not be allowed to be removed from the class list.

Constructors will only be present for classes and not interfaces. The constructors listed are only for the current class. Since constructors are not inherited, changing the ending class will not affect this page.


Saving and Loading

Caffeinator has the ability to save and reload your work. To save your current session, select the File menu from the Caffeinator window and select Save... To reload any work that was previously saved, select the File menu and select Load... When you start Caffeinator, you may also load a previous session by passing the name of the file as a command-line argument.

Caffeinator can also import from a list of classes. The format of the file is class name followed by methods to add, i.e., <class name> [all] [<method name 1> <method name 2> ...]. The <class name> is the name of the class that is to be add. To add all constructors and methods, specify "all". When a method name is specified, all public methods matching the name are added. For example, to expose the constructors of java.util.Hashtable and all methods of java.util.Enumeration would look like:


Code Generation

Caffeinator will generate a C++ include file and a C++ source file for every class or interface listed in the class list. Any interface that inherites from java.util.EventListener listed will also have an extra C++ include and C++ source file as well as a Java file.

The code generated for a class or non-java.util.EventListener interface is simple to use. All parameters and return types correspond to either their native Java type as is the case for all primitive Java types, e.g., byte, char, int, etc..., or their C++ equivalent. The C++ equivalent for any Java class is the full name of the class including the package name with the period (.) replaced with an underscore (_), e.g., java.lang.String becomes java_lang_String.

An interface that has java.util.EventListener as a base class generates three extra classes so that a programmer can create a C++ version of the event listener.


Coding

Now that we have all the code, how do we use it? Let use first start by looking at some of the helper files that are generated by Caffeinator. The three helper files are JObject, JArray, and CPPXForm.

JObject represents the base class for all C++ classes corresponding to a Java object. There are a few methods that are useful to programmers, they are: A JObject is bound to either a local reference or a global reference. JObjects that are bound to local references are only valid within the thread it was created on. Global referenced JObjects can be used on any thread. However, the JNIEnv created for a JObject is only valid for the thread it was create on. Therefore, you should only use JObjects created within your thread. If you are passing objects across threads, you must make a global referenced JObject prior to copying it on another thread. For example, to pass a java.lang.String that was created in thread 1 to thread 2:
Thread 1 

java_lang_Object obj = ....; 
java_lang_String localStr = obj.toString(); 
java_lang_String 
globalStr(local.getObject()); 

... pass globalStr to Thread 2 ... 
Thread 2 

java_lang_String 
myStr(globalStr.getObject()); 

JArray encapsulates array manipulation for both primitive types and objects. The common methods for both types of array are: For primitive types, there are two more methods for getting and setting a consecutive group of values: CPPXForm contains various transformations for strings. The current supported transformations include: For example, to create a java.lang.String from a char * and then create a string from that:
To transform an instance of a class to one of the interfaces it implements, just create an instance of the interface passing the object of the instance. For example, to create an instance of java.io.DataOutput from an instance of java.io.DataOutputStream: Creating a C++ instance of an event listener is quite easy. For every event listener, the C++ event listener class that must be overriden is given the name of the C++ version of the Java class appended with caf, e.g., java.awt.event.ActionListener becomes cafjava_awt_event_ActionListener. You must subclass this class and add whatever behavior you wish to those methods you are interested in. To attach an instance of your event listener just perform the same actions you would with any event listener by calling the appropriate registration method. For example, to create an java.awt.event.ActionListener and attach it to a button: After every method invocation or constructor invoked which creates a new instance, a check for a thrown exception is performed. If a Java exception was thrown, a corresponding C++ will be thrown. The only type of C++ exception thrown is java_lang_Throwable. If you wish to know the specific exception thrown, you can examine the class name of the thrown Java exception. For example, if I put a null value into a java.util.Hashtable, a NullPointerException is thrown:


Futures