Copyright ©1996, Que Corporation. All rights reserved. No part of this book may be used or reproduced in any form or by any means, or stored in a database or retrieval system without prior written permission of the publisher except in the case of brief quotations embodied in critical articles and reviews. Making copies of any part of this book for any purpose other than your own personal use is a violation of United States copyright laws. For information, address Que Corporation, 201 West 103rd Street, Indianapolis, IN 46290 or at support@mcp .com.

Notice: This material is excerpted from Special Edition Using Java, ISBN: 0-7897-0604-0. The electronic version of this material has not been through the final proof reading stage that the book goes through before being published in printed form. Some errors may exist here that are corrected before the book is published. This material is provided "as is" without any warranty of any kind.

Chapter 14 - Extending Java with Standard Languages

Introduction

This chapter explores the topic of interfacing Java language programs with programs (or functions) written in other languages.

In this chapter, you will learn:

Adapting Software to the Internet Era

The Internet and its core enabling technologies, such as the Web, Java, and VRML, have rapidly emerged from the backwaters of university and corporate research labs and have been transformed into mainstream, consumer-oriented mass media. These new media are rapidly complementing or even replacing traditional consumer electronic devices and creating completely new application markets.

This wave of change and emergence of new media markets has created entirely new applications as well as repositioning opportunities for old applications (software, hardware, and services).

One good example of such repositioning of application software is the Internet-enabling of personal finance software, which used to be strictly confined to the users' desktops. But with additional modules added on to provide network connectivity, the old accounting and check balancing programs are being transformed almost entirely into a personalized virtual bank branch office.

The emergence of executable content and transportable code technologies, such as Java, will cause even more transformations in traditional software applications. But in the face of extremely rapid evolution of Internet technology standards, what is the best strategy for vendors of existing stand-alone, shrink-wrapped software to leverage their existing code-base and customer-base and rapidly co-opt emerging technologies, such as Java? One possible answer to this question-getting legacy code written in non-Java languages, such as C and C++, to work with Java-is the topic of this chapter.

Native methods: Co-Opting non-Java programs into the Java Method Invocation System

The approach described in this chapter is a standard feature of Java called native methods. The term native refers to such languages as C and C++. To interface with Java, functions written in these languages are compiled into dynamically loadable objects (dynamic shared libraries on UNIX platforms and DLLs on Microsoft Windows 95/NT platforms) and encapsulated inside a Java method call. At runtime, Java invokes these functions much like it would call any Java method. Thus, Java methods implemented in a language other than Java are called native methods.

Mixing Java with other languages: What you gain and what you lose

The unique features of Java that make it very suitable for networked applications, including executable content using applets, is its properties of full OOP model, robustness, compactness, security, dynamic, and so on. Java has been designed from the ground up to avoid several deficiencies found in older languages, such as C and C++. Mixing non-Java programs with Java code could undermine some of these unique attributes of Java.

On the other hand, Java is so new that there is not yet a whole lot of production quality, commercial grade software base out on the market. Also, software engineers well versed in large-scale Java development are still a minority, and there is a huge installed base of C and C++ software. This makes a compelling case for taking the native method interface route to quickly migrate non-Java

The following is a list of pros and cons of constructing a system containing Java and native methods.

Pros:

Cons:

Interfacing Java with C language programs

This section details the exact process you need to follow to convert a simple C program to work with a Java program via the native method interface.

The example used in this section has the very simple behavior of printing out the contents of the current directory it is running in. The example program has two Java classes: the first implements the main() method for the overall program, and the second, called ListDirJava, has one method-a native method implemented in C that displays the contents of the current directory.

Perform the following seven basic steps to interface a C program with a Java program via the native method interface:

  1. Write the Java code.
  2. Compile the Java code using javac.
  3. Create the .h file using javah.
  4. Create a stubs file using javah.
  5. Write the C function as a .c file.
  6. Create a dynamically loadable object module from the .h file, the stubs file, and the .c file that you created in Steps 3, 4, and 5.
  7. Run the program.

Step 1: Write the Java Code

The following Java code fragment implements a class named ListDirJava that declares and calls the native C function. Enter this code into a class file called ListDirJava.java

Listing 14.1 Declare and Call Native Method

class ListDirJava {

     public native void ListDirC();

     

     static {

          System.loadLibrary("liblistdir");

     }

}

