Chapter 22

Integrating Native Programs and Libraries


CONTENTS


Although Java has many advantages, it is a relatively new programming language compared with C, C++, or Smalltalk. These and other existing languages provide some features not yet available in Java. Also, many useful applications written in other languages are in use in businesses today. Programmers can use Java in more applications if they can include existing code written in other languages.

Despite the advantages of leveraging existing code, any time multiple languages are used in a single application there is more complexity. For example, it is necessary to create code to translate information between the two different languages. This code will make some Java methods available to other languages and will make the functions from other languages available to Java. Data as well as functions can be transferred between languages.

Why and When to Use Native Methods with Java Applications

Before explaining how to include native methods in your Java code, this section provides a little more detail on when such methods are appropriate. Part of the power of Java is its platform independence. Code compiled on one platform can be run on any platform with a Java interpreter. This advantage is lost, however, as soon as native methods are used.

Even if the native method can be compiled on every platform where the application is desired, there is still the additional cost of maintaining multiple versions of the compiled code. With native methods, the application will run only on platforms with the same operating system, and the dynamic library used to store the native methods must be transferred along with the application. Given these restrictions, there are still times when you will find it appropriate to use code written in a different language.

When the code you want to use already exists in another language, it may be faster to integrate the existing code with a new Java application than to rewrite it in Java. This is especially true with large, complex applications. It also may be appropriate to use Java as the front-end interface. Such an application would leave the number-crunching or back-end interface in the existing language. This combination would enable a programmer to develop a graphical user interface for applications currently run with a text-based interface. The users would have the advantage of a graphical interface while the code for the remainder of the application was still being rewritten.

When the features you need cannot yet be done with Java, it certainly makes sense to use native methods. This is true even if you have to create several different libraries for different platforms. Examples of this situation include recording audio in applications or displaying images in a format other than GIF or JPEG. Of course, if you are using native methods for this reason, it is also worthwhile to contact the Java development team to let them know what features you want. If there is sufficient interest in a particular feature, it will probably be added to the Java API.

Accessing a device on the machine that Java does not recognize is another reason for employing native code. There are many new peripherals being developed, and many of them have device drivers specific to a particular platform. To access these devices, you most likely will need to link dynamic libraries specific to the platform where the device is installed.

The final reason for using native methods in Java applications is speed. Even after optimizing your Java code to achieve as much speed as possible, native methods may still run faster. This is particularly true if they can access properties in the hardware, additional processors, or video boards, which Java does not use.

Using C Programs

The C language is considered first because this language is so prevalent. There is a large body of existing C code that has been tested over time and that many businesses may be reluctant to rewrite.

In fact, the use of C is so extensive that the Java Developer's Kit includes a tool just to create header files for linking C programs into Java applications. This tool is called javah and is used to create the header files for the example shown here.

The first topic in this section describes how to write a new C program to work with a Java application. The next topic reviews how to modify an existing C program to access it through Java. Although the two topics are closely related, creating new code is a longer, more complex process than modifying existing code.

Creating New C Programs for Use with Java Applications

The SumGraph application is used to illustrate how to create new code for use with Java by rewriting the ASum class so that the sum is calculated in C rather than in Java. This is an extreme example in that the calculations could easily be done in Java, but it serves to illustrate how a Java class can be used to encapsulate calculations done in other code. It also shows how parameters can be transferred between Java and other languages.

Step 1: Create the Java Class

The first step is to create a Java class that declares a native method. The previous Java class for ASum appeared as follows:

class ASum {
int theSum;

public ASum() {theSum = 0;}     // Constructor set instance variable to 0
public void incSum(int toAdd) {
theSum = theSum + toAdd;     // Add to instance variable
}
public int getSum(){
return theSum;     // Return the value of the instance variable
}
}

This example explains how to modify it so that the calculations currently done in the incSum() method are done in a C routine. The new incSum method appears as

public native void incSum(int toAdd);

Although the method definition for incSum has been replaced with a call to a native method, the method operates as before. It updates the class variable theSum by adding the value in the input variable to the value already stored in theSum.

You must add one more feature to the class. Because the class is using a native function, Java must be certain that the library containing that function has been loaded into the runtime environment. Java does this using a class initializer. A class initializer starts with the keyword static followed by braces that surround the methods called when the class is loaded. The modified code for ASum that calls a native C method appears as follows:

class ASum {
   int theSum;

   public ASum() {theSum = 0;}      // Constructor set instance variable to 0
   public native void incSum(int toAdd);
   public int getSum(){
      return theSum;         // Return the value of the instance variable
   }
      static {
         System.loadLibrary("sum_in_c");
      }
}

The loadLibrary() method that replaces the method body loads a dynamic library. The string passed to it is the name of the library containing the C function that the method will use. This method looks for the library along the library path. On UNIX systems, the library path is specified in the environmental variable LD_LIBRARY_PATH. On Windows systems, the environmental variable PATH is used to search for DLL libraries. The directory where the executable is stored is also checked for DLLs.

