HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

Introduction
JDK1.1 supports JDBC and RMI and will support CORBA in a future release. It becomes a language run not only inside an applet but also on the server side, integrating all kinds of systems such as RDBMS, a bank's mainframe as a CGI script or a lightweight servlet. As of today, CGI script is still the most popular way to link backend to your Web server. However, since Java has no standard API to read the environment variable, such as QUERY_STRING, CONTENT_LENGTH, the current way is to wrap the Java program inside a UNIX shell script and read the environment variables inside a shell, passing them to the Java interpreter on the command line [1]. This article will show you a better and more elegant way. I hope this article will encourage the use of Java on the http server side. To facilitate the development of retrieving http request, I wrote an "HttpRequest" class to handle http requests. I assume the reader has a basic knowledge of CGI and Java.

Drawbacks of Wrapping the Java Class Inside a Shell
The drawback of this approach is obvious: First, another child process has to be forked to load the shell on top of the process running Java interpreter. Second, this approach is so dependent on shell flavors on UNIX (sh, csh, bash, ksh, etc.) that you have to be familiar with different shell syntax. Moreover, on Windows NT you have to write a batch script instead of a shell script unless you are using NT Perl. Finally, if you accidentally put the Java interpreter in your cgi-bin directory, it could be a very dangerous security hole[4].

Solution
Using JNI and Invocation API will solve these problems. As shown in Figure 1, the idea is to create a JVM via the Java Invocation API inside the CGI process space and use getenv() to get all the CGI environment variables. Then, invoke your main Java class via JNI into JVM, and pass all the variables as a hash table indexed by its environment name, such as CONTENT_LENGTH. Thus, there will be only one CGI process running JVM and it can be written in C/C++, which is a lot more portable since the JNI C API is the same across the platform. In addition, there is no Java interpreter involved, making it a lot more secure.

Figure 1
Figure 1:

Steps to Make Things Happen
Step 1: Creating the JVM
If you are on WINDOWS NT, you have to fill the classpath here [3]. The JNI_CreateJavaVM() function loads and initializes a Java VM and returns a pointer to the JNI interface pointer. The thread that called JNI_CreateJavaVM() is considered to be the main thread (see Listing 1).

Step 2: Create a Java hash table to store CGI-related environment variables
At this point, you might think a hash table is too much for this case. The reason for this is that I am going to pass this hash table with the http request name/value pair to the HttpRequest object inside Java code. In this way, you don't need to be bothered with the order of names since it can be looked up by its name, such as "REMOTE_HOST" (see Listing 2).

Step 3: Fill the hash table with (name,value) pair
The code in Listing 3 puts all the cgi-related environment variables (name, value) into a java hash table indexed by its envKeys.

Step 4: Java class entry point
Snoop.main(java.util.Hashtable) serves as the entry point to your Java classes. Of course, you can tailor it to your specific needs. At this point, you might think JNI is so powerful that you can make the entry point other than public staticmain()(string) (see Listing 4). It is.

Java Classes
There are two Java classes. One is "HttpRequest", which models http request and intends to be a generic class to handle CGI-related http requests. The other is "Snoop", which shows how to use HttpRequest. It creates an HttpRequest object from a hash table passed from the C++ side and then uses it to get REQUEST_METHOD, CONTENT_LENGTH, form input data via HttpRequest's instance method, such as getRequestMethod(), getContentLenth() , getParameter() (see Listing 5).

Conclusion
As I said at the beginning, there is only one CGI process being forked here and no java.exe needed on your web server. It makes use of the power of the JNI to facilitate writing CGI script in Java.

References

  1. Marty Hall ([email protected]) Using_Java_for_CGI.html
  2. JavaSoft JNI Specification
  3. JavaSoft JNI Tutorial
  4. NT Perl cgi-bin danger

Source Code
All the Java code has been compiled with Symantec Café with JDK1.1 suppport. All of the executables have been tested with the lastest version of IIS, Frontpage Web Server on the Windows NT platform.

The C++ code "wrapper.cpp" has been compiled and linked with MS VC++ 5.0 to wrapper.exe running as a CGI script.

Java class "Snoop.java".
Java class "HttpRequest.java".

A testing html, "login.html", is a login screen and has two input fields, "username" and "password". It has an html form to use wrapper.exe at localhost's 8080 port.

