12 HORB and Native Calls


(This section was contributed by Larry Bottorff.)

Normally a Java applet downloaded to a client's Web browser cannot make calls to object methods that have been rewritten as "native". Java has no technique of communicating with a function library on a server, nor could it contend with the security concerns of downloading non-Java binary code. A HORB client applet, however, can access methods written in the C language and housed in a function library such as a DLL on the server. We may have made the switch to Sun Microsystem's Java, but we still may be asked to splice the Java and non-Java worlds together. No doubt this is why Java has preliminarily offered native calls.

In order to fully appreciate this section, you should have a good grasp of how Java works with native calling. See the Java Language Tutorial's "Integrating Native Methods into Java Programs" section. Our approach is fundamentally similar. The steps involved in creating a HORB-powered native calling applet marry applet and standalone native calling strategies. In our example we will pass a string from the client-side to the server, and the server will then make a native call to a DLL. Let us start by writing a server class.

        // Server.java
        package nativeHORB;
        
        public class Server {

          public Server() {
            System.loadLibrary("nathorb");
          }
          public native String nativeWordnat(String rawString);

          public String nativeWord(String rawString) {
            String s = nativeWordnat(rawString);
            return s;
          }
        }

For this example create the subdirectory nativeHORB directly under a path mentioned in your CLASSPATH environment variable.

We see similarities with the Java tutorial's server class. In our example, we make the system aware of our DLL as part of Server's constructor. (There are other ways of accomplishing this. See the "comprehensive" example in the Java tutorial for throwing exceptions.) Next we offer a prototype for the method that will be written in C. Class Server's only method, nativeWord, takes a string as an argument and passes the string on to the native function. (Java will not allow a Java and a native method to have the same name. There is a trick to avoiding two separate methods, but we won't complicate the issue now.)

Compile Server.java:

        C:\..\nativeHORB> horbc Server.java

Once the full set of proxy and skeleton classes have been created, we need to generate the native stub and include file. Here we simply follow the standard procedure, including the reference to the package.

        C:\..\nativeHORB> javah nativeHORB.Server
        C:\..\nativeHORB> javah -stubs nativeHORB.Server

Now let's look at the client-side. No frills nativeClient.java displays a text box for input, a button and a textbox for output:

        package nativeHORB;
        //making native calls with HORB

        import java.awt.*;
        import java.net.*;
        import horb.orb.*;
        import java.util.*;
        import nativeHORB.*;

        public class nativeClient extends java.applet.Applet {

          int port = 8887;
          Server_Proxy remoteServer;
          Label wordLabel = null;
          TextField wordField = null;
          TextField returnField = null;
          Button nativePush = null;
          String errMsg = null;
          String getWord = null;
          String returnWord = new String("bozo");
        
          public void init() {
            setLayout(new GridLayout(2, 2));
                
            add(wordLabel = new Label("Enter a word: ", Label.CENTER));
            wordLabel.setFont(new Font("Helvetica", Font.PLAIN, 14));
            add(wordField = new TextField(30));
            add(nativePush = new Button("Process with Native Function"));
            nativePush.setFont(new Font("Helvetica", Font.PLAIN, 12));
            add(returnField = new TextField(30));
        
          }

          public void start() {
            String host = "-";
            try {
              URL url = getDocumentBase();
              host = url.getHost();
              if (host.length() == 0)
                host = "-";
              } catch (Exception e0) {}

            try {
              remoteServer = new Server_Proxy(new HorbURL(host, port, null));
            } catch (Exception e) {
              showStatus(e);
            }
          }

          public void stop() {
            remoteServer._release();
          }

          public boolean handleEvent(Event evt) {
            if ((evt.id == Event.ACTION_EVENT)&& (evt.target == nativePush)) {
              getWord = wordField.getText();
              try {
                returnWord = remoteServer.nativeWord(getWord);
              } catch (Exception e) {
                showStatus(e);
              }
              returnField.setText(returnWord);
              return true;
            }
            return super.handleEvent(evt);
          }
        }

This is similar to previous HORB applet code. The user enters a word or phrase (hold it to 60 characters or less), and the handoff occurs in the handleEvent method when the user clicks on the button. The second textbox should repeat the word or phrase, plus something tacked on to the end. Compile this code just like any client-side HORB code.

        C:\..\nativeHORB>horbc -c nativeClient.java

Let's switch over to the C language now. The Java Language Tutorial mentions Microsoft Visual C++ 2.x as the preferred compiler, but our tests had trouble with two MS include files with Microsoft Visual C++ 2.0. You need to add a special parameter -DWIN32.

Now we can create the native function in C. Below is our example code. Again, you must recreate the function based on the prototype in nativeHORB_Server.h.

        #include <StubPreamble.h>
        #include <javaString.h>
        #include "nativeHORB_Server.h"
        #include <stdio.h>
        #include <string.h>

        struct Hjava_lang_String *nativeHORB_Server_nativeWordnat(struct
             Hnative3_Server *this, struct Hjava_lang_String *first) {
          char second[70];
          javaString2CString(first, second, sizeof(second)); /*Java supplied C function*/
          strcat(second, " back again!");
          return makeJavaString(second, (strlen(second))); /*Java supplied C function*/
        }

As you may see, passing strings between Java and C is a bit awkward.
Hopefully more Java documentation in the future will help us all in
knowing exactly which data types need what extra support.

It's time to create the DLL. Actually, it should fly fairly straight:

        C:\..\nativeHORB> cl nativeHORB_Server.c nathorb.c -Fenathorb.dll -MD -LD -DWIN32 javai.lib

Remember, if you have any additional libraries to include, make sure javai.lib is last:

        . . .-LD orant71.lib javai.lib

Once the compile and link are completed, you should find nathorb.dll in your directory. Now it is important to copy this file to a directory mentioned in your PATH statement. Take a look at the html example below for ideas:

<HTML>
<HEAD>
<TITLE>Native Test</TITLE>
</HEAD>
<BODY>
<applet code="nativeHORB.nativeClient.class" width=350 height=80 codebase="..">
</applet>
</BODY>
</HTML>

Start up the HORB server in a new window: 
        C:\> horb -v

Then start up our Netscape 2.x browser and call up your new creation. Again, enter a word in the first text box and click on the "Process with Native Function" button.

Again, some of us may be asked to integrate legacy C code into a Java project. This small example will get you started in the right direction. Unfortunately, not enough documentation exists on the Sun Microsystem side for us to figure out how to safely pass objects back and forth. For example, how do you represent a C struct and pass it to Java? Hopefully these issues will be resolved in the near future.