An alternate form is the System.load() method, which loads a library based on a complete pathname. If you use this form, you can include libraries that are not part of the library path in your application.

The loadLibrary() method is part of the System class. All the components of the System class are static, so it cannot be instantiated or subclassed. It is, however, a useful class. In the nongraphical examples, this class is used to supply an output stream that displays on the user's command line. Here, the System class is used to load a dynamic library. The System class also can be accessed to run the garbage collector, obtain parameters, or get the current time.

The new ASum class is called from within the Java application. The call remains exactly as it was in earlier examples. The class is stored in the file ASum.java and compiled just like any other class. The command is

javac ASum.java

When the class has been compiled and the .class file has been created, the header files needed to create the C code can be generated.

Step 2: Generate Header and Stub Files

The next step is to use the .class file generated by the Java compiler to create the header file required by the C compiler. The header file can be automatically generated by the javah tool. The header file defines a C structure to depict ASum. It also contains the function definition for the implementation of the incSum method.

To run javah against your class file and create the ASum.h file, type the following at the command line:

javah ASum

Be certain before running javah that the CLASSPATH environmental variable has been set to point to the classes.zip file. If it is not set, the javah program will return an error.

If everything is set correctly, the javah tool will place the ASum.h file in the same directory as the .class file. The contents of the file on a Windows 95/NT-based machine are as follows:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <native.h>
/* Header for class ASum */

#ifndef _Included_ASum
#define _Included_ASum

typedef struct ClassASum {
    long theSum;
} ClassASum;
HandleTo(ASum);

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void ASum_incSum(struct HASum *,long);
#ifdef __cplusplus
}
#endif
#endif

The header file defines a structure, ClassASum, that contains the instance variables in the Java class ASum. In this case there is only one instance variable, but it is important to the application. After the instance variable structure, the header file provides a check for a C++ compiler. If a C++ compiler is used, you must define the C function as extern. This prevents the C++ compiler from modifying the function name.

The definition for the C function is generated as part of the header file. The function is expected to be part of a DLL export and is declared as such. The function returns a void and takes two parameters: an instance of the structure just defined and the value passed to the method from the caller.

The name of the C function is ASum_incSum. The javah tool created this function name by combining the class name and the name of the Java method. If the class were part of a package, the package name would have been prefixed to the function name as well.

In addition to the header file, interfacing between Java and C requires a stubs file that translates the parameters and return values between the two programming languages. The stubs file is also automatically generated using the javah tool. To create a stubs file, type the following at the command line:

javah -stubs ASum

The resulting file is ASum.c, and it contains the following code:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <StubPreamble.h>

/* Stubs for class ASum */
/* SYMBOL: "ASum/incSum(I)V", Java_ASum_incSum_stub */
__declspec(dllexport) stack_item *Java_ASum_incSum_stub
Â(stack_item *_P_,struct execenv *_EE_) {
extern void ASum_incSum(void *,long);
(void) ASum_incSum(_P_[0].p,((_P_[1].i)));
return _P_;
}

The ASum.c file is compiled and linked when the C code is linked.

Step 3: Write the C Program

The C function must include the signature generated with javah. There are three included header files in the C code that follows. The header file StubPreamble.h is included to provide information to the C code to enable it to interact with the Java runtime system. This header file should be included in any C code that is being linked to Java. The header file generated earlier with javah is included in the C code to provide the function declaration. The last header file included is the standard I/O header file.

The following code is saved in the file ASumInC.c:

#include <StubPreamble.h>
#include "ASum.h"
#include <stdio.h>

void ASum_incSum(struct HASum *this,long addend) {
    long currentSum;
    currentSum = unhand(this)->theSum;
    unhand(this)->theSum = currentSum + addend
}

The next section explains how to compile and link the C program on both UNIX and Windows platforms.

Step 4: Compile and Link the C Program

Before attempting to compile the C source code, be sure the directory containing the Java include file StubPreamble.h is in your include path. If it is not, extend the include path as part of the compiler command.

The correct command to compile the C code and link a dynamic library depends on the system on which you are compiling. For a Windows system with Visual C++ version 2.0, the command is

cl ASum.c ASumInC.c -FeASum.dll -MD -LD javai.lib

The library javai.lib must be the last argument to the C compiler. You may also need to update the library path so the linker can locate this library. The command to do so is

set LIB=%JAVAHOME%\lib;%LIB%

where JAVAHOME is the installation directory for Java.

For a UNIX system, the command to compile and link is

cc -G ASum.c ASumInC.c -o libASum.so

Now that your program is compiled and linked, you can use the java class to start the program as you would any other Java application.

Running the Program

Running an application with a native method is no different than running any other Java application. If the environment has been set up correctly, the native method is transparent to the user. The command to run the example is still

java SumGraph

The program should perform as it has throughout the chapter. If a NullPointerException appears when the application is run, make sure the library path is set. In Windows, the library path is the same as your PATH variable. In UNIX C shell, the path is set using the command

setenv LD_LIBRARY_PATH mylibrarypath