About the Author
Victor Yang has developed Java/C++ applications in Internet banking and CTI (Computer Telephone Integration) for three years. He is proud to be a team member who built the first Internet bank (http://www.sfnb.com). Presently, he is a senior developer at Footprint Software Inc (http://www.footprint.com) and integrates CTI legacy systems with a Java front end via JNI. During his spare time, he loves to make tools. Victor can be reached at [email protected] or [email protected] Check out his homepage, http://www.interlog.com/~vyang.

	

Listing 1. 

C++ code to create JVM 
JNI_GetDefaultJavaVMInitArgs(&vm_args); 
/* vmArgs is a JDK1_1InitArgs structure. 
please put your JDK1.1.x 's class library path here 
*/ 
vm_args.classpath = ".;c:\\cafe\\java\\lib\\classes.zip"; 
res = JNI_CreateJavaVM(&jvm,&env,&vm_args); 
if (res < 0) { 
fprintf(stderr, "Can't create Java VM\n"); 
exit(1); 
}   

Listing 2. 

C++ code to create a java.util.Hashtable 
// create a Java hashtable 
cls = env->FindClass("java/util/Hashtable"); 
if (cls == 0) { 
    fprintf(stderr, "Can't find HashTable class\n"); 
    exit(1); 
} 
mid = env->GetMethodID(cls, "<init>", "()V"); 
if (mid == 0) { 
   fprintf(stderr, "Can't find HashTable <init> ()V\n"); 
   exit(1); 
} 
 jobject envTable=0; 
envTable=env->NewObject(cls,mid); 
if (!envTable) { 
  fprintf(stderr, "Can't create hashtable\n"); 
  exit(1); 
}
 
Listing 3. 

C++ code to fill the hashtable with name 
char * envKeys[size]={"REMOTE_HOST", 
"REMOTE_USER","HTTP_USER_AGENT",...} char * envValues[size]; // build the 
hashtable from envValues         for (int i=0;i<size;i++) { 
                envValues[i]=getenv(envKeys[i]); 
                if (envValues[i]) { 
                        // create a java string from envValues[i] and 
                        // store name/value pair in the hashtable 
                        jstring name= env->NewStringUTF(envKeys[i]); 
                        jstring value=env->NewStringUTF(envValues[i]); 
                        env->CallObjectMethod(envTable,mid,name,value); 
                } 
        }         
Listing 4. 
 
C++ code to invoke snoop.main(java.util.Hashtable) 
cls = env->FindClass("Snoop"); 
if (cls == 0) { 
  fprintf(stderr, "Can't find Snoop class\n"); 
  exit(1); 
} 

mid = env->GetStaticMethodID(cls, "main", "(Ljava/util/Hashtable;)V"); 
if (mid == 0) { 
  fprintf(stderr, "Can't find Snoop.main(hashtable)\n"); 
  exit(1); 
} 

// call Java code here 
cls = env->FindClass("Snoop"); 
mid = env->GetStaticMethodID(cls, "main", "(Ljava/util/Hashtable;)V"); 
env->CallStaticVoidMethod(cls, mid,envTable); 

Listing 5. 

C++code to invoke snoop.main(java.util.Hashtable) 

import java.util.Hashtable; 

import HttpRequest; 

class Snoop { 

public static void main(Hashtable envTable) { 

// create a request from envrioment vars 

HttpRequest request=new HttpRequest(envTable); 

System.out.println("Content-type: text/html"); 

System.out.println(); 

System.out.println("<html><HEAD><TITLE> Test </TITLE></HEAD><BODY>"); 

System.out.println("<h1> Form Data</h1> "); 

System.out.println("<OL>"); 

System.out.println("<LI> REQUEST_METHOD: "+request.getMethod()+"</LI>"); 

System.out.println("<LI> CONTENT_LENGTH: 
"+request.getContentLength()+"</LI>"); 

System.out.println("<LI> CONTENT_TYPE: "+request.getContentType()+"</LI>"); 

System.out.println("<LI> REMOTE_USER: "+request.getRemoteUser()+"</LI>"); 

System.out.println("<LI> REMOTE_HOST: "+request.getRemoteHost()+"</LI>"); 

System.out.println("<LI> username 
"+request.getParameter("username")+"</LI>"); 

System.out.println("<LI> password 
"+request.getParameter("password")+"</LI>"); 

System.out.println("</OL>"); 

System.out.println("</BODY></html>"); 

} 
 } 




 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.