An API for caffeinating your native application!

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

J-Empower

J-Empower is a developer's tool that provides a simple interface which allows embedding of Java applets and JavaBeans into your application. J-Empower is evolving into its second release. The first release addressed applets. To better service the needs of the Java 1.1 community, JavaBeans support has been added to release 1.1 of J-Empower.

J-Empower takes the form of a system specific DLL and a collection of Java .class files. J-Empower provides a simple 'C' interface which include functions to interact with the Java runtime, and execute and manipulate Java applets and JavaBeans within a native application. J-Empower allows native applications to exploit context specific components, applets in the browser context and JavaBeans in Java applications.

J-Empower was initially built as a result of a practical need to connect Java to real world applications. Specifically, applications like Lotus Note, Lotus SmartSuite, OS/2 Netscape Navigator and the OS/2 desktop. As a result of working with an initial clientele, J-Empower deals particularly well with applets. However, we are starting to see an increasing demand to go beyond applets. Support for JavaBeans is the next logical step. 


The J-Empower API

The J-Empower API is broken up into 3 categories: Java Environment, Applets, and Beans. The Java Environment set of APIs deal with the Java Runtime (Java Virtual Machine). They include functions to start the Java runtime as well as the processing of console messages generated by the runtime. This API set also allows you to read and write information (properties) to an area common to all Java classes. The Applet API set deals, of course, with either single or sets of applets, include security. The Bean API set deals with creating and embedding JavaBeans.

Java Environment

jeInitJavaRuntime  starts the Java runtime 
jeVersion  return version of JEmpower 
jeConsoleSetVisible  Show or hide the Java output console window 
jeConsoleIsVisible  query the visibility of the console window 
jeRegisterShowConsole  register a function that will be passed all console output 
jeGetProperty  query a the value of a given property 
jeSetProperty  set the value of a given property 

Applets

jeInitAppletRuntime  starts the Applet runtime 
jeAppletInit  initializes the applet specified 
jeAppletStart  starts an Applet that has been stopped or initialized 
jeAppletStop  stops a running applet 
jeAppletDestroy  destroy an existing applet 
jeAppletsIconify  Notify all applets that your application has been minimized 
jeAppletsUniconify  Notify all applets that your application has been restored 
jeAppletsDestroy  Destroy all applets 
jeAppletGetStatus  return the status of the applet 
jeRegisterShowDocument  register a function that will follow a URL link 
jeRegisterShowStatus  register a function that will be passed all status information 

JavaBeans

jeBeanInstantiate  create a JavaBean 
jeBeanEmbed  embed a GUI JavaBean 
jeBeanSerialize  write a JavaBean to a file 
jeBeanDeserialize  create a JavaBean from a file 
jeBeanDispose  destroy a JavaBean 
jeObjectFromBean  return the jobject of a bean 

General Functions

jeMove  move and resize a bean/applet 
jeShow  show or hide a bean/applet 
jeResize  resize a bean/applet 
jeHWNDFromHandle  return the window handle given a handle 


Architecture

JavaSoft's Java is ever evolving. Allowing "native" applications to embed Java components is the cornerstone of the bigger "inter-operation" picture. Java 1.1 has introduced a component architecture called Java Beans. Web browsers however use components called applets. Applets may or may not be Java Beans. "Embedded Java" is the essence of J-Empower. The J-Empower API provides a mechanism for establishing the Java virtual machine (runtime) and interacting with it. The first release of J-Empower focused on embedding applets in native applications laying the groundwork needed for tight inter-operation between native applications and Java applets. The second release of J-Empower focuses on improving the current applet support and adding new support for JavaBeans.

The Package

 

J-Empower is packaged as a DLL, containing a little over two dozen C-API's and a ZIP file, containing the Java support classes. Because of the portable spirit of Java, it is critical the the JEMPOWER.DLL be portable. To guarantee a high level of portability, J-Empower uses the Java Native Interface(JNI) to communicate with Java.

Figure 1 shows the flow of control between "your" application, J-Empower, the Java runtime and an embedded Java applet or JavaBean.

Communication paradigm

 

Figure 2 illustrates the communication paradigm used by J-Empower. J-Empower uses the Java Native Interface (JNI) to communicate between native code (C/C++) and Java. JNI allows only one Java Virtual Machine (JVM) to be created within a process. Therefore, J-Empower and the JVM are both executing as extra threads within the native application.

Since J-Empower and the JVM are within the same process as the native application, communication between the navtive application and Java is simplified. The communication between native code and Java can be both bi-directional and unidirectional with a starting point from either side. This is to allow support for asynchronous callbacks as well as basic notifications.