The native keyword signals the ClassLoader that the implementation for the ListDirJava class's ListDirC() method is written in another programming language. This definition provides only the method signature for ListDirC() and does not provide any implementation for it. The implementation is done in a separate C language source file.

The C code that implements ListDirC must be compiled into a dynamically loadable object and loaded into the Java class that requires it. Loading the object into the Java class connects the implementation of the native method to its definition.

The following code fragment from the ListDirJava class in listing 14.1 loads the appropriate library, named liblistdir.

static {

     System.loadLibrary("liblistdir");

}




Next, in a separate source file named Main.java, create a Java Main class that instantiates ListDirJava and calls the ListDirC() native method.

Listing 14.2 Main Program Class for ListDirJava

class Main {

     public static void main(String args[]) {

          new ListDirJava().ListDirC();

     }

}




It is clear from the above code fragment that you call a native method the same way you would call a true Java method. Any arguments to pass into the method can be passed into the parentheses after the method name. The ListDirC() method doesn't take any arguments.

Step 2: Compile the Java code

Use javac to compile the Java code that you wrote in step 1.

Step 3: Create the .h file

In this step, use the javah utility to generate a C header file (a .h file) from the ListDirJava.java class. The header file contains a structure that encapsulates the ListDirJava class on the C side and a C function definition for the implementation of the native method ListDirC() defined in the Java class. On UNIX platforms, you would use the following command in your shell to run javah on the ListDirJava class:

% javah ListDirJava   

By default, javah places the new .h file in the same directory as the .class file. Javah generates the header file with the same name as the class name appended with a .h extension. Therefore, the command shown above will generate a file named ListDirJava.h. Its contents will look like the following:

Listing 14.3 Header File Generated By javah

/* DO NOT EDIT THIS FILE-it is machine generated */

#include <native.h>

/* Header for class ListDirJava */



#ifndef _Included_ListDirJava

#define _Included_ListDirJava



typedef struct ClassListDirJava {

    char PAD;   /* ANSI C requires structures to have a least one member */

} ClassListDirJava;

HandleTo(ListDirJava);



extern void ListDirJava_ListDirC(struct HListDirJava *);

#endif





You can see that the header file contains a definition for a structure named ClassListDirJava. The members of this structure mirror the members of the corresponding Java class. The fields in the structure correspond to instance variables in the class. But because ListDirJava does not have any instance variables, there is just a dummy field in the structure. The members of the structure enable the C function to reference Java class instance variables.

In addition to the C structure that mirrors the Java class, the header file also contains a C function prototype:

extern void ListDirJava_ListDirC(struct HListDirJava *);          

This is the prototype for the C function that you will implement in Step 5. You must use this exact same function prototype when you write the actual C function. If ListDirJava contained any other native methods, their function prototypes would appear here as well. The name of the C function is derived from the Java package name, the class name, and the name of the Java native method. Thus, the native method, ListDirC(), within the ListDirJava class becomes ListDirJava_ListDirC(). In this example, there is no package name because ListDirJava is in the default package.

The C function has one parameter even though the native method defined in the Java class has none. You can think of the parameter as being similar to the this variable in C++. However, in this example, it is ignored.

Step 4: Create a stubs file

Now, use javah with the stubs option on the Java class to create a stubs file. On UNIX systems you would use the command

% javah -stubs ListDirJava

The stubs file contains C code that actually maps the Java class to its parallel C structure. By default, javah will place the resulting stub file in the same directory as the .class file. Like the .h file that javah generated in step 3, the name of the stubs file is the same as the class name with .c appended to the end. In this case, the stubs file is called ListDirJava.c. It will look like the code listing below.

Listing 14.4 Stubs File Generated By javah

/* DO NOT EDIT THIS FILE-it is machine generated */

#include <StubPreamble.h>



/* Stubs for class ListDirJava */

/* SYMBOL: "ListDirJava/ListDirC()V", Java_ListDirJava_ListDirC_stub */

stack_item *Java_ListDirJava_ListDirC_stub(stack_item *_P_,struct execenv *_EE_) {

        extern void ListDirJava_ListDirC(void *);

        (void) ListDirJava_ListDirC(_P_[0].p);

        return _P_;

}



For the purposes of this discussion, all you need to know about the stub file at this point is that you will later compile it into the dynamically loadable library that you create in step 6.

Step 5: Write the C function

Now write the actual C code for the native method in a C source file. The function that you write must have the same name as in the function prototype generated with javah into the ListDirJava.h file in step 3. The function prototype generated looks like this:

extern void ListDirJava_ListDirC(struct HListDirJava *);

The C code for this function looks like the following. You could, of course, embellish it in any way except returning any value back to the calling function. Create the C code in a file called ListDirC.c.

Listing 14.5 C Code Implementing The Native Method

#include <StubPreamble.h>

#include "ListDirJava.h"

#include <stdio.h>

void ListDirJava_ListDirC(struct HListDirJava *this) {

system("ls -l\n");return;

}




As you can see, the C function uses the C system() function call to list the contents of the current directory.

The above C code example will work as is for UNIX users. Users of Microsoft Windows NT/95 and Macintosh platforms should use the directory list command specific to their operating system. The C file includes three C header files:

Step 6: Create a dynamically loadable library

In this step, use appropriate commands specific to your operating system to turn the C code you wrote in step 5 into a dynamically linked library or object file. The command should include both the C file containing the native method implementation as well as the stubs file generated in step 4.

% cc -G ListDirJava.c ListDirC.c -o liblistdir.so

If necessary, use the -I flag to indicate to the compiler where to find the various header files. The C compiler will create a file liblistdir.so in the current directory. On the DOS shell under Windows 95/NT, assuming you are using Microsoft Visual C++ 2.x to generate DLLs, the command looks like this:

C:\> cl ListDirJava.c ListDirC.c -Feliblistdir.dll -MD -LD javai.lib

javai.lib must be the last argument to the C compiler. If necessary, add the directory path containing the Java source files to the environment variable, INCLUDE, and the directory path containing the Java libraries to the environment variable, LIB.

Step 7: Run the program

And finally, use java-the Java interpreter-to run the program. You should see some output showing the contents of your current directory in a format appropriate to your operating system. If you see an exception like the following, then you don't have a library path set up:

java.lang.NullPointerException

at java.lang.Runtime.loadLibrary(Runtime.java)

at java.lang.System.loadLibrary(System.java)

at ListDirJava.(ListDirJava.java:5)

at java.lang.UnsatisfiedLinkError ListDirCat Main.main(Main.java:3)

The library path is a list of directories that the Java runtime system searches when loading dynamically linked libraries. Set your library path now, and make sure that name of the directory where the liblistdir library lives is a part of the library path list.

If you see the following exception, then you have a library path set in your environment, but the name of the directory where the liblistdir library resides is not a part of the path list:

java.lang.UnsatisfiedLinkError no liblistdir in LD_LIBRARY_PATH

at java.lang.Throwable.(Throwable.java)

at java.lang.Error.(Error.java)

at java.lang.LinkageError.(LinkageError.java)

at java.lang.UnsatisfiedLinkError.(UnsatisfiedLinkError.java)

at java.lang.Runtime.loadLibrary(Runtime.java)at java.lang.System.loadLibrary(System.java)

at ListDirJava.(ListDirJava.java:5)

at java.lang.UnsatisfiedLinkError ListDirC

at Main.main(Main.java:3)



Modify your library path, and make sure that the name of the liblistdir library is a part of it.

Interfacing Java with C++ programs

There is a degree of resemblance between Java and C++ in that both are object oriented, and a lot of the operator syntax looks similar. This might lead people to believe that it is relatively straightforward to migrate C++ code to Java. But that is not entirely true.

From an interfacing point of view, the following characteristics differentiate Java from C++:

The consequence of the above two factors is that if you want to call a C++ method from Java, you cannot use the same name you assigned the method in C++. Currently, the only way to interface C++ to Java is to create a C wrapper around the C++ methods and call the C wrapper functions from Java via the native method interface.

The additional downside of doing this includes the following:

Also, from a complete source-level migration point of view, you need to be aware of these additional important differences between C++ and Java:

Alternative ways to interface Java with non-Java language programs

Native methods are not the only way to interface C or C++ programs with Java. In fact, this might not even be possible if the functionality of the non-Java program cannot be neatly segmented and encapsulated into some sort of an API with C bindings and turned into a dynamic shared object.

In such cases, the following alternative approaches may be taken to get the Java program to work with the non-Java language program:

Resources for further reading

Interfacing Java with non-Java language programs is as yet an evolving topic, and, hence, the most topical and current sources of information on this are the following:

QUE Home Page

For technical support for our books and software contact support@mcp.com

Copyright ©1996, Que Corporation