Chapter 28

Client/Server Using Java Applets and Perl


CONTENTS


This chapter provides the basic information for getting a client/server application using Perl 5 and Java applets. After reading this chapter, you'll understand how to get data from a Perl server to a Java client.

The information in this chapter is based on the assumption that you have read Chapter 12, "Using Sockets." In this chapter you'll learn how to connect a Java applet to your socket. This chapter is not meant to be a tutorial on Java. For programming in Java, you'll have to go to the sources listed in the section titled "For More Information on How to Program in Java."

Using CGI or Sockets

The first way to hook client applets up to your Perl CGI server is via a URL page. The basic scenario is that a Perl CGI script creates an HTML document for your client, based on responses to a FORM or simply via a URL. The output of your CGI script can even exist on disk in the form of a file.

Using the disk to store the data as an intermediate place for data to reside before it's retrieved by the client may seem like a crude approach. (Compare this to using a sledgehammer to put a nail in a wall.) Indeed, using the disk for a file transfer is a very crude approach and not quite as elegant as using a socket as the medium from the server to the client. For one thing, you are writing to disk, which in itself is an inherently slow process when compared with actions that are performed in memory alone. Second, you are opening the door to potential file-locking problems. That is, while your server is writing to disk, a client might be trying to read the same file.

Even so, the problem with sockets is the overhead of copies of a server daemon to handle each client's request. If the data per request is small enough, writing it to disk won't hurt your computer's performance. Most Net users at 28.8Kbps won't notice the extra milliseconds spent on spinning the disk.

Writing logs to disk has the advantage of keeping a record of what was sent back. Nightly or even hourly backups of the data file can help ensure the possibility of reconstructing any previously sent messages. With data being piped via a socket, you have to write extra code to save data off to disk.

Figure 28.1 presents two ways of implementing client/server applications with Perl. In the first scenario, you are writing intermediate results to disk while satisfying requests for clients. The client does not necessarily have to be a Java applet in the first scenario because the Perl server could be writing HTML pages to disk for another browser to pick up. In the second case, the results from a server calculation are sent back directly with the use of a socket. In this second case, a Java applet would reduce the coding effort of communicating via sockets because of the built-in socket support in Java. You are at liberty to write your own client application to do the client-side socket processing.

Figure 28.1 : Two ways of communicating between client and server.

Ultimately, it's up to you to decide which approach to take when designing an interface. If your server and client are guaranteed to be able to use sockets, by all means code your applet in this manner. If you are in doubt and are willing to use the disk as an intermediary, use URLs.

Using the Disk

This section focuses on writing a Java applet to collect data from a file given a URL on a remote machine. The Java applet runs on a client using a browser. The server in this example is a Perl script that is used to create a data file. Listing 28.1 presents the server-side code.

For using the disk as the intermediary, as in the example presented here, the Java applet will be configured to read the file. It's your Perl script's responsibility to ensure that the file is updated regularly. For example, your Perl script might wake up every five minutes, collect the temperature from a sensor, and then write the HTML file.

The client using your applet will be querying the file that your script writes. The Perl script to write the test data to a known file is a format that is well known to other Web browsers. The Java applet that implements the client side is shown in Listing 28.2.


Listing 28.1. The Perl script to handle server-side details.
 1 #!/usr/bin/perl
 2
 3 #
 4 #  Sample "server" script to create data for Java Applet
 5 #
 6
 7 #
 8 # Flush immediately to disk.
 9 #
10 $| = 1;
11 $i = 0;
12
13 while(1) {
14     open (TEMPH,">tst.txt") || die "\n Cannot open \n";
15     $|=1;
16     $now = `date`;
17     print TEMPH "Data updated \n $now \n";
18     select TEMPH;
19     $| = 1;
20     select STDOUT;
21     print "Data updated\n $now \n";
22
23     # ------------------------------------------
24     # You may want to increase this time a bit
25     # to allow the client(or clients) to catch up.
26     # ------------------------------------------
27     sleep(5);
28     close TEMPH;
29 #
30 # If you really want to put a limit to this looping
31 # then uncomment the following lines.
32 #
33 #    $i++;
34 #    if ($i > 25) { exit(0); }
35
36 } # loop forever.


