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 applets can be used to provide better dynamic interaction with the user on Web pages than simple CGI scripts and html forms. However, like any programming language, the code does not spring forth perfect, but rather is designed, implemented, and then debugged.

Debugging code that lives on a Web page can be difficult. The old standby of trace printfs (or System.out.println in Java terminology) is always available, but tedious at best. The JDK, available from JavaSoft, contains a debugger, jdb, but it really serves best as a sample implementation rather than as a development tool. If your applet does not do any communications back to the Web server it comes from, then debugging the applet can be done completely off-line.

The problem comes about when your applet needs to communicate with the Web server host in order to function correctly. Perhaps the applet is doing CGI GETs to a backend-database, and then validating entry fields. Or maybe it is a simple page-hit counter storing the result back to the server. No matter how simple or complicated the applet, the security model of Java is such that an applet must come from a host before it can communicate with that host.

This security model, which exists to protect you from a malicious applet being pulled over your corporate firewall by an unwitting user and then attacking the now unprotected hosts from within, works against you when it comes to debugging. In this article, I discuss a way to debug applets which must communicate with the Web server using a product from Symantec, Cafe. Symantec Cafe is an integrated development environment (IDE) which contains a graphical debugger. This IDE is good for stand-alone applet development, but it also can be used to debug your applets from your Web server while they are 'live' and communicating with it.

In order to accomplish this, the CLASSPATH environment variable is used to cause the Symantec IDE to fetch classes from your local disk. [Note: In Cafe, CLASSPATH is not an environment variable, but is stored instead in the project file. The result is the same]. At the same time, you tell Cafe the html page you are using is on your Web server, using http as the protocol. The directory hierarchy used locally is the same as the one you will eventually use on the Web server. While you are debugging, it doesn't matter if the .class files exist on the http server or not; they won't be used.

It doesn't matter what architecture your http server is...it could be a large UNIX server...all of the debugging, user interface, input, output, etc., is done on your PC. You won't even use the Web server host for anything other than serving you html pages, and whatever CGI communications your applet needs to perform.

As far as the applet is concerned, it really came from that Web server, and its getCodeBase(), getDocumentBase(), etc., methods will indicate the applet came from the Web server using http, just as if it really were fetched from there. This means you don't need to modify your code for the purpose of debugging.

Perhaps the simplest way to explain this is with an example.

My Web server is called 'www'. My development PC is called 'xdb4'. I wish to write and debug an applet which validates IP addresses as they are entered, and communicates the result back to the http server in a CGI post. This applet has a single text field, and when the user hits enter, checks to see if it contains a valid IP address. If it does, it posts it to the CGI script as 'IP=value'. If it does not, it should inform the user with a dialog or otherwise.

On host www, my directory hierarchy is such that /usr/local/www/htdocs is my DocumentRoot [i.e. the URL http://www/file.html will fetch /usr/local/www
/htdocs/file.html]. I have a cgi script called 'cgi-bin/post-ip.cgi' and the HTML page is called 'ipEntry/enter-ip.html'. I wish to have a Java applet called ipVerify.class that will go in the same directory as the HTML page.

Thus, my Web server has this directory structure:

/usr/local/www/htdocs/cgi-bin/post-ip.cgi
/usr/local/www/htdocs/ipEntry/enter-ip.html
/usr/local/www/htdocs/ipEntry/ipVerify.class

The HTML file, 'enter-ip.html', which specifies the applet looks like this:

<HTML>
<HEAD>
<TITLE> Enter IP Address </TITLE>
</HEAD>
<BODY>
<APPLET CODE="ipVerify.class" WIDTH=100 HEIGHT=100></APPLET>
</BODY>
</HTML>

The Java source file I used is shown in Listing 1. There are four methods in it: ipVal() validates the IP address; postVal() sends the value to the CGI server; init() starts the applet, and handleEvent() checks for the return key. This source code is saved in the file ipVerify.java (Listing 1).

Now, in Cafe, on the 'Project' menu, select 'Project Settings'. Select the 'Target' tab at the top, specify 'http://www/ipEntry/enter-ip.html' for the HTML file as shown in Figure 1. In the same dialog, 'Project Settings', select the 'Directories' tab; specify a CLASSPATH of '.;/Cafe/Java/Lib/symclass.zip;/Cafe/Java/Lib/classes.zip'. It doesn't matter where you place the .java or .class file on your PC.

