Friday 15 May 2015

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. 

No comments:

Post a Comment