Integrating FastCGI with Java
/fastcgi/words
fcgi-hd.gif
[FastCGI]
Integrating FastCGI with Java
Steve Harris
Open Market, Inc.
7 May 1996
Copyright © 1996 Open Market, Inc. 245 First Street, Cambridge,
MA 02142 U.S.A.
Tel: 617-621-9500 Fax: 617-621-1703 URL:
http://www.openmarket.com/
Java is an object-oriented programming language developed by Sun
Microsystems. The Java Depvelopers Kit (JDK), which contains the basic
Java class packages, is available from Sun in both source and binary
forms at Sun's
JavaSoft
site. This document assumes that you have some familiarity with the
basics of compiling and running Java programs.
There are two kinds of applications built using Java.
- Java Applets are graphical components which are run off
HTML pages via the <APPLET> HTML extention tag.
- Java Applications (Apps) are stand-alone programs
that are run by invoking the Java interpreter directly. Like
C programs, they have a main() method which the interpreter
uses as an entry point.
The initial emphasis on using Java for client side applets should not
obscure the fact that Java is a full strength programming language
which can be used to develop server side stand alone applications,
including CGI and now FastCGI applications.
The remainder of this document explains how to write and run FastCGI Java
applications. It also illustrates the conversion of a sample Java CGI program
to a FastCGI program.
Writing a FastCGI application in Java is as simple as writing one in C.
- Import the FCGIInterface class.
- Perform one-time initialization at the top of the
main() method.
- Create a new FCGIInterface object and send it an
FCGIaccept() message in a loop.
- Put the per-request application code inside that loop.
On return from FCGIaccept() you can access the request's environment
variables using System.getProperty and perform request-related
I/O through the standard variables System.in,
System.out, and System.err.
To illustrate these points, the kit includes examples/TinyCGI,
a CGI Java application, and examples/TinyFCGI, the FastCGI
version of TinyCGI. These programs perform the same
functions as the C programs examples/tiny-cgi.c and
examples/tiny-fcgi.c that are used as examples in the
FastCGI Developer's Kit document.
A. TinyCGI
class TinyCGI {
public static void main (String args[]) {
int count = 0;
++count;
System.out.println("Content-type: text/html\n\n");
System.out.println("<html>");
System.out.println(
"<head><TITLE>CGI-Hello</TITLE></head>");
System.out.println("<body>");
System.out.println("<H3>CGI Hello</H3>");
System.out.println("request number " + count +
" running on host "
+ System.getProperty<"SERVER_NAME"));
System.out.println("</body>");
System.out.println("</html>");
}
}
B. TinyFCGI
import FCGIInterface;
class TinyFCGI {
public static void main (String args[]) {
int count = 0;
while(new FCGIInterface().FCGIaccept()>= 0) {
count ++;
System.out.println("Content-type: text/html\n\n");
System.out.println("<html>");
System.out.println(
"<head><TITLE>FastCGI-Hello Java stdio</TITLE></head>");
System.out.println("<body>");
System.out.println("<H3>FastCGI-Hello Java stdio</H3>");
System.out.println("request number " + count +
" running on host "
+ System.getProperty<"SERVER_NAME"));
System.out.println("</body>");
System.out.println("</html>");
}
}
}
C. Running these Examples
We assume that you have downloaded the JDK and the FastCGI Developer's
Kit, and that you have a Web server running that can access the
fcgi-devel-kit/examples directory. In all cases where we
specify paths, we are using relative paths within
fcgi-devel-kit or the JDK which will need to be enlarged to a
full path by the user.
Configuring
- Add your JDK's java/bin directory to your Unix PATH
if it isn't there already.
- Add the directories fcgi-devel-kit/examples and
fcgi-devel-kit/java/classes to your Java
CLASSPATH.
- In your Open Market Secure WebServer configuration file,
httpd.config, add the following two lines:
ExternalAppClass TinyFCGI -host hostName:portNum
Responder TinyFCGI fcgi-devel-kit/examples/TinyFCGI
- hostName is the name of your host machine.
- portNum is the port that you've selected for
communication between the Web server and the Java application.
On other servers you can use cgi-fcgi to get a
similar effect.
- Create a soft link examples/javexe to the
java/bin directory in your JDK.
This link is required only to run the
CGI scripts examples/TinyCGI.cgi and
examples/TinyFCGI.cgi, which use it to
invoke the Java interpreter java/bin/java.
It is not used by FastCGI applications.
- You might have to modify examples/TinyFCGI.cgi to use a
Unix shell for which your CLASSPATH is defined.
Running
- To run TinyFCGI as FastCGI, you invoke the Java interpreter
with the -D option, giving it the FCGI_PORT environment
variable
and the same portNum that was used in the Web server
configuration. The command is:
java -DFCGI_PORT=portNum TinyFCGI
Then point your browser at fcgi-devel-kit/examples/TinyFCGI.
Notice that each time you reload, the count increments.
- To run TinyCGI, point your browser at
fcgi-devel-kit/examples/TinyCGI.cgi on your host machine.
Notice that the count does not increment.
- Finally, you can run TinyFCGI as a straight CGI program by pointing
your browser at fcgi-devel-kit/examples/TinyFCGI.cgi. The results
are exactly the same as when you ran TinyCGI. Invoking a FastCGI
program without an FCGI_PORT parameter tells the
FastCGI interface
to leave the normal CGI environment in place.
Due to gaps in the Java interpreter's support for listening
sockets, Java FastCGI applications are currently limited to
being started as external applications. They can't be started and
managed by the Web server because they are incapable of using
a listening socket that the Web server creates.
As we have seen above, FastCGI for Java offers a redefinition
of standard I/O corresponding to the the fcgi_stdio functionality.
It also offers a set of directly callable I/O methods corresponding to
the fcgiapp C library. To understand where these methods occur
we need to look briefly at the FastCGI redefinition of standard I/O.
Java defines standard I/O in the java.System class as follows:
public static InputStream in = new BufferedInputStream(new FileInputStream(FileDescriptor.in), 128);
public static PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out), 128), true);
public static PrintStream err = new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.err), 128), true);
The File Descriptors in, out, err are constants set to 0, 1 and 2 respectively.
The FastCGI interface redefines java.System in, out, and err
by replacing the File streams with Socket streams and inserting streams
which know how to manage the FastCGI protocol between the Socket streams
and the Buffered streams in the above definitions.
For those cases where the FCGI application needs to bypass the standard I/O
streams, it can directly access the methods of the FCGI input and output
streams which roughly correspond to the functions in the C fcgiapp
library. These streams can be accessed via the request class variable
in FCGIInterface. Each Request object has instance variables that refer to an
FCGIInputStream, and to two FCGIOutputStreams associated with that request.
Java does not use the C environ list. Nor is there a getenv
command that reads system environment variables. This is intentional for
reasons of portability and security. Java has an internal dictionary of
properties which belongs to the System class. These System properties
are name/value associations that constitute the Java environment.
When a Java application starts up, it reads in a file with default properties.
As we have seen, additional System properties may be inserted by using
the -D Java command argument.
For CGI, where the Java application is invoked from a .cgi script that,
in turn, invokes the Java interpreter, this script could read the environment
and pass the variables to the Java application either by writing a file
or by creating -D options on the fly. Both of these methods are somewhat
awkward.
For FastCGI Java applications, the environment variables are obtained from
the FastCGI web server via FCGI_PARAMS records that are sent to the
application at the start of each request. The FastCGI interface stores the
original startup properties, combines these with the properties obtained
from the server, and puts the new set of properties in the System properties
dictionary. The only parameter that has to be specifically added at startup
time is the FCGI_PORT parameter for the Socket creation. In the future, we
expect that even this parameter won't be needed, since its use is due to an
acknowledged rigidity in the JDK's implementation of sockets.
The next two examples illustrate the points made in the last two sections.
EchoFCGI and Echo2FCGI both echo user input and display the application's
environment variables. EchoFCGI reads the user input from System.in, while
Echo2FCGI reads the user input directly from the intermediate FastCGI input
stream.
A. EchoFCGI
import FCGIInterface;
import FCGIGlobalDefs;
import java.io.*;
class EchoFCGI {
public static void main (String args[]) {
int status = 0;
while(new FCGIInterface().FCGIaccept()>= 0) {
System.out.println("Content-type: text/html\n\n");
System.out.println("<html>");
System.out.println(
"<head%gt;<TITLE>FastCGI echo
</TITLE></head>");
System.out.println("<body>");
System.out.println(
"<H2>FastCGI echo</H2>");
System.out.println("<H3>STDIN</H3>");
for ( int c = 0; c != -1; ) {
try {
c = System.in.read();
} catch(IOException e) {
System.out.println(
"<br><b>SYSTEM EXCEPTION");
Runtime rt = Runtime.getRuntime();
rt.exit(status);
}
if (c != -1) {
System.out.print((char)c);
}
}
System.out.println(
"<H3>Environment Variables:</H3>");
System.getProperties().list(System.out);
System.out.println("</body>");
System.out.println("</html>");
}
}
}
B. Echo2FCGI
import FCGIInterface;
import FCGIGlobalDefs;
import FCGIInputStream;
import FCGIOutputStream;
import FCGIMessage;
import FCGIRequest;
import java.io.*;
class Echo2FCGI {
public static void main (String args[]) {
int status = 0;
FCGIInterface intf = new FCGIInterface();
while(intf.FCGIaccept()>= 0) {
System.out.println("Content-type: text/html\n\n");
System.out.println("<html>");
System.out.println(
"<head><TITLE>FastCGI echo
</TITLE></head>");
System.out.println("<body>");
System.out.println("<H2>FastCGI echo</H2>");
System.out.println("<H3>STDIN:</H3">);
for ( int c = 0; c != -1; ) {
try {
c = intf.request.inStream.read();
} catch(IOException e) {
System.out.println(
"<br><b>SYSTEM EXCEPTION");
Runtime rt = Runtime.getRuntime();
rt.exit(status);
}
if (c != -1) {
System.out.print((char)c);
}
}
System.out.println(
"<H3>Environment Variables:</H3>");
System.getProperties().list(System.out);
System.out.println(<"/body>");
System.out.println("</html>");
}
}
}
C. Running these Examples
Configuring
As with TinyFCGI, you need to configure the web server to recognize these
two FastCGI applications. Your configuration now looks like this:
ExternalAppClass java1 -host hostname:portNum
Responder java1 fcgi-devel-kit/examples/TinyFCGI
ExternalAppClass java2 -host hostname:portNumA
Responder java2 fcgi-devel-kit/examples/EchoFCGI
ExternalAppClass java3 -host hostname:porNumB
Responder java3 fcgi-devel-kit/examples/Echo2FCGI
Note that the application classes and port numbers are different for each
application.
Running
As with TinyFCGI, you need to run these programs with the -D option
using FCGI_PORT and the appropriate port number.
To get some data for standard input we have created two html pages with
forms that use a POST method. These are echo.html and echo2.html. You must
edit these .html files to expand the path to fcgi-devel-kit/examples
to a full path. Once the appropriate Java program is running, point your browser at the corresponding HTML page, enter some data and select the go_find button.
The Java FastCGI classes are included in both source and byte code format in
fcgi-devel-kit/java/src and :fcgi-devel-kit/java/classes
respectively. The following is a brief description of these classes:
- FCGIInterface
- This class contains the FCGIaccept method called
by the FastCGI user application. This method sets up the appropriate
FastCGI environment for communication with the web server and manages
FastCGI requests.
- FCGIInputStream
- This input stream manages FastCGI
internal buffers to ensure that the user gets all of the FastCGI
messages associated with a request. It uses FCGIMessage objects
to interpret these incoming messages.
- FCGIOutputStream
- This output stream manages FastCGI
internal buffers to send user data back to the web server
and to notify the server of various FCGI protocol conditions.
It uses FCGIMessage objects to format outgoing FastCGI messages.
- FCGIMessage
- This is the only class that understands the
actual structure of the FastCGI messages. It interprets incoming
FastCGI records and constructs outgoing ones..
- FCGIRequest
- This class currently contains data fields
used by FastCGI to manage user requests. In a multi-threaded
version of FastCGI, the role of this class will be expanded.
- FCGIGlobalDefs
- This class contains definitions of FastCGI
constants.
Steve Harris // harris@openmarket.com