J-Empower and JNI

The JNI is a native programming interface. It allows Java code that runs inside a Java Virtual Machine (VM) to interoperate with applications and libraries written in other programming languages, such as C, C++, and assembly [JNI97]. 

Using JNI from a native application is not as easy as using JNI in a native method. Java has the concept of garbage collection. JNI extends the notion into your native application by keeping track of all references touched through its methods. It becomes the responsibility of the programmer to remove all unnecessary references. J-Empower does this for you. It also provides a higher level interface to deal with Java components in a very simplistic manner. 

A Quick Example

The best way to understand the "caffeinating" process of J-Empower is to examine a simple example.

The following example steps through the creation of a simple Windows95 program starting the Java VM. The program is written in C, but one could imagine easily adapting the same example to use C++. While "walking" through the following example the sections pertinent to J-Empower will be explicitly highlighted. Appendix A contains the complete program listing.
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
        MSG msg;
        HANDLE hAccelTable;

        if (!hPrevInstance) {
                // Perform instance initialization:
                if (!InitApplication(hInstance)) {
                        return (FALSE);
                }
        }

        // Perform application initialization:

        if (!InitInstance(hInstance, nCmdShow)) {
                return (FALSE);
        }

        hAccelTable = LoadAccelerators (hInstance, szAppName);

        jeInitJavaRuntime();
        jeInitAppletRuntime();

        jeRegisterShowConsole(MyShowConsole);

        // Main message loop:
        while (GetMessage(&msg, NULL, 0, 0)) {
                if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg)) {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                }
        }

        return (msg.wParam);
}
Figure 3. Initializing J-Empower