Figure 1
Figure 1

What this means is that Cafe will now use 'http://www/
ipEntry/enter-ip.html' for the HTML page. Whenever you click 'Start/Restart Debugging' or 'Run', the debugging appletviewer will fetch that page. [Note: I've been unable to get the standard 'Run' command in Cafe to fetch this page, but the debugger will, which is all that counts]. The CLASSPATH you specified means that when it needs to find a .class file, it will first look in the current directory ('.'), then in symclass.zip, then in classes.zip. If it finds it in none of these, it will look where the HTML file came from on the Web server. In our situation, the debugger will fetch ipVerify.class from the current directory on 'xdb4'.

To debug this now, select 'Rebuild All' from the toolbar. When Cafe has finished rebuilding the applet, click 'Start/Restart' debugging from the toolbar. When the hourglass goes away, set your breakpoints, watch your data, etc., just as if the applet were completely local. Note that when the applet calls getCodeBase(), getDocumentBase(), etc., it always reports that the applet has come from http://www/ipEntry.

So the sequence of events would be:

  • Cafe starts appletviewer
  • appletviewer fetches file 'http://www/ipEntry/enter-ip.html' from 'www' to 'xdb4'
  • appletviewer loads ipVerify.class from the current working directory on 'xdb4'
  • ipVerify.class writes IP=value pair to 'www' from 'xdb4' with CGI POST.
You may use the debugger on any aspect of the execution of ipVerify.class...it won't interfere with the client-server interactions at all. If you wish to verify that the data came through correctly on the CGI side, you can modify the 'post-ip.cgi' script to log its operations into a file on its local disk.

When you have your applet working correctly, and all debugged, transfer the .class files to the appropriate place on the Web server. In this example, transfer ipVerify.class to /usr/local/www/htdocs/ipEntry/ipVerify.class. Verify your work by running Netscape [which won't fetch the applet from the CLASSPATH, so will be forced to fetch it from the http server].

Using this technique, I have been able to debug many applets that do complex multi-part interactions with CGI posts and gets. It has saved me hours of frustrating trace statement debugging.

	

Listing 1: The Java source file

import java.awt.*;
import java.net.*;
import java.io.*;
import java.applet.*;
import java.util.*;

public class ipVerify extends Applet
{
  TextField tf;

  public void init()
  {
    tf = new TextField(20);
    add(tf);
  }
  private void
  postVal()
  {
    String ip = tf.getText();
    String postV = "IP=" + URLEncoder.encode(ip);
    URL base = getCodeBase();
    String port = (base.getPort() == -1) ?
                    "" : ":" + base.getPort();
    String urlString = new String(base.getProtocol() +
                                  "://" +
                                  base.getHost() +
                                  port +
                                  "/cgi-bin/post-ip.cgi");
    try
    {
      URL url = new URL(urlString);
      URLConnection UConn = url.openConnection();
      UConn.setDoOutput(true);
      PrintStream out = 
                 new PrintStream(UConn.getOutputStream());
      out.println(postV);
      out.close();
    }
    catch (MalformedURLException e) { }
    catch (IOException ioe) { }
  }
  private boolean
  ipVal()
  {
    String ip = tf.getText();
    int nTokens = 0;
    StringTokenizer st = new StringTokenizer(ip, ".");
    while (st.hasMoreTokens())
    {
      Integer in;
      nTokens++;
      String token = st.nextToken();
      try
      {
        in  = new Integer(token);
      }
      catch (NumberFormatException nfe)
      {
        return false;
      }
      if (in.intValue() < 0 || in.intValue() > 255)
      {
        return false;
      }
    }
    if (nTokens == 4)
    {
      return true;
    }
    return false;
  }

  public boolean
  handleEvent(Event evt)
  {
    if (evt.target == tf &&
        evt.id == Event.KEY_RELEASE &&
        evt.key == '\n')
    {
      if (ipVal())
        postVal();
      // else handle the error some how
    }
    return false;
  }
}

 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: info@sys-con.com

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.