If an UnsatisfiedLinkError message is encountered, it indicates that the library path is set but does not include the library that was just created for this example. In Windows, extend the PATH using

PATH = %PATH%;C:\path_to_dll_file

In UNIX C shell, extend the library path using

setenv LD_LIBRARY_PATH mylibrarypath

Using Existing C Programs

The previous section explained in detail how to create a new C program and include it in your Java application. The steps to include an existing C program are similar. Therefore, this section only highlights the key differences.

If you already have C code to link into a Java program, you just need to create the hooks in the Java classes to access your C functions. The important, and difficult, task is to make sure the header files generated by the Java compiler match the declaration of the existing C functions.

To create structures accessible to both your C code and your Java code, use the unhand() function in the C code. You may want to create an interface layer, which does the unhand and puts the result in a separate structure known only to the C code.

There are several useful functions available for accessing Java from within C. Java methods may be executed from within C code using the following function:

long execute_java_dynamic_method(ExecEnv *e, Hobject *ojb,
char *method_method_name, char*method_signature, ...);

To have the C code throw a Java exception when control returns to Java, use this function:

SignalError(0, JAVAPKG "NameOfExceptionClass","ExceptionMessage");

Peter's Principle
When including native code in a Java application, it is worth giving some consideration to freeing memory allocated in the native method. If the native method does not use alloc to reserve memory, this is not a concern. Unfortunately, most complex applications will make some use of memory-allocation functions. This memory must be freed at some point.

Java's garbage collector will not automatically reclaim memory allocated in native methods; you must write a native method to free the memory allocated in a native method. However, you can call the method used to free the memory from within your Java class using the dispose() method. This method is automatically called when your instance is garbage collected. In this way you can be assured that memory allocated by native methods in association with a specific instance of a class is freed when that instance is discarded.

Using Programming Languages Other Than C

There is a large body of C programs currently in use in the business and scientific worlds, so it is expected that most of the code linked to Java will be C code. However, there are many other languages in use, and algorithms coded in those languages also can be linked to Java.

C++ Programs

C++ code is the next-easiest language to link into a Java application. Although you do not need to create C functions to call your C++ code, you do need to surround your C++ code with extern "C". This will enable you to reference the C++ functions by the names used in the source file. The following is a simple example of using extern "C":

extern "C" {
     void HelloWorld(void) {
     cout << "Hello, world.\n";
     } // end HelloWorld
}      // end extern "C"

If you do not use extern "C" in your C++ code, the compiler will modify the names and the linker will not be able to resolve the references. You may also run into some type conflicts, which can be handled using wrapper functions. However, accessing C++ functions from Java is usually no more complicated than accessing C. Therefore, you can follow the same steps outlined previously to link C++ code into Java applications. To recap:

  1. Create the Java class with appropriate references for use with native functions.
  2. Generate header and stub files.
  3. Write or modify the C++ program for use with Java.
  4. Compile and link the C++ program.

Programs in Other Languages

Any program compiled into a dynamically loadable library can be linked into a Java program. The advantage using of C/C++ is the availability of Java tools that create the appropriate header and stub files. If you are linking in other languages, you must create these files manually.

To incorporate other languages easily, consider linking the code from that language into a C program and then linking the C code to Java. The additional overhead of going through the extra compile is offset by the advantage of having the Java compiler create the header and stub files.

Microsoft has recently announced support for the Java language. The new ActiveX controls, formerly known as OLE controls, will make many full-featured components available to Java developers. These components can be incorporated into Java applications and applets. It would then be possible to include a Microsoft application such as Excel in a Java application.

As you include features from other languages in your applications, it is important to remember that these features are not available on every platform. If you are developing for a specific target audience, this may not be a problem. Many corporate information-systems groups believe in keeping all desktop platforms standardized. Developing in this environment means you can use any native tools that are considered part of the corporate standard. However, if you want your application to be truly portable, stay with objects defined and supported as part of the Java Developer's Kit.

One final word of caution involves dynamically loadable libraries. Be very certain that the names you provide for your libraries do not conflict with other library names the user may have installed already. The functions compiled in your libraries also must have unique names. If the names are not unique, the Java application risks interfering with applications already installed on the user's system.

The fact that Java uses a combination of the package, class, and method names to create the interface function names should help to ensure uniqueness. If you notice any problems with other applications after installing a Java application that calls native methods, remove the dynamic library installed with the Java application. If the other applications then perform normally, modify the names of the functions in the dynamic library installed with the Java application to resolve the conflict.

Summary

Your Java applications can access native methods written in other programming languages, such as C/C++. The Java Developer's Kit includes a tool called javah that you can use to create header files for linking C programs into Java applications, which makes C the easiest language to integrate with Java.

C++ code is the next-easiest language to link into a Java application. All you need to do is surround your C++ code with extern "C". You can link programs written in other programming languages into your Java applications as long as the program is compiled into a dynamically linked library, yet for ease of integration you will probably want to link the code into a C program and then link the C code to Java.