We start with WinMain. WinMain has the honor of kicking off all the typical things needed to correctly start a Windows95 application. These responsibilities include initializing window data, registering the window class, creating the main window and loading the window accelerators. All these tasks are accomplished by using the helper functions InitApplication() and InitInstance() and the Win32 API LoadAccelerator(). So far nothing out of the ordinary. It's all straight Win32. Now we introduce our first J-Empower API. jeInitJavaRuntime() is the API that "gets it all started". It is responsible for loading and initializing the virtual machine (runtime). The next J-Empower API to get called is jeInitAppletRuntime(). The applet framework is loaded and initialized. The jeRegisterShowConsole() method registers a function that will receive all Java virtual machine console output message strings. This particular example passes in the helper function MyShowConsole(). As you will see, our implementation of MyShowConsole isn't very ambitious, in fact, we don't do anything within this function. So, feel free to use it as a basis for something more interesting in your own application. Lastly, WinMain enters the mandatory message dispatch loop. Next stop, Window Proc.
All the remaining "action" in our test program occurs in the Window procedure. The following WinProc() fragments illustrate many of the Applet related API's found in J-Empower. Some of the APIs are self explanatory. For example, we process the ID_SHOWCONSOLE command message by issuing the J-Empower API jeConsoleSetVisible(TRUE). 
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
{
        static JEAPPLETHND hApplet=0;
        static JEBEANHND bean = 0;
        HWND hwndApplet=0;
        static int xPos = 0, yPos = 0;

        int wmId;
        PAINTSTRUCT ps;
        HDC hdc;

        switch (message) {
                case WM_CREATE:
                        /*
                         * Window initialization is performed here in WM_CREATE processing
                         * WinLoadString loads strings from the resource file.
                         */
                        break;

                case WM_COMMAND:
                        wmId = LOWORD(wParam); // Remember, these are...
                        //Parse the menu selections:
                        switch (wmId) {
                                case ID_SHOWCONSOLE:
                                        jeConsoleSetVisible(TRUE);
                                        break;

                                case ID_HIDECONSOLE:
                                        jeConsoleSetVisible(FALSE);
                                        break;

                                case ID_INITAPPLET:
                                {
                                        HWND hwndApplet;
                                        JEAPPLETWND hApplet;
                                        JECONTEXTID contextID = 1;
                                        char *argv[7]; // holds the 4 applet parameters
                                        long height, width; // applet height and width

                                        height = 160;
                                        width = 460;

                                        argv[0] = "imagesource=images/Beans"; // set the applet parameters
                                        argv[1] = "backgroundcolor=0xc0c0c0";
                                        argv[2] = "endimage=10";
                                        argv[3] = "soundsource=audio";
                                        argv[4] = "soundtrack=spacemusic.au";
                                        argv[5] = "sounds=1.au|2.au|3.au|4.au|5.au|6.au|7.au|8.au|9.au|0.au";
                                        argv[6] = "pause=200";

                                        jeAppletInit(&hApplet, contextID, hwnd,
                                                "file:////java/demo/Animator/","Animator",
                                                NULL, NULL,
                                                width, height, 7, argv);

                                        // get the applet container window from the applet handle
                                        jeMove(hApplet, pt.x, pt.y, width, height, TRUE);

                                        pt.y += height + 10;

                                        // show the applet container window
                                        jeShow(hApplet, TRUE);

                                        // now... start
                                        jeAppletStart(hApplet);
                                }
                                break;

                                case ID_STARTAPPLET:
                                        jeAppletStart(hApplet);
                                        break;

                                case ID_STOPAPPLET:
                                        jeAppletStop(hApplet);
                                        break;

                                case ID_DESTROYAPPLET:
                                        jeAppletDestroy(hApplet);
                                        break;

                                ...
Figure 4.Applet WndProc

The most interesting fragment in the figure above is the processing of the ID_INITAPPLET command message. It is within this "case" that the Animator applet is created. The J-Empower API used to create the applet is jeAppletInit(). This API takes several parameters all of which correspond to the parameters needed to create the APPLET HTML tag. The trickiest parameter is the argument list parameter. However, after looking at the example below, the organization of this parameter becomes quite obvious. One thing to remember is the applet is initially created hidden. To show an applet, the API, jeShow() is provided. Previously, the window handle of the applet had to be obtained using jeHWNDfromHandle(). This is one such improvement made in release 2. (See Applets for a more complete explanation applet window relationships).

Again, the example almost speaks for itself.

We now move on to an example showing the new JavaBean APIs in action.
...
case ID_BEANCREATE:
        jeBeanInstantiate(&bean, "test");
        break;

case ID_BEANEMBED:
{
        HWND bHwnd;

        jeBeanEmbed(hwnd, bean);
        bHwnd = jeHWNDFromHandle(bean);

        SetWindowPos(bHwnd, HWND_TOP, xPos, yPos, 150,150, SWP_SHOWWINDOW);
        yPos += 150 + 10;
}
break;

case ID_BEANDISPOSE:
        jeBeanDispose(bean);
        break;
...
Figure 5. JavaBean WndProc

The example like the previous ones is self-explanatory. It is important to understand that not all JavaBeans can be embedded. jeBeanEmbed() will only allow beans which inherit from java.awt.Component to be embedded.


Java Environment

All applications using J-Empower, must begin by initializing the Java runtime using the jeInitJavaRuntime() method. However, an application can start the Java VM prior to calling jeInitJavaRuntime so that different startup options are used, but jeInitJavaRuntime() must still be called. Once the Java VM is running, any of the other methods within J-Empower can now be invoked.

Properties are an important feature of Java since they are used as configuration parameters for some of the subsystems within Java. Most applications are interested in setting up either socks or proxy support for applets. JDK 1.1 isolated the proxy support to the HTTP classes which now support HTTP 1.1. The HTTP properties are: http.keepAlive, http.maxConnections, http.nonProxyHosts, http.proxyHost, and http.proxyPort. The Socks properties are: socksProxyHost, and socksProxyPort. 

Properties are set using the jeSetProperty() method. To configure socks to use the host socks.ibm.com on port 1081, the following statements are used:


Applets


The J-Empower API has a complete set of functions dealing with Applets. The difficulty with Applet support is that both Sun and Netscape have different implementations of the Java applet classes. This fact is a good example of the need for a set of APIs to control applet embedding in a consistent fashion.

The J-Empower implementation of applets place an applet within a sun.awt.windows.EmbeddedFrame. Sun uses a panel, and Netscape uses a modified java.awt.Frame for embedding. The EmbeddedFrame was chosen for a very good reason: Sun published this class specifically for the ability to embed a Java GUI within native applications. As figure 3 illustrates. this EmbeddedFrame is then placed within a native window which we call an Applet Container.

An important feature of the J-Empower Applet APIs is to have notification of completion. Since all applet actions are asynchronous, there needs to also be asynchronous notification back to the native applications.

Currently, if an applet requests to be resized, the applet is given this new size. If we embed applets into native applications, the "automagic" resizing effect could have bad implications. Our implementation, allows the applet to resize, but the container window will not resize. If the native application requests a resize, both the container window and the applet are resized.

The current framework for applet support assumes that applets are grouped together in a single pool. We have introduced the notion of a context id which allows the programmer to group applets into an AppletGroup which are identified by the context id. Applets are restricted to communicating with only those applets that are (a) in their AppletGroup and (b) cleared for communication by the SecurityManager rules.

Default Applet Security

As the J-Empower architecture evolved there seemed to be a growing need for native applications to take a larger part in the Java applet execution. These areas include security, status information, and native application support of http/socket calls.

The J-Empower security model allows the application to either set its own Security Manager or allow the default J-Empower Security Manager to be installed. The following table defines the default security checks.

Security check J-Empower Default behavior

classloader creation  fail 
thread access  fail if outside applet's threadgroup 
threadgroup access  fail if outside applet's threadgroup 
system command execution  fail 
exit VM  fail 
linked library existance  fail 
System.properties() access  fail 
property access  if property, a, is requested and property, a.applet is true, then success else fail 
file read access -  if file starts with directories in acl.read/acl.read.default or same directory as the applet then success else fail 
10  file write access  if file starts with directories in acl.write/acl.write.default then success else fail  
11  file descriptor read access  success if through Socket or invalid fd otherwise fail 
12  file descriptor write access  success if through Socket or invalid fd otherwise fail 
13  listen  success if port > 1024 otherwise fail 
14  accept  success if applet is allowed to connect to the host otherwise fail. 
15  connect  based on the network mode (appletviewer.security.mode), one of three cases occur: 

 NETWORK_UNRESTRICTED default = success 

NETWORK_NONE:default = fail 

NETWORK_HOST:default= if hosts match success, else if proxy trusted (trustProxy) success else fail 
16  top level window warning  always display warning 
17  package access  if the package, pkg, is being accessed and the property, package.restrict.access.pkg, is true then fail else succeed. 
18  package definition  if the package, pkg, is being defined and the property, package.restrict.definition.pkg, is true then fail else succeed 
19  setting factories  fail 
20  class member access  success for public members on classes loaded with the same classloader otherwise fail 
21  print job  fail 
22  clipboard access  fail 
23  event queue access  fail 
24  security access   fail 
J-Empower supports other types of callbacks which provide assist in providing a tight degree of integration between your application and Java applets. The ground rules for callbacks are as follows: (1) callbacks are invoked from Java, (2) callbacks are allowed to call into Java, and (3) callbacks must be re-entrant.


JavaBeans

 

The J-Empower API has another set of functions dealing with JavaBeans. The JavaBean APIs support both non-GUI and GUI JavaBeans. There is also support for saving and reloading beans.

The J-Empower support of JavaBeans allows an application to instantiate any bean. If the bean is a GUI bean, an application can request the bean to be embedded. The bean is placed within a sun.awt.window.EmbeddedFrame and the handle of the frame is returned. If the bean inherits from java.awt.Frame, then the handle of the window that the bean represents is returned instead.

All beans created with J-Empower must be disposed of. The disposal of a bean, cleans up internal storage used to keep track of the bean and will destroy any Java frame associated with the bean either by embedding or itself.

Persistence storage is a piece of the JavaBean puzzle. Currently, the JavaBeans specification only supports object serialization. Hence, the JavaBean APIs only support serialization and deserialization. These APIs allow a native application to save the state of the object in which they are dealing with for use at a latter time.


J-Empower API Details


jeInitJavaRuntime 
jeVersion 
jeConsoleSetVisible 
jeConsoleIsVisible 
jeRegisterShowConsole 
jeGetProperty 
jeSetProperty 
jeInitAppletRuntime 
jeAppletInit 
jeAppletStart 
jeAppletStop 
jeAppletDestroy 
jeAppletGetStatus 
jeAppletsIconify 
jeAppletsUniconify 
jeAppletsDestroy 
jeRegisterShowDocument 
jeRegisterShowStatus 
jeBeanInstantiate 
jeBeanEmbed 
jeBeanDispose 
jeBeanSerialize 
jeBeanDeserialize 
jeObjectFromBean 
jeMove 
jeShow 
jeResize 
jeHWNDFromHandle 

References

[G+96] G. Cuomo, M. Fraenkel, R. Redpath and J. Eisen. "4/25/97J-Empower: An API for caffeinating your native application!." Version 1.0, May 1996

[F97] M. Fraenkel. "An Example using the Java Native Interface(JNI) in a Native Application." TR 29.2226, January 1997

[JB96] Sun Microsystems Inc. "Java Beans 1.0." Version 1.00-A, December 1996

[JNI97] Sun Microsystems Inc. "Java Native Interface Specification." Release 1.1, February 1997