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
 

Java enables us to embed miniature applications called Applets within Web pages which can process data, perform graphical animations and access databases, among other things, in a dynamic fashion. In addition, these Applets work fine on several different types of computers accessing these Web pages over the Internet. The dilemma with using Java in real world Internet Web solutions for small businesses is not necessarily a limitation of the Java language, but limitations placed upon it by Internet service providers.

Most small businesses do not maintain their own Web servers; instead, they employ the services of a Web hosting company. In fact, about 80 percent of my clients use Web hosting companies to store their Web pages. When using these other companies, you typically have to take what they give you. Many of these hosting services use some sort of Unix-based Web server, while others use Windows NT server with Internet information server.

It wasn't long before I realized that I would want to use Java for more than just the animation and basic ticker-tape applets that made it famous in the first place. In previous articles (JDJ, Vol. 2, Issue 5), I have demonstrated how easy it is to write a Web server in Java. Using such a Web server, I could receive data from a client-side Java Applet and then process that data in any way I chose. I may want to log this data to a file to be stored on the Web server and/or return data to the Applet. However, this is where I hit a brick wall.

Just because I can write a wonderful server application in Java doesn't necessarily mean that the Web hosting company will allow me to run it on their Web server. Since many of these companies are responsible for hosting several hundred Web sites, it is not hard to see why they may become hesitant to run my server application. Rest assured, if lightning comes through the window and strikes their Web server, my Java application would be blamed.

The one thing I have found that most Web hosting companies seem to have in common is that they have Perl installed on their Web servers. Hence, it may be more practical to use Java Applets on the client side and Perl scripts on the server side. For some reason, Web hosting services tend to trust Perl programs and as a result, I decided to learn how to code in Perl out of necessity.

In going to the bookstore, the first book I picked up was "Perl Programming for Dummies." Although it seems like a fine book, I saw a chapter entitled "Ten reasons why Perl is better than Java." I quickly put that book back on the shelf. After all, everyone knows that Java is the world's finest programming language. I have written programs in Basic, Fortran, Cobol, PL/1, RPG, Assembler, C, C++, Clarion and Java. Programming in Perl feels like learning to ride a bike again. You will fall down and get bloody knees over and over again, but you have to keep trying. As you will see here, Perl is an incredibly cryptic and hard to learn language at first, second and third glances. However, it is a powerful and useful tool to add to your programming tool chest. My advice is to buy a few Perl books and start small and simple. In this article, you will see that you only need to know the basics of Perl to write effective back ends to your Java applets.

Why do we need a back end in the first place? Java Applets can easily pull data from a Web server; however, they can not easily push data to a server for storage. This is due to the very nature of the mechanism provided by http Web servers. Typically, a Web browser requests a Web page and the server sends it. CGI extends Web servers so that data can be sent to a server and processed by programs which the server specifies. One of these programs is Perl.

The Perl Interpreter, along with a Perl script, can process the data sent to the Web server by the browsing client and send back a dynamically created Web page based on the processed data. For example, I can write a Perl script called addTwoNumbers.pl in an ordinary text editor. The Perl interpreter will interpret the script so it does not need to be compiled by a compiler. I can place this script in an executable directory on a Web server which has been set up to use Perl. I can write an HTML form which allows a browsing client to enter two numbers in a form. When the submit button on the form is pressed, the browser sends the two numbers which the person has typed to the server. This, in turn, invokes the Perl Interpreter to process this data along with the Perl Script. Perl can then create an HTML document on the fly and send it back to the browser with the answer. I could also use this method to do other server side things, for instance store data to a log file.

Using Perl in this way is sort of a clunky way to send data back and forth to the server; however, it is perhaps the most popular. Each time data is sent, a new web page must be reloaded. Any one who has done shopping online this past holiday season can attest to the amateur way in which transactions are handled.

In this article, I will write a Chat Applet in Java. This applet will exemplify the benefits of a Perl back end and it will interface with a Perl Script on the server. A broad overview of this project is as follows:

The applet will read a log file stored on the server repeatedly at a given interval of time. This log file will store the last twenty chat submissions. I will use a thread to read the log file every 8-10 seconds. Any new data in the log file will be appended to the text in a TextArea. I will need a way of parsing the log file and extracting new data only. I will send Chat data to the server's Perl script in a separate thread. This will emulate the process examined in the above HTML/Perl example. Each person will be required to enter a name to chat with. When a person enters or leaves the Web pages with the chat applet on it, a message will be submitted to the server that informs others that he/she has either entered or exited the chat room.

