Friday, 15 May 2015

The Nuts and Bolts of Native Methods in Java

 Because native language methods are so closely tied to a particular platform, the difference in procedures is more pronounced than with the JDK itself. For example, the compilers for different platforms have wildly different command-line arguments. Because of the possible variations and permutations, this section cannot present an exhaustive reference on the native compilers. Therefore, you should always consider the appropriate compiler reference as the ultimate authority. The procedures in these sections worked at the time this was written, using JDK 1.0.2 on the following platforms: SPARC Solaris 2.5, x86 Solaris 2.5, Microsoft Windows 95, Microsoft Windows NT 3.51, and Microsoft Windows NT 4.0 Beta. The Solaris platforms use the usual "cc" compiler. The Windows platforms have been tested using Visual C++ 2.0 and 4.0.

Configuring Your Environment for Solaris

If you have not already done so, you should modify your PATHvariable to include the /java/bindirectory. In addition, setting your JAVAHOMEand CLASSPATH variables properly will help javah run smoothly.
When your class's static block calls System.loadLibrary, the Java runtime will search for the library in the current directory. If you intend to install your dynamic libraries in any other directory, you will need to set your library search path to include that directory. For example, if your libraries are stored in java_libin your home directory, use the following command in Bourne and Korn shells:
$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/java_lib 
$ export LD_LIBRARY_PATH
In C shell, use this:
% setenv LD_LIBRARY_PATH "$LD_LIBRARY_PATH:$HOME/java_lib"

Building Native Methods for Solaris

Use the following command to compile the code and link the dynamic library:
$ cc -G UserInformation.c UserInformationImp.c \
> -o libUserInformation.so
You will probably need to use the -Iflag to tell the compiler where to find the Java header files:
$ cc -G -I${JAVA_HOME}/include -I${JAVA_HOME}/include/solaris \
> UserInformation.c UserInformationImp.c \
> -o libUserInformation.so
The linker will create libUserInformation.soin your current directory. For details about the linker options, refer to the man pages for ccand ld.
To execute System.loadLibrary("libname"), the Solaris Java Virtual Machine will search for a library named liblibname.so.

Configuring Your Environment for Windows 95/NT

You should either make these changes to your C:\AUTOEXEC.BATfile or to a batch file you will run every session.
Setting your JAVA_HOME and CLASSPATH variables properly will help javah run smoothly. In addition, if you have not already done so, you should modify your PATHvariable to include the %JAVA_HOME%\bindirectory.
When your class's static block calls System.loadLibrary, the Java runtime will search for the library in the PATHvariable, as well as the directory in which the executable lives. If you intend to install your dynamic libraries in any directory other than these, you will need to set your path to include that directory.
To make the compile process a little easier, you might want to set the IncLUDE and LIBvariables to point to the Java files:
set IncLUDE=%JAVA_HOME%\include;%IncLUDE% 
set LIB=%JAVA_HOME%\lib;%LIB%

Building Native Methods for Windows 95/NT

Use the following command to compile and link the dynamic library:
C:\> cl UserInformation.c UserInformationImp.c 
-FeUserInformation.dll -MD -LD [other_libs] javai.lib

Note
You must use Visual C++ 2.0 or higher to compile the dynamically linked libraries. In particular, Visual C++ 1.52 or below produce 16-bit code, which will not work with Java.
The linker will create UserInformation.dllin the current directory. For details on the linker options, refer to the Visual C++ online manual.
To execute System.loadLibrary("libname"), the Windows 95/NT Java Virtual Machine will search for a library named libname.dll.

Troubleshooting Native Methods

Here are some common exceptions you might see when you run your program.
java.lang.UnsatisfiedLinkError no hello 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 UserInformation.(UserInformation.java:5) 
   at
java.lang.UnsatisfiedLinkError: Username
   at main.main(main.java:6)
This exception appears on Solaris systems. It means that you have a library path set, but the particular library is not in it. You need to modify your library path to include the directory where your library lives.
java.lang.NullPointerException
   at java.lang.Runtime.loadLibrary(Runtime.java) 
   at java.lang.System.loadLibrary(System.java) 
   at UserInformation.(UserInformation.java:5) 
   at
java.lang.UnsatisfiedLinkError: Username
   at main.main(main.java:6)
This exception also appears on Solaris systems. It indicates that you do not have a library path set at all, and that the runtime cannot find your library without it. You should either set your library path or move your library to the current directory.
Unable to load dll 'UserInformation.dll' (errcode = 485)
Exception in thread "main" java.lang.UnsatisfiedLinkError: 
   no UserInformation in shared library path
   at java.lang.Runtime.loadLibrary(Runtime.java:268) 
   at java.lang.System.loadLibrary(System.java:266) 
   at UserInformation.<clinit>(UserInformation.java:3) 
   at