Listing 28.2. Using URLs to display the data.
  1 //
  2 // --------------------------------------------------------
  3 // Sample Java program to collect text from a remote site
  4 // --------------------------------------------------------
  5
  6 //
  7 // The following lines are required classes for this program
  8 //
  9 import java.awt.*;
 10 import java.io.*;
 11 import java.net.URL;
 12 import java.net.URLConnection;
 13 import java.net.MalformedURLException;
 14
 15 //
 16 // The Applet is declared here
 17 //
 18 public class l28 extends java.applet.Applet implements Runnable {
 19
 20     int delta;     // Interval between fetches
 21     URL theURL;      //
 22     Thread runner;
 23     TextArea ta = new TextArea("Synchronizing",20,20);
 24
 25 public void init() {
 26     String lbl = getParameter("URL");
 27     if (lbl == null) { lbl = "http://ikra.com/tst.txt"; }
 28     //
 29     // Collect after 60 seconds (as default).
 30     //
 31     delta = 60000;
 32     String dt = getParameter("DELTA");
 33     if (dt == null) { delta = 60000; }
 34         else {
 35         delta = Integer.parseInt(dt);
 36         }
 37     //
 38     // Never let the user collect data at less
 39     // than seven second intervals.
 40     //
 41     if (dt < 7000) { delta = 7000; }
 42
 43     //
 44     // Attempt to parse URL and bail out if you
 45     // do not have a valid URL
 46     //
 47     try { this.theURL = new URL(lbl); }
 48     catch ( MalformedURLException e) {
 49         System.out.println("Bad URL:" + lbl);
 50     }
 51
 52     //
 53     // This part needs work to make the output in
 54     // the Java Applet look pretty.
 55     //
 56     add(ta);
 57
 58 } // init function
 59
 60 //
 61 // Your thread's starting function
 62 //
 63 public void start() {
 64     if (runner == null) {
 65         runner = new Thread(this);
 66         runner.start();
 67     }
 68
 69 } // start
 70
 71 //
 72 // Your thread's stopping function
 73 //
 74 public void stop() {
 75     if (runner != null) {
 76         runner.stop();
 77         runner = null;
 78     }
 79 } // stop
 80
 81 //
 82 // While your thread is running this function is called.
 83 //
 84 public void run() {
 85     InputStream conn;
 86     DataInputStream din;
 87
 88     String line;
 89
 90     while (true) {
 91     //
 92     // Pick up the data from the URL passed into applet 
 93     //
 94     try {
 95         conn = theURL.openStream();
 96         din = new DataInputStream(new BufferedInputStream(conn));
 97         StringBuffer buf = new StringBuffer();
 98
 99         while ((line = din.readLine()) != null) {
100             buf.append(line + "\n");
101         }
102         ta.setText(buf.toString());
103     }
104     catch (IOException e) {
105     System.out.println("Whoops no data at URL:"); }
106
107     //
108     // The screen has to be updated. (Not really needed
109     // but it's better to leave the next line in here for
110     // platforms running this applet.)
111     //
112     repaint();
113
114     //
115     //  Wait a bit before trying html document again.
116     //
117     try {
118         Thread.sleep(delta);
119         }
120     catch (InterruptedException e) {
121         System.out.println("Whoops no data at URL:"); }
122     }
123 } //run
124
125 } // class l28

A Quick Note About Java

The Java programming language was developed at Sun Microsystems Inc., Mountain View, California, by a group headed by James Gosling. Java is a platform-independent, object-oriented language developed primarily for use in household appliances. Java's usefulness extends far beyond the initial intended use for developing it. Java appeared at the same time as the World Wide Web (WWW) and was used by Sun to develop WebRunner, a Web browser written entirely in Java. (WebRunner was later renamed HotJava to avoid copyright problems.)