Now it becomes apparent why the Perl script is needed. While it would be a trivial task to read from the log file in a Java Applet, sending data (to be appended) to the log file, using http, becomes a challenging task. Each line of the log file will represent one line submitted to chat. I will configure the Perl script to log the 20 most recent chat submissions in a file, which will be read later by my applet. In doing this, I can be assured that the log file will contain 20 or less lines of text, thus keeping it very small in size. Each chat line received will be appended to the beginning of the log file so that the most recent submissions will appear at the top of the file. This is like making pancakes - the hottest (most recent) one is on top of the stack.

Each time the Java Applet reads the log file, it will read the most recent chat line first, since the Perl script is creating the file with each new submission appended to the top. The Applet will compare each line to the first line read in the previous reading. If these lines are different, then the chat log has been updated since the applet last read it. The Applet will continue to read the file until it either finds a match or reaches the end of the log file. I will store each line read in a temporary String, appending each line read to the beginning of the String. This will re-order the chat data with oldest chat first to most recent chat last. This is like taking the pancakes off the plate one at a time and placing them on another plate - now the hottest one is on the bottom. Then I will append the temporary String to the end of the TextArea, where the users are reading the chat data.

To submit a chat, I need to emulate the interaction that happens when a Web browser uses CGI to send data to a Web server. A Web browser can use GET or POST to send data to a Web server. If you made a form in HTML and used the GET method, when a person pressed the Submit button the browser would request the following URL (or something similar):

http://www.myserver.com/
cgi-bin/joeTest.cgi?name=
joe&age=32&eyecolor=blue

The URL up to the question mark would name the Perl Script, in this case joeTest.cgi. After the question mark would be the data, which has been URL-encoded to replace spaces and other illegal characters, to be sent to the Perl script and to be used like command line variables. This data is called a query string. The Perl program decodes the data and makes chunks that look like: Variable=Value. Then, as you've probably guessed, it parses these chunks into variables and assigns the appropriate values to them. Now these variables can be used for something useful in the Perl program.

The POST method works almost the same way as the GET. The main difference is that the data is read from standard-in instead of the query string. This data is still URL-encoded, so I will still need to decode it in the Perl script. It is almost always a good idea to use the POST method over the GET, since the GET method has limitations.

With this preliminary planning complete, let's jump right in and start this project! First, we'll examine the Perl program:

The first line describes the path to the Perl Interpreter. This line is not needed if your Web server is Windows NT with IIS. In Perl, a comment is denoted by the # in the same way that // are in Java. Next, you will find the line that says:

require 'ctime.pl';

This is similar to a Java import. It states that this Perl script requires the ctime.pl file, which is included with Perl. I will use this file for date and time utilities, since I would like to record the time that each chat line has taken place. The "define variables" portion of this script defines a variable called $maxChat and assigns it the value of 20. This is where I can state the number of lines I will maintain in the chat log file. Notice how variables in Perl start with a $. These are called Scalar variables. Using scalar variables, I do not need to define a type for the variable, as I do with Java. Perl figures it out based on the context of how the variable is used.

The next three lines create a variable called $date and assign a String value to it, which is returned by the function &ctime(time). This function will create a string with the date and time. Chop removes the terminating or new-line character from the end of $date. I will then extract a substring from $date and store it back in $date, since I am not interested in all of the date-time data.

$date = &ctime(time);
chop($date);
$date = substr($date,4,12);

The entire "Get Input" area does quite a lot. First, it will read the data, which will be sent by my Java Applet, via standard-in and place its contents into a variable named $buffer. The line,

@pairs = split(/&/, $buffer);

splits the buffer into chunks, delimited by the & symbol. Each chunk will be added to an array called @pairs. Notice that I did not need to specify an array length in Perl and that arrays are denoted by the @ symbol. The foreach loop iterates through the array and states that each value will be referenced by $pair. In the loop, I am taking each $pair, splitting it delimited by the = sign and placing the results in temporary variables called $name and $value. The next two lines decode the URL-encoded lines. Next, I stuff the $value into an associative array called $contents and index it by $name. This works much like Java's Hashtable object.

In the next section, I specify the full path and file name to the log file. On my computer, this is the specific path to the log file, which must be placed where the applet can read it. I get the log file name from $contents{'chatfile'} and append a .log extension. I am doing this so that a hacker cannot make a chatfile.bat file with commands in it to format my hard drive. Such a chat file name would result in chatfile.bat.log. Call me paranoid, but I don't like to take chances. Since I use Windows NT, I am using an MS-DOS style path. In UNIX, I would use a Unix style path. Regardless, this path must place the log file in a directory that will be accessible by the Applet. Thus, I provide a path to the exact location where the html file which runs the Applet resides. I will do something similar and specify a file with a .lock extension as well. The "lock file" will be used later so that only one person can write to the chat file at a time.

The next line, &get_file_lock; will call a function which I have defined at the end of the script. Let's jump down there now to look at it.

sub get_file_lock {
$endtime = time + 60;
while(-e $lock_file && time <$endtime){}
open(LOCK_FILE, ">$lock_file");
}

The lock file is simply a flag which indicates that someone has opened the log file. When the user is finished with the log file, he/she will delete the lock file. The idea behind this lock file is:

  • I check if a lock file already exists. If it exists, I know that someone else is in the process of writing to the log file. I will continue to check for its presence for 60 seconds.
  • If the lock file disappears within the 60 second period, I create a new lock file so that others will know that I am now using the log file. When I am done with the log file, I will later delete the lock file using the &release_file_lock function so that others can use the log file.
  • If the lock file does not disappear, I will assume that something went wrong. If the Web server was rebooted while the lock file exists, for instance, it will reboot, in error, with a lock file existing. Sixty seconds is more than enough time to wait. After waiting this time period, I will take control of the lock file for myself.
In detail, the function (subroutine) creates a variable called $endtime and sets its value to whatever the current time is, plus 60 seconds. The while loop will check if the $lock_file exists with the -e. If it does exist, it will continue to check for its disappearance until $endtime, while performing a do-nothing loop. Then it will open the lock file for writing by using the > sign.

While we are down here, let's look at the &release_file_lock subroutine, which I will use later to delete the lock file from the system. After closing the file, the unlink command will delete it from the hard-drive (see Listing 1).

The "read the log file" section uses the open command to open $filename for reading and assigns LOG as the file handle. It reads the entire file into an array called @logcache, making each line an element of the array. Next, I will chop @logcache to remove each new-line character from each element of the array. I will then close the log file, since I am actually storing its contents in my array. The unshift command will take an array and add an element to it as its first element. This pushes all the other array elements down and makes the array grow by one, like a stack. Perl uses the period (.) to append strings, like Java uses the + sign.

The element I am adding to the array is a string, which consists of the $date, a space and the chat line. The next line will determine how many elements are contained by the array. If I have exceeded the desired array length, I will pop the array. This will take the last element off of the array and lessen its length by one.

Now, all I have to do is write this array to the log file and I'm home free, at least on the Perl side.

open(LOG,">$filename");
foreach $sendchat (@logcache){
print LOG "$sendchat \n";
}
close(LOG);

I will open the log file for output and this will overwrite the old logfile. Then, for each element of @logcache, I will write its contents to the log file and append a new-line character to each line. When I am finished, I will close the log file. Now all I have to do is release the lock file and send a reply back to the applet, which will finish the Perl program.

print "Content-type: text/html\n\n";
print "Chat Received and Processed by Server";

The last part of this month's article will be to examine the Java Applet code, which will submit the chat data to the CGI/PERL script. I have created a class called SubmitToChatServer for this purpose. Since this class implements Runnable, you can assume that I will use a separate Thread to submit the data. This object has the standard Start() method to create and spawn the Thread. Objects of this class will be constructed with the following parameters:

  • String text - This represents the actual chat text to be sent to the chat server.
  • Applet app - This is a handle to the applet which is needed to construct the URL in order to send the chatline, as well as to show status in the applet.
  • String chatFileName - This specifies the name of the file on the Web Server which will store the chat log.
Note that the Perl Script will append a .log extension to this name. Therefore, when I construct this object I will leave the extension off of the file name. I will extract this file name from a parameter listed in the HTML file, which loads the Applet. The reason I might want to do this is so that I can use a single Chat applet to open several chat rooms. The HTML page, which has the name of the chat log file embedded in it as a parameter, can be created on the fly by another Perl script. This other Perl script may specify the chat log file name based off of user input.

The constructor will create a String, named completeMessage, by encoding the data that will be sent to the Server by using a utility method in the URLEncoder class, called encode(). I must separate each variable with an & symbol and use the = sign to assign the value. The URLEncoder.encode() method will replace illegal characters, such as spaces, with characters that can be transmitted to the server without problems.

completeMessage = "chatfile="+URLEncoder.encode(chatFileName)+"&chatline="+
URLEncoder.encode(message);

As with most Runnable objects, the heart of this object is in its run() method (see Listing 2).

Here, within a try block, I will create a new URL object which will point to my Perl script on the Web server, named chat.cgi. Next, I will need to get a URLConnection object for that URL. I will do this by calling the openConnection() method for chatServer, which is the URL I just created. One method I will need to call is setDoOutput(true). This will give me the ability to send my chat data to the Web server. I will not want to cache data, so I will call the setUseCaches(false) method on my URLConnection object.

There are just a few more things I need to specify before I can send the data. I will need to set some Request Properties for my URLConnection object. The Post method requires that I specify what type of data I am sending and its length, in bytes. Since I am Posting the data, I will need to specify the Content-type and Content-length using the setRequestProperty() method. The parameter sent as: "+completeMessage.length()" uses a kind of hack to convert the returned int from completeMessage.length() into a String.

Once the URLConnection is set up like this, I can create a DataOutputStream object with an output stream from my URLConnection object. Using this DataOutputStream, I can then send the data to the Web Server with the writeBytes() method. When I am finished, I simply need to close the stream.

Although, I do not really need to get any data back from the server for my program, I chose to anyway. I have spent many hours trying to figure out why certain Applets like this worked with Internet Explorer and did not work with Netscape. I discovered that Netscape needs to get data back from the Server, while Microsoft Internet Explorer does not. Hence, since getting data back will not hurt anything on MSIE, I did it anyway. I will get back an acknowledgement from the Perl script that I will print out to the console using System.out.println(). To do this, I will open a DataInputStream to the URLConnection object and read from it in a while loop, in the standard way.

Using the information covered in this month's article, you can set up any applet to interface with CGI/Perl scripts on a Web server. This particular project will use this technology to create a Chat Room-style Applet. If you choose, this method can also be used to access data from databases using Perl and ODBC, without the need for JDBC. Next month, however, I will complete the Chat Applet and discuss how to optimize it on your Web Server.

Figure 1
Figure 1:

About the Author
Joseph M. DiBella is the Senior Java Instructor and Curriculum Developer for Computer Educational Services in New York City. He also is the President of HMJ Electronics, a computer consulting company which develops software and Java-enhanced Web sites. Joe can be reached at [email protected]

	

Listing 1: Unlink command.
  
# read the log file data  
 open(LOG,"$filename");  
 @logcache = <LOG>;  
 chop @logcache;  
 close(IN);  
 unshift(@logcache,$date.' '.$contents{'chatline'});  
# Calculate the number of lines our chat log contains  
 $numberOfChatLines = ($#logcache + 1 );  
# check to see if we have exceeded our maximum log size  
 if ($numberOfChatLines > $maxChat){  
    # Remove the oldest chat line  
    pop(@logcache);  
 }  

Listing 2: Run ( ) method.
  
   chatServer = new URL(app.getDocumentBase(),cgiPath+"chat.cgi");  
   chatServerConnection = chatServer.openConnection();  
   chatServerConnection.setDoOutput(true);  
   chatServerConnection.setUseCaches(false);  
   chatServerConnection.setRequestProperty   
      ("Content-type","application/octet-stream");  
   chatServerConnection.setRequestProperty   
      ("Content-length",""+completeMessage.length());  

   DataOutputStream send = new DataOutputStream(chatServerConnection.getOutputStream());  
   send.writeBytes(completeMessage);  
   send.close();  

Listing 3: Chat.CGI may be found on our Web site at www.sys-con.com/java
 

 
      

Download Source Code Assoicated Files (Zip file format - 5 KB)
 

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.