java.lang.UnsatisfiedLinkError: Username
   at main.main(main.java:6)
This is essentially just the Windows version of the same problem. Again, the solution is to copy the library into a directory that is in the PATH, or to modify the PATH to include the library's directory.
java.lang.UnsatisfiedLinkError: Username 
        at main.main(main.java:6)
If you get this exception by itself, without a larger walkback above it, then your library is missing the function being called by this native method.
As you can see, when you are using native methods, most of the problems show up when the runtime attempts to load the library. Unfortunately, there is no simple solution. This is basically an installation and configuration management problem. Worse yet, there is no way for one class to catch exceptions that occur when the JVM is loading another class.

The Method and the Function

There are two parts to every native method: the Java declaration and the native language definition. The Java Virtual Machine provides enough capabilities that the native language component can do virtually everything that a typical Java method can. This section will examine the two sides of a native method: the Java declaration and the native language definition. The next few sections will examine how the native language component works with the JVM to make all of this work.
Starting with the Java declaration, consider the method signature from the UserInformationexample earlier in the chapter.
public native String Username();
It looks more or less the same as any other method. The nativekeyword means that the method body is not needed. (In fact, it is not permitted.) The nativekeyword tells the compiler that the definition (the method body) is provided in a different language.
In this example, no arguments were needed. However, you can pass arguments to native methods and get return values from them. These arguments and return values can include objects. See "Arguments and Return Values" for specifics.
We used javah to create the header file for the native side. Take a look at the function signature created by javah.
struct Hjava_lang_String *UserInformation_Username(struct HUserInformation *);
By examining this piece by piece, you can see how the Java to C linkage works. First, the return type is declared as struct Hjava_lang_String *. In the native language, all objects are accessed through a handle-a special structures that allows access to an object's instance variables and the class's methods (the object's vtable.) You can translate Hjava_lang_Stringdirectly to "java.lang.String". Handles are always passed as pointers for efficiency.
As you can see, the function name itself is comprised of the class name, an underscore, and the method name itself. If you wanted to add a new native method to find out the user's disk quota, it would be named something like UserInformation_DiskQuota. Because javah can generate all of the function declarations, there is rarely a need to create these names, but because the names are not "mangled" (the way early C++ to C translators did), the resulting code is very readable.
The final item in this signature is the argument struct HUserInformation *. It is an automatic parameter, which will be explained in the next section.

Arguments and Return Values

Pure Java methods can pass arguments to native methods just as they would to any other method call. Native methods can return any value that a Java method would. How does this work when two such vastly different languages are involved? It works because the Java Virtual Machine exposes some of its internals to the native language. At the same time, the header files that support native methods use features of the C preprocessor and compiler to shield you, the developer, from the guts of the Java implementation. Essentially, when you are developing a native method, you are hooking right into the most fundamental aspects of Java's implementation. As a result, you must be careful to follow the rules, or you risk writing fragile code and upsetting the JVM itself.
Simple data types map directly from Java to C. Some difficulties arise because arrays are first class objects in Java, whereas they are aggregate data types in C. Arrays appear in C code as Java objects. Table 19.1 shows the data type mapping from Java to C.
Table 19.1. Mapping some Java data types to C data types.

Java typeC ArgumentC Structure
Primitive Types
booleanlonglong
charlonglong
shortlonglong
ushortlonglong
intlonglong
floatfloatfloat
doublefloatfloat
Complex Types
Objectstruct Hjava_lang_Objectstruct Hjava_lang_Object
boolean[]longlong
char[]unicodestruct HArrayOfChar
short[]longstruct HArrayOfLong
ushort[]unsigned shortunsigned short
int[]longstruct HArrayOfLong
float[]floatstruct HArrayOfFloat
double[]floatstruct HArrayOfFloat
Object[]HArrayOfClassstruct Hjava_lang_Object
The most important argument for any native method is the first parameter passed to all native methods. It is referred to as an automatic parameter. Look back at the example earlier in this chapter. The function UserInformation_Usernamewas passed a struct HUserInformation * as the first (and only) argument. This pointer serves the same function as the implicit thispointer in C++. That is, the first parameter passed to a native method points to the object instance for which that method is being called. You can use this pointer to access the instance variables of the class. With it, you can even call other methods of the object. This automatic parameter takes the form of a handle. (See the sidebar "What are Handles, Anyway?".)
The confluence of handles, multithreading, and garbage collection leads to some interesting consequences for your C code. First of all, your C code must be fully re-entrant. Because a Java application can have an arbitrary number of threads, any one of those threads can call your native method-even if another thread is already executing in that method, unless you synchronize the method. See "Multithreading and Native Methods." Also, remember that the garbage collection runs in an idle thread, but another thread may request that the garbage collector run immediately.
How does this affect your native methods? The garbage collector may relocate objects in memory! As long as you maintain a reference to your arguments, particularly your thispointer, it will not be relocated. However, if you try to store the obj pointer from the object handle (for example, hUserInformation->obj) in a global variable, two very bad things will happen. First, the garbage collector may relocate or even destroy the object. This is because each object has a reference count-a count of the outstanding handles to that object. When the object's reference count hits zero, it is put into a pool that can be garbage collected at any time. When your method returned, the handle passed to it went out of scope and the object's reference count was decremented. Although you still have a copy of the handle, the object may get destroyed without warning. For instance, another thread may cause the reference count to hit zero, ven though your variable still has a pointer. Then your variable points to deallocated memory. The second problem with this scenario comes from the multithreading itself. Any function that uses global data is suspect in a multithreaded program. It is very difficult to prove that a function using global data will behave correctly when faced with re-entrant calls. Here again, the most likely consequence is an invalid pointer. The JVM is well protected, but it can still be crashed. Never believe anyone who tells you that it is impossible to get a core dump or GPF from Java! Anytime native methods are involved, the JVM is only as robust as those native methods.
In Table 19.1, all of the Java array types appear as handles in the object instances. Java treats arrays as first-class types and implements them as objects. When passing an array to a native method, the JVM translates it into a C-like array. However, the array instance variables are still (Java) arrays when accessed from C. Therefore, the C code must treat it as an object.

Returning primitive and complex data types works exactly the same way as primitive and complex arguments. Sometimes, as in the UserInformationexample, this may involve constructing new Java objects in C code. The next section explains how to accomplish this feat. 

A Java Class to Identify the User

 To demonstrate the entire process, from the Java class all the way to the implementation file, we will develop a class that can return the current user's name as a string. This example will use Windows NT native methods.

Note
There is already a mechanism in Java to do this. (See the documentation for the java.lang.System class.) Normally, you should not create a native method to accomplish something already available in the base packages. Because this is a simple demonstration, we will conveniently disregard the System.getProperty method.

The Class Definition

What do we really want to know? For now, we just want to know the user's login name. Various platforms have different API calls to get this information. Good design dictates that we should have a method name that is descriptive of what we want to know, not how we learn it. So, let's just call this method Username. It will return a Java string.
Remember that there are no global functions in Java. Even though we really only need one function, we have to put it in a class. At first, this might seem like a lot of trouble. After all, who wants to define a complete class just to get one method? But think about the future. Will this class always have just one method? Odds are that, like everything else, the requirements for this class will evolve over time. Perhaps there are other details about users that would be of interest to your application (or even a future application). The extra few minutes spent here in creating a class give you a framework in which to build additional functionality later.
Listing 19.4 shows the Java source for the UserInformationclass.

Listing 19.4. The Java class definition of UserInformation.
 1:  class UserInformation {
 2:     static {
 3:        System.loadLibrary("UserInformation"); 
 4:     }
 5:     public native String Username(); 
 6:  }

Here is a line-by-line description of the code:
Line 1: This line defines the class UserInformation. There is nothing fancy about this class; it inherits directly from java.lang.Object. Although this class does not show it, it is usually a good idea to make native methods-sometimes entire classes-"final." Otherwise, anyone can inherit from your native methods and possibly subvert their behavior.
Line 2: This line defines a static block of code. Static blocks are executed when the class itself is loaded. You can think of a static block as a constructor for the class as a whole.
Line 3: When this class is loaded, direct the runtime system to load the dynamically loadable library UserInformation. Exactly how the library is located and loaded varies by target platform. See the appropriate "Configuring Your Environment" section for your platform later in this chapter.
Line 5: This line defines the method Usernameas a native method that returns a Java Stringobject.
Now, compile this class using javac. 
> javac UserInformation.java
This will produce the usual class file UserInformation.class. The next step is to create the header and stub files using javah.
> javah UserInformation
> javah -stubs UserInformation
This will produce two files, UserInformation.hand UserInformation.c. These are the header and stub files. You can disregard UserInformation.c(until it is time to compile, of course.) Listing 19.5 contains the header file UserInformation.has created by javah. Your output file may vary in some details, depending on your platform and version of the JDK.

Listing 19.5. The header file UserInformation.hcreated from the UserInformation.classclass file.
 0   /* DO NOT EDIT THIS FILE - it is machine generated */
 1   #include <native.h>
 2   /* Header for class UserInformation */ 
 3
 4   #ifndef _Included_UserInformation
 5   #define _Included_UserInformation
 6
 7   typedef struct ClassUserInformation { 
 8       char PAD;   /* ANSI C requires structures to have at least one member */
 9   } ClassUserInformation;
 10  HandleTo(UserInformation);
 11
 12  #ifdef __cplusplus
 13  extern "C" {
 14  #endif
 15  struct Hjava_lang_String;
 16  extern struct Hjava_lang_String
      *UserInformation_Username(struct HUserInformation *);
 17  #ifdef __cplusplus
 18  }
 19  #endif
 20  #endif

Notice the structure definition on lines 7 through 9. Because we did not define any members of the Java class, javah created this dummy structure. This will keep C compilers happy, but you should not attempt to use PADto store information. The Java specification makes no guarantees about the contents of PAD. For our purposes, the only line of real interest is line 16, where the declaration for Usernameappears. This is the function that our implementation file must define. Notice that instead of taking a pointer to a ClassUserInformationstructure, the C function gets a pointer to an HUserInformationstructure. See the sidebar "Where Did HUserInformationCome From?" for details on this structure.

Where Did Come From?
The structure HUserInformation, which is passed to each of the C functions, is declared as a result of the HandleTo() macro, which is defined in \java\include\oobj.h as this:
#define HandleTo(T) typedef struct H##T { Class##T *obj; \
struct methodtable *methods;} H##T
So, HandleTo(UserInformation); expands to
typedef struct HUserInformation {
  ClassUserInformation *obj;
  struct methodtable *methods;
} HUserInformation;
This structure provides the bookkeeping that will allow C functions to behave like class methods. To access an instance variable from your native method, follow the obj pointer to the instance variable structure declared by the header file. For example, if the Java class UserInformation had a member variable UserID, its native methods could reference that member as hUserInformation->obj->UserID.
The methods pointer also allows your native method to get information about, and even invoke, the other methods of your class.
This structure is intended to be opaque to native methods. Because it may change in the future, the include file interpreter.h provides macros to hide the details of HUserInformation. In your native methods, you would use unhand(hUserInformation) to get access to the ClassUserInformation pointer. By using the macros, you insulate your code from future changes to Java internals. See "Using Java Objects from Native Methods" later in this chapter for details.
Finally, the time has come to write the implementation file. By convention, the implementation filename ends in "Imp". For UserInformation, our implementation file is named UserInformationImp.c. Listing 19.6 shows the implementation file for UserInformation, followed by a line-by-line description of the code. (We will not explore the target platform's API functions. After all, this is a book about Java, not C.)

Listing 19.6. The implementation file UserInformationImp.c.
 1:  /* UserInformationImp.c                                    */ 
 2:  /* Implementation file for Java class UserInformation      */ 
 3:  #include <StubPreamble.h>
 4:  #include "UserInformation.h"
 5:  #include <winnetwk.h>
 6:
 7:  struct Hjava_lang_String *UserInformation_Username( 
 8:     struct HUserInformation *hUserInformation 
 9:  )
10:  {
11:    char szUserName[128];
12:    DWORD cchBuffer = sizeof(szUserName); 
13:    if(NO_ERROR == WNetGetUser(NULL,szUserName,&cchBuffer)) 
14:       return makeJavaString(szUserName, sizeof(szUserName));
15:    else {
16:       printf("UserInformation_Username: GetLastError = %x\n",
17:          GetLastError()); 
19:       return makeJavaString("", 0);
20:    }
21: }

Here is a line-by-line description of the implementation file:
Line 3: Every native method implementation file must include StubPreamble.h. Indirectly, it provides all of the macros, definitions, and declarations that enable C code to interoperate with the Java Virtual Machine.
Line 4: Include the header file created by javah. This brings in the structure definitions ClassUserInformationand HUserInformation.
Line 5: Include the Win32 header file, which defines WNetGetUser-the function we will use to find the user's name.
Lines 7-9: This function signature must be identical to the corresponding function declaration in UserInformation.h.
Lines 11-12: Declare two local variables-a character array that will hold the C string of the user's name, and a double word that will indicate the buffers size.
Line 13: Retrieve the user's login name by calling WNetGetUserand checking the return value for failure. If WNetGetUserfails because the buffer is too small, cchBufferwill contain the actual buffer length needed.
Line 14: If the call to WGetNetUsersucceeded, construct a Java string object from the C string. The function makeJavaString is declared in javaString.h, along with several other functions which C code can use to manipulate Java string objects. Not all Java classes have such convenient C interfaces. In general, you will use the classes' inherent capabilities by calling the Java code from your C code.
Lines 15-19: If the call to WGetNetUserfailed, print an error message on stdoutand construct an empty Java string to return. Returning an empty string is more conscientious than returning a NULL. Java has garbage collection, so you do not need to worry about the eventual destruction of these strings.
Now we just need to compile and link the two C files. For complete details on building the code, see the following sections called "Building Native Methods for Solaris" and "Building Native Methods for Windows 95/NT," whichever is appropriate for your target platform. For now, the sample makefile in Listing 19.7 will make things more convenient. (It will also make sure that your header and stub files stay current with respect to your .java file.) Remember to put tabs, not spaces, at the beginnings of the action lines.

Listing 19.7. Sample makefile suitable for use on Windows 95/NT.
 cc         = cl
 LFLAGS     = -MD -LD
 CLASSPATH  = .;$(JAVA_HOME)\lib\classes.zip
 
 CLASS      = UserInformation 
 
 all: $(CLASS).dll $(CLASS).class main.class
 
 main.class: main.java
     javac main.java
 
 $(CLASS).class: $(CLASS).java
     javac $(CLASS).java
 
 $(CLASS).dll: $(CLASS)Imp.c $(CLASS).class
     javah -classpath $(CLASSPATH) $(CLASS) 
     javah -stubs -classpath $(CLASSPATH) $(CLASS)
     $(cc) $(CLASS)Imp.c $(CLASS).c -Fe$(CLASS).dll $(LFLAGS) mpr.lib javai.lib

Once you compile your native methods into a dynamically loadable library, you can create a simple class to exercise this native method from an application. When you are testing an application that uses native methods, the command-line Java interpreter "java" can be a tremendous help. In this case, the class mainsimply creates an instance of UserInformationand prints the results of Username(). 
>java main
Your username is: mtnygard

Now that you have gone through the entire process one step at a time, you can delve into the nuts and bolts. The next sections will refer back to this example from time to time as they explore the details of the interaction between native methods and pure Java code. 

An Overview of Native Methods

 All functions in Java are object methods. Even the declaration part of your native methods must be contained within a class. Therefore, the first step to creating native methods is to define one or more class interfaces. If you are dealing with a large number of functions, it will be useful for you to partition them into logical, coherent groups. These should form the nucleus of each class. There are basically two approaches for defining a class interface that will wrap the native methods. Suppose you are dealing with native methods to provide file access. Assume that your target platform's standard runtime library supports multithreading. If it does not, you will have to serialize access to all RTL functions.
One approach we could take is to create a Fileclass that contains a mixture of native and Java methods that implement an abstraction of a disk file. (This is the approach used most often by the Java development team.) As an alternative design, we could create two classes: a Fileclass that presents an abstract view of files, and a FileImpclass that contains all of the native methods relevant to files. The File class would then contain a reference to a single global instance of FileImp.






Note
For concrete details on how the Java development team designed this solution, look in the JDK source code. The source code is available from JavaSoft at http://java.sun.com/products/JDK/1.0.2. Check out the file src/java/io/File.java. The File class is an excellent example of integrating native methods with Java methods.
Ultimately, both techniques result in a Fileclass that presents a clean abstraction of a file. The first approach (the single-class approach) has a significant drawback in terms of maintainability, however. Whenever the Fileclass changes, the dynamically loadable library must be rebuilt. In fact, you would need to regenerate the C header and C stub files and recompile the library for every interface change in File.
With the second approach (the split interface), FileImpis insulated from changes to File. Likewise, changes to FileImpdo not affect File. The methods of FileImp do not need to be a direct mapping from the target API. You can give them abstract names and easily define an interface, which will rarely (if ever) need to change. For example, the POSIX API defines two sets of file-manipulation functions. Roughly, the sets correspond to those which use handles returned by open, and those which use a structure pointer returned by fopen. The Win32 API defines a group of file-manipulation functions that use a Handle returned by CreateFile. Your FileImpclass would define a single method called Openas illustrated by Listing 19.1. Now any client of Filecan call Open without regard to the underlying system-level API.

Listing 19.1. Definition of the native Openmethod.
class FileImp {
      .
    public native boolean Open(String Pathname);
      .
 }

After you have designed your class interfaces, you will need to create the Java classes. These will be identical to any other Java class, except for the use of the keyword native. Native signals to javac that this method body will be provided by a library loaded at runtime. Therefore, you do not provide a method body for a native method. The method declaration looks the same as if you were declaring an abstract method. This is no coincidence. In both cases, you are telling javac that you want to defer the definition of this method until later. With an abstract method, you are saying that "later" means a subclass. With a native method, "later" means runtime loadable C code.


When and Where Can I Use "nativ"?
You can put "native" on almost any method declaration. How does javac interpret an abstract, native method? Not very well. You can provide a method body for an abstract native method in C, but it will never be called. A static native method behaves just as you would expect. Its semantics are the same as they would be in a static method defined in Java. A static native method is always passed a NULL for its this argument.
Native methods that are overridden in a subclass work normally. In fact, you can mix native and Java methods arbitrarily when subclassing.
After creating and compiling your Java classes, you will use javah to create two C files: a header file and a stub file. The header file contains a structure definition and function declarations (prototypes) for the native methods. The stub file contains some "glue" code, which you should never need to change. Because both files created by javah are generated automatically, you should never change either of them. javah maps the instance variables and methods of the class directly into the C files. So, if you ever change the Java class, you must regenerate the C files. If you do not, strange and unpredictable behavior will result. For example, javah created the header in Listing 19.3 from the class in Listing 19.2.

Listing 19.2. This is the original PortfolioEntryclass.
class PortfolioEntry {
    String  TickerSymbol;
    float   LastQuote;
    float   BoughtAtPrice;
    float   LastDividends;
    float   LastEPS;
 }


Listing 19.3. The structure definition javah created from PortfolioEntry.
typedef struct ClassPortfolioEntry {
     struct Hjava_lang_String *TickerSymbol;
     float LastQuote;
     float BoughtAtPrice;
     float LastDividends;
     float LastEPS;
 } ClassPortfolioEntry;

Now suppose that you add a new integer member, NumberOfShares, to the Java class, right after the TickerSymbol. If you do not recreate the header file, your C code will be using the old structure layout. Therefore, your C code would access every member from LastQuote. Your C code would use the offset for NumberOfShares(an integer) as if it were LastQuote(a float)! This mismatch can cause some of the most irreproducible and hard-to-find bugs. A similar problem can arise with the methods. In that case, you are even more likely to have problems, because any change in a method's signature means that you have to regenerate the stub and header files.
One good way to avoid this problem is to make sure that you rarely need to change your classes after you write them. Good design will help there, but changes are inevitable. Creating a makefile is the best way to ensure that you always use the most up-to-date header and stub files. Some version of "make" is available for every platform supported by the JDK. Later in this chapter, you will find sample makefiles for Java classes with native methods.
Javah creates two files for you, but it leaves the most important file up to you. Along with the header and stub files, you will need to write an implementation file. The implementation file must contain the definitions of the functions that the header file declares. All of the really interesting things happen in the implementation file. Figure 19.2 depicts the relationships among the four files-the Java source, the C header, the C stubs, and the C implementation files.


Figure 19.2: The relationship between the Java class file and the three C files..
At this point, a simple example will illustrate the interaction between the Java class and the three C files.


Extending Your Programs with Native Methods in Java

 The Java Virtual Machine provides sophisticated capabilities for creating user interfaces, performing Internet and WWW accesses, and for general-purpose programming. Unfortunately, the Java Virtual Machine has some (deliberate) limitations. First and foremost, the JVM only supports functionality that applies to many platforms. That means that unaided Java applications cannot take full advantage of their host platforms. The Java development team also introduced some artificial limitations in the interest of security. For the most part, these restrictions are not onerous. In fact, users can have a relatively high degree of confidence that Java applications and applets are not malicious. Without such assurances, the World Wide Web community would surely have revolted at the notion of self-downloading, self-executing code in a Web page. Just imagine the prospect of a cross-platform virus that attaches itself to Web pages! In some cases, however, these benevolent restrictions prevent applications from making the most of Java.
Consider these situations:
  • You have a large, specialized library of C or C++ functions that you would like to use from Java applications.
  • Your application must use a non-standard (that is, non-JDBC) database access library.
  • An existing application that communicates through an operating system-specific channel (such as Windows DDE or UNIX shared memory) must be integrated with a new Java application.
  • Your application needs to automatically determine operating system-specific information, such as the user's Netware ID.
Native methods can solve all of these problems. A Java class can declare a native method to indicate that the actual code for the method is provided in another language. (At present, native methods must be implemented in C. Support for other languages is in the works.) That code is compiled to the native machine code of your particular platform, hence the name "native method." Before it can be used by the Java Virtual Machine, the compiled code must be linked into a dynamically loadable library suitable for use on the target platform. Finally, the DLLor .so file must be installed on the target computer.
There are two parts to every native method, the Java declaration and the "native" implementation. As you do with all other methods, you declare a native method inside a Java class. Native methods can be final, static, or synchronized. They can throw exceptions and be inherited (unless they are final, of course). Because the implementation is compiled for the target platform, it can take full advantage of the capabilities of that platform. Native methods provide a powerful means of extending the Java Virtual Machine.
You have probably guessed by now that native methods are not cross-platform by nature. Your native methods will be as portable as the C code in which you write them and the APIs they call. Because the most common reason for creating native methods is to use a platform-specific API, native methods tend not to be very portable.
Because the dynamically loadable library must be installed on the target machine prior to execution of the Java classes that need it, you will have to face all the issues of software distribution and configuration management. On the other hand, native methods can only be called from Java applications, which have to be distributed and installed themselves. So, the added difficulty of distributing multiple native-method libraries should not pose too great a hurdle.


When Not to Use Native Methods
Here are some questions to ask yourself before you decide to use native methods:
  • Are you writing a full-blown application instead of an applet? Some Web browsers will prevent applets from calling native methods not distributed in the base classes. In some cases, the browser allows the user to enable native method calls from applets. Bear in mind that many users will be reluctant to deliberately short-circuit security features.
  • Can you manage the platform-specific code? Because the native methods are compiled for each platform, you will encounter significant configuration management hassles. More than likely, you will also have to cope with multiple versions of the native methods, one for each platform. This is particularly true when you are dealing with operating system features. (I know, C is supposed to be a portable language, but isn't that why you are using Java?)
  • Is it acceptable to mix object-oriented and procedural code? Keep in mind that the native methods will be written in an object-oriented style of C. You will be working with regular C structures instead of classes. These structures do not provide full information hiding. (No protected or private specifiers.) Ultimately, you will be working in C, with no encapsulation, no inheritance, and no polymorphism. It will require great discipline to maintain good object semantics in C. Most native methods are self-contained, so these should not be serious limitations, but you should be aware of the difficulties ahead.
If you answered "yes" to all of these questions, then feel free to proceed. If any of the questions trouble you, then you should probably think twice before implementing that class with native methods. The Java packages contain hundreds of classes, and the method you are looking for just might be there already, buried within an obscure class.

Thursday, 14 May 2015

The Java Security Model

Now that you know what restrictions the Java security system enforces, you'd probably like to know how it does this. The answer is shown in Figure 18.2, which shows the four layers in the Java security model. Each layer provides a unique barrier to the construction and execution of applets with evil intent.

  
Now let's examine each of the layers in Figure in detail.

he Compiler and the Java Language

One of the first barriers to rogue applets is the design of the Java language. Because Java doesn't have pointers and is strongly type checked-so much so that you can't cast an integer to an object reference-many of the traditional security holes found in C and C++ are plugged. For example, because there are no pointers, Java applets can't invade the memory space of another program. These architectural features are actually beneficial in that they eliminate the most common coding errors that cause problems in C++. In fact, the lack of pointers and the automatic garbage collection will probably lead to a factor of two or more reduction in software development costs for any application that doesn't require low-level control of the machine-such as a device driver-because of the time saved tracking down pointer-related errors in the code.
The compiler plays a role in this by enforcing the rules and prohibiting various types of potentially dangerous casts.

The ByteCode Verifier

This is the heart of the Java security scheme. If someone figures out how to get past this, real trouble can arise. The basic job of this layer is to verify every program before it's run. This protects you against someone hand-coding a class file that violates the Java security rules or developing a compiler that doesn't enforce the Java security rules.
The ByteCode verifier is a very powerful guard which first verifies that the file is a properly structured Java class file. The next step is complex and involves proving certain theorems about the class. The process guarantees the following about the applet:
  • It doesn't cause stack over- or underflows.
  • Operators' arguments are of the proper type: An integer isn't being used with an operator that requires an object reference for example.
  • Objects are only accessed according to the approved rules; for example, this refers to accessing private and protected methods.
  • No casts that violate Java security rules are attempted.
This approach has a significant side benefit of making the interpreter run faster, because it doesn't have to check for any of these problems.

The ClassLoader

After a class has been verified, it can be loaded into the Java system. In order to prevent applets from replacing security-related classes and thereby breaking the security system, Java divides the namespace into several levels. Currently there are three levels: the local file system-most protected; the local network-middle protection; and the Internet-least protected. The class loader won't replace a class in a more protected level with a class from a less protected level. The really key classes, such as those that control I/O, are in the local level so no applet that is coming from another machine can override and replace them.
Additionally, the class loader prevents classes in one layer from accessing any non-public methods in classes in other layers. In a similar fashion, when multiple applets are loaded, they're placed in separate namespaces so that they can't interact unless they're designed to do so.

The SecurityManager

This is actually an abstract class that is designed to allow you to tailor the security policies that a browser or application will enforce. Applets can't modify the SecurityManager, but applications can extend it and define their own security policies. Your security policies, as determined by your SecurityManager, determine what types of dangerous operations an applet can perform. For example, the Netscape SecurityManager prohibits all file reading/writing, but other browser SecurityManagers could allow different levels of file access depending upon the source of the applet.

These four levels working together provide a nearly airtight defense against malicious applets. There are still some security problems (researchers have been finding a new problem every few months), but they are fairly difficult to exploit. In fact, the real problem is that, because the security system limits file and network I/O (a key reason for the wonderfulness of networks and computers), there are too many powerful and useful applications that can't be written.  

Java Security Issues

  If Java succeeds as the Internet language, it will have to provide a workable and effective security system. The reason is simple: How many people would risk running an applet from some unknown Web site if the applet could destroy their hard disk, make it look as though they'd sent threatening mail to the President, or steal their passwords? Even if individuals would take this chance, companies would not, because a poorly secured Java applet could destroy the effectiveness of their own system's firewalls.



Note
A firewall is a computer that controls the flow of information between the Internet and a company's internal networks. Its mission is to let the folks on the inside access any information they need on the Internet while preventing folks on the outside from seeing what's on the company's computers. As a result, machines on the Internet can't establish connections with machines behind the firewall, but machines behind the firewall can establish connections with machines outside the firewall. Remote users can get behind the firewall if they have the appropriate security software/keycards-a daunting task even if you're authorized.


Local machine A runs a hostile applet that it downloads from the evil Hostile Applet Server, which resides somewhere out on the Internet-perhaps in Chernobyl. That applet opens a network connection to machine C, which contains valuable corporate secrets-like the toupée vendor the boss uses-and steals all of the data, which it sends back to the Hostile Applet Server. While it's doing all this, the poor idiot working on machine A thinks he's just playing some game.

Essentially, the firewall couldn't distinguish a Java applet running on an internal machine from a user using a browser on the same machine. As a result, the firewall wouldn't stop the applet from reading files from a company's machine and sending them to some other company's machine-not too good from a security perspective. Even worse scenarios arise if a Java applet can write files on the client system. The potential for Trojan horses and viruses would be unlimited. Clearly, people wouldn't be willing to wander around the World Wide Web as they currently do if any site could inflict this type of damage.

The bottom line is that, if Java weren't designed to provide security, the probability of anyone using it on the WWW would be fairly small.




Note
A Trojan horse is an application that pretends to be useful but actually performs some bad actions on a computer. An example might be a program that is supposed to be a freeware word processor but which actually erases your hard disk. Trojan horses are different from the products of software companies in that they are designed to cause problems.  

The Differing Security Aspects of Applets and Applications

  If you download applications from the Internet, you probably check them with a virus scanner before running them. In general, using applications you get from an unknown source is buyer-beware activity where you know there are risks involved. Most of the time things are fine. I've found only two infected files in all my years surfing BBSs and the Net with my trustworthy Mac-but Mac viruses are much less common than IBM ones. However, if a problem occurs and you don't fix it quickly, you can lose lots of time and money. Because a Java application is no different than any other application, it has no more purely security-related constraints than any other language. Applets, though, are different.

Unlike the people who frequently download applications, most folks who encounter Java applets are real people, not computer experts. When you surf to a given URL, the applets on any of its pages automatically load and execute-unless you've set your browser to keep from running applets. You don't have a chance to run a virus checker or to control the execution environment of the applet. As a result, if you didn't know that Java applets couldn't hurt you, you'd tend to set your browser to avoid running Java. But if most browsers are set not to execute Java applets, a significant part of the appeal of Java goes away. That's why there are extensive security features in Java applets that aren't there for Java applications. The next section walks you through them.


Note
When you use Java to write an application, security restrictions are pretty much the same as writing an application in C or SmallTalk.