The Java Programming Language is very similar to, but much simpler than, C++. Numbers and Boolean types serve as basic building blocks for developing Java classes for object-oriented programming. Programmers can create their own objects as classes. All classes in Java are restricted to single-level inheritance only, because a class in Java may only inherit from one class at a time. Abstract classes in Java allow definitions of interfaces to permit multiple inheritance.

A Java applet is the result of running a Java compiler on Java source code. Basically, in order to create a Java applet, you need to have a Java Developer's Kit (JDK) on your system. You can get a JDK for your machine from the Web site http://www.javasoft.com. Included in a JDK distribution are compilers, documentation, and source code for some sample applets that folks were kind enough to donate for use, with limited copyright restrictions of course, to the general computing community.

A Java source file can consist of several objects to implement a certain set of functionality. The source code for an applet when run through a Java compiler, javac, produces one or more .Class files, one for each type of object defined in the source file. Each .Class file is a series of byte codes that are then referred to as "applets."

Applets reside on servers and are downloaded to clients when referenced via a URL. Applets execute on remote sites under the auspices of the browser that downloaded them. The address space and code execution are modeled under a process known as the Java Virtual Machine (JVM). The JVM describes how to implement Java applet code and addresses issues regarding the ways to parse the byte codes in an applet. Java is designed to be secure and robust enough to prevent any applet's byte code's instructions to compromise the client machine's system. By using the JVM model, byte codes can be verified by javac to ensure that the resulting applet will not compromise system integrity.

Java has support for multithreaded applications built into the language. A browser running an applet can manage multiple threads for a Java applet. Java threads are mapped by the browser into the underlying operating system threads if the underlying system supports it. Java-enabled browsers are designed and built for each type of platform, such as Windows NT, 95, Macintosh, and UNIX workstations.

Java also has support for network communication via the use of TCP/IP or UDP sockets as well as with the use of URLs. In essence, Java is a distributed language because Java applets can retrieve data from any accessible node on the Internet. The permissions available for a Java applet to access a file on the Internet can be the same as those of the browser running the applet. Java applets cannot have file access permissions that are greater than that of their host browser for security reasons. Items accessed via URLs include other HTML pages containing applets, raw files or images, or links to other sites. Socket support under Java is implemented using Berkeley (BSD) Socket Extensions.

For more information about programming in Java, please consult the books in the section "For More Information on How to Program in Java," later in this chapter.

Line 18 of Listing 28.2 starts a thread for the applet and declares the l28 class. The Java program gets the parameters to itself in lines 26 and 35. A TextArea (ta) widget is declared and added to the applet for the applet to display the incoming data.

Lines 26 and 32 show the code to extract the two input parameters to the applet: URL and DELTA. The URL parameter is a string containing the URL of where the data is located. The DELTA parameter is used to determine the time interval between successive tries to extract the data. Default values are used when incoming values are absurd or absent.

The start() and stop() functions in lines 63 and 74 are called when the applet thread starts and stops. The code in line 63 is called when a background thread is created in the applet to do socket communication. The subroutine in line 74 is called when the thread stops executing.

The bulk of the work is done in the run() subroutine in line 84, where the running thread connects to the server in lines 95 to 97, extracts the data in the while loop in line 99, and then displays the received text in a text area for the applet in line 102.

If there is an exception, it's of the type IOException and is caught at line 104, where a message is displayed on the system status bar. The try-and-catch exception-handling mechanism shown in lines 94 to 105 to fetch data is very common in Java applet source code. The code in line 105 notifies the applet user of exceptions like missing data or a bad URL value. The text area actually updates the screen in line 112 with the repaint() call.

The loop is repeated starting from the section in line 117 to fetch the next updated URL.

Once you have typed the code for the applet, you have to compile it with the javac compiler. The Java applet is compiled to a l28.class with the following command:

javac l28.java

The result of the command is a file called l28.class in the same directory. The prefix of the file and the class name must be the same.

To use the applet in your HTML document, you have to use <APPLET> and </APPLET> tags. A sample usage is shown Listing 28.3.


Listing 28.3. HTML page for using an applet to get text from a server.
   1 <HTML>
   2 <BODY>
   3 <TITLE>
   4  Test
   5 </TITLE>
   6 <H1> Test the applet to recover text from server </H1>
   7 <applet code=l28.class width=300 height=400>
   8 <param name=URL value="http://ikra.com/tst.txt">
   9 <param name=DELTA value="10000">
  10 </applet>
  11 </BODY>
  12 </HTML>

The code in line 7 shows how to include the applet code. The width and height of the applet are required parameters. The applet will not load if you do not specify the height and width. Line 8 sets the parameter URL to the applet and gives it a value of a complete URL. You can specify relative URLs as well. Line 9 specifies a second parameter for the time interval between updates as a string. The interval can be specified as a string, but this way you'll see how to use code in the applet to convert from a string to an int. The applet is closed out at line 10 with the </applet> tag.

At line 117 of Listing 28.2, the program sleeps for the DELTA period specified. As just seen with I/O handling, this sleep call also catches the exception of the type InterruptedException in cases of errors. Note that the sleep time in applets is in milliseconds, whereas in Perl it's in seconds.

The run() function loops forever until the applet is destroyed by the user selecting another HTML page.

As stated earlier, this chapter is not about teaching you to write Java applets. Instead, this chapter shows you how it's possible to interface with applets using CGI. The method shown in this section is a bit crude in that the applet has to wake up and fetch a document every time. Even cruder is the fact that the Perl script at the other end has to open a file and then write to and close it every so many seconds. You might consider keeping the file open at all times in the Perl server script.

Another alternative to the scheme shown here is to use UNIX system facilities to use sockets.

Using CGI and Sockets

The second example of using Perl and Java together involves using sockets for client/server communication. The server is written in Perl, and the client is a Java applet on a client browser.

The approach to using sockets over files has the obvious advantage of speed and more efficient use of system services. Of course, your advantage is minimized a lot if you are echoing what you write to the socket back onto the disk.

Also, Perl is portable to platforms that might not support sockets. For example, Windows-based systems might not have Winsock loaded correctly, or they might have the wrong version loaded, with which they can run the browser but cannot access the socket's features. In such a case where sockets are not supported on your server side (a rare but true possibility), the first method of using URLs alone will have to do.

The server application is pretty straightforward. It binds itself to a port and listens for a connection. After getting a connection, the server in this example sends 1,000 data items back to the connecting client. Each data item sent back has an identifier that is set to the current time. The identifier is often referred to as the time stamp for this packet. (See Listing 28.4.)

The server in this example can be extended to use the fork system call and handle requests via children. The sample shown here simply bails out after it's done. For a discussion of how this server works, refer to Chapter 12. See Listing 28.4 for a sample Perl server that simply listens to a socket and, on receiving a connection, sends some dummy data back to the calling client.


Listing 28.4. Connection-oriented server using Perl.
 1 #!/usr/bin/perl
 2 # ----------------------------------------------------------
 3 # Sample connection oriented server using Perl
 4 # ----------------------------------------------------------
 5 #
 6 $AF_UNIX = 1;
 7 $AF_INET=2;     # Use AF_INET and not AF_UNIX.
 8 $SOCK_STR = 1;  # Use STREAMS.
 9 $PROTOCOL = 0;  # stick to the default protocols (IP).
10
11 $SIG{'KILL'} = cleanup;
12 $SIG{'INT'} = cleanup;
13 $port = 6783 unless $port;
14
15 #
16 # The pattern for packing into a sockaddr structure
17 #
18 $PACKIT='S n C4 x8';
19
20 #
21 # Disable any buffering on any newly created sockets.
22 #
23 select(NEWSOCKET);
24 $| = 1;
25 select(STDOUT);
26
27 #
28 # Create the socket.
29 #
30 socket(MY_SOCKET, $AF_INET, $SOCK_STR, $PROTOCOL) ||
31                       die "\n $0: Cannot open socket: $!";
32 print "Socket successfully opened\n";
33
34 #
35 # Get the host address for this node
36 #
37 ($name, $aliases, $addrtype, $len, @addrs) = gethostbyname("ikra.com");
38 ($a,$b,$c,$d) = unpack('C4',$addrs[0]);
39 print "Server Name=$name, Server Address= $a.$b.$c.$d\n";
40 $my_ip_addr = pack($PACKIT,$AF_INET,$port,$addrs[0]);
41
42 #
43 # Bind to the socket and listen on this port
44 #
45
46 bind(MY_SOCKET, $my_ip_addr) || die "$0: Cannot bind .. $!\n";
47
48 print  "\n Bound to socket";
49 listen(MY_SOCKET,5)  || die "$0: Cannot listen: $!\n";
50 print  "\n Listening \n";
51
52     $remote = accept(NEWSOCKET, MY_SOCKET) || die "$0: Unacceptable: $!\n";
53 #
54 # In case you have to display incoming connection
55 # information, you can uncomment the next three lines of code:
56
57         @remoteInfo = unpack($PACKIT,$remote);
58         $, = ' ';
59         print @remoteInfo; print " <<- Remote \n";
60
61     close MY_SOCKET;
62     select(NEWSOCKET);
63     $| = 1;
64     print NEWSOCKET "Kamran was here\n";
65     print NEWSOCKET "Another line\n";
66     $i = 0;
67     srand();
68     while(1){
69         $tick = rand() * 100;
70
71
72         printf " $i: %6.2f \n", $tick;
73         $i++;
74         if ($i > 1000)
75             {
76             close NEWSOCKET;
77             exit(0);
78             }
79         }
80     close NEWSOCKET;
81
82 exit(0);
83
84 sub cleanup {
85 print "\n Being killed ";
86 close MY_SOCKET;
87 die "$0: Cleanup : $!\n";
88 }
89

Note that in line 24 in the server script shown in Listing 28.4, all output is flushed immediately to the socket with the $|=1; command. The client Java applet to talk to the server is also shown. Two classes are defined in this source file:

After you compile the Java program in Listing 28.4, you'll get two files in your directory:

The Java program takes two parameters: the port name and the host name. No timeout value is given because the applet always reads and displays immediately what it sees on the socket. The parameters into the applet are handled in lines 26 and 42 in Listing 28.4.

The applet itself is included in an HTML file, as illustrated by Listing 28.5.


Listing 28.5. Using the socket applet.
 1
 2 <HTML>
 3 <BODY>
 4 <TITLE> Test
 5 </TITLE>
 6 <applet code=cl28.class width=300 height=100>
 7 <param name=HOST value="ikra.com">
 8 <param name=PORT value="6783">
 9 </applet>
10 </BODY>
11 </HTML>

Lines 6 through 9 define the parameters into the new applet, called cl28.class. The required width and height parameters are also provided where the applet is declared.

The code for the applet itself is shown in Listing 28.6.


Listing 28.6. Java applet using sockets.
  1 // --------------------------------------------------------
  2 //               Using Sockets
  3 // --------------------------------------------------------
  4 // Sample Java program to collect text from a remote site
  5 // --------------------------------------------------------
  6 //
  7 // The following lines are required classes for this program
  8
  9 import java.applet.*;
 10 import java.awt.*;
 11 import java.io.*;
 12 import java.net.*;
 13
 14
 15 //
 16 // The applet begins here.
 17 //
 18 public class cl28 extends Applet {
 19
 20     //
 21     // Declare the socket,port tuple here
 22     //
 23     public int PORT;
 24     Socket s;
 25     DataInputStream din;
 26     TextArea txtOut;
 27
 28     //
 29     // This class is derived from the
 30     //  "Java in a Nutshell" book
 31     //
 32     StreamComm listener;
 33
 34     public void init() {
 35
 36     String host = getParameter("HOST");
 37     //
 38     // DO NOT FORGET TO chANGE THE HOST NAME FROM
 39     // ikra.com TO THE NODE NAME YOUR SERVER IS ON!!
 40     //
 41     if (host == null) { host = "ikra.com"; }
 42     String port = getParameter("PORT");
 43
 44     //
 45     // Set the default at 6970
 46     //
 47     if (port == null) { PORT = 6970; }
 48     else {
 49     try {
 50         PORT = Integer.parseInt(port);
 51         } catch (NumberFormatException e) {
 52         PORT = 6970;
 53         }
 54
 55     }
 56
 57
 58     //
 59     // Use the socket "s" to connect to server.
 60     //
 61     try {
 62         s = new Socket(host,PORT);
 63         din = new DataInputStream(s.getInputStream());
 64
 65         txtOut = new TextArea("txtOut", 20, 25);
 66         this.add(txtOut);
 67
 68
 69         listener = new StreamComm(din, txtOut);
 70
 71         showStatus("Connect:" +
 72             s.getInetAddress().getHostName() +
 73             ":" + s.getPort());
 74
 75     } // try
 76     catch (IOException e) {
 77         showStatus(e.toString());
 78     } // catch
 79 } // init
 80
 81     //
 82     // When your thread stops
 83     //
 84     public void stop() {
 85     try {
 86         s.close();
 87         } // try
 88         catch (IOException e) {
 89         showStatus(e.toString());
 90         } // catch
 91     }
 92
 93 } // class cl28
 94
 95
 96 //
 97 // Class to put some wrapper around the Socket reading
 98 // code.
 99 class StreamComm extends Thread {
100
101     DataInputStream din;
102     TextArea outp;
103     StringBuffer buf;
104
105     public StreamComm(DataInputStream in, TextArea output) {
106         this.din = in;
107         this.outp = output;
108         this.start();
109         buf = new StringBuffer();
110     }
111
112
113     public void run() {
114
115     String line;
116
117     for(;;) {
118         try     {
119             outp.setText("Reading");
120             line = din.readLine();
121             if (line == "end") break;
122             if (line == null)
123                 {
124                 outp.setText("NULL");
125                 break;
126                 }
127             buf.append(line + "\n");
128             outp.setText(buf.toString());
129
130
131         } // try reading
132         catch (IOException e) {
133             outp.setText(e.toString());
134         }
135
136
137     } // for loop
138     } // run function
139
140 } // class StreamComm

In this Java applet, lines 105 to 109 in the StreamComm constructor copy the values of local variables in the applet. Line 120 is where the StreamComm thread reads the incoming input and sets it in a string buffer. The helper class StreamComm loops forever, until the applet is no longer referenced in its containing HTML document.

For More Information on How to Program in Java

The online information on Java is perhaps the best source for information on how to write Java applets. Here are the sites to visit:

The online Application Programming Interface (API) on the sites is usually the most current one to use. There are some texts already on the market. I used the following books as references for my work on this chapter and in Java programming:

Summary

This chapter introduced you to interfacing your Perl scripts to Java applets by using files or sockets. Each method has its own merits. Using files gives you a semi-permanent record of what was sent, and multiplexing is easy because multiple socket/child process pairs do not have to be opened up to each client. The downside is the use of the disk and slower processing overall. Using sockets provides security, speed, and more efficient use of resources. However, the con side of socket usage is the limitation on the number of connections your server can use to transfer data to clients. If you intend to transfer data to a large number of clients, consider using files. If security is an issue, use sockets.

The methods discussed here are not the only ones available to you. Using the Perl/Tk toolkit, you can draw comparable analogies to display data via sockets as well, but you'll have to write your own process and input-handling routines. (The Perl/Tk toolkit is discussed in Chapter 17, "GUI Interfaces with Perl/Tk.") If there is a browser that will do most of the work for you, why not use it instead? You should base your design decisions on using the best tools available.