Chapter 13

The Net and Debug Class Libraries


CONTENTS


A discussion of the API for the Internet's premier programming language would not be complete without a look at the class library that makes networking possible-java.net. Although java.net is the final class library in the original Java API, new class libraries that extend the functionality of the Java programming language are continually being introduced. One such class library is the sun.tools.debug library, which was added to give Java built-in debugging support. Whenever you use the Java debugger, you are accessing this library.

Introduction to the Net Class Library

The Net class library, java.net, provides a high-level interface for connections over the Internet or any other kind of TCP/IP network. Using these interfaces makes it possible for a Java application to connect to existing network services, provide its own network services, and even allow for multiuser, network-aware games over the Internet. Whereas these can be quite complicated to program in other languages, the java.net package allows the basics of socket connection to be set up in as little as 10 lines of code. That's power!

The following sections describe the classes in the java.net package.

The ContentHandler Class

ContentHandler is used to read a stream of data that is generated by a network connection and produce an object. The exact content handler called depends on the MIME type of the remote data.

ContentHandler should not be called directly. This task is much better performed by URL.getContent() or URLConnection.getContent(), which simplify the process of reading a stream of data.

The DatagramPacket Class

DatagramPacket is used to both send and receive datagram packets. A datagram packet is a segment of data containing packet data, packet length, Internet address, and port. This allows programming at the IP packet level.

DatagramPacket methods are used to determine the address, length, and port of the datagram packet, as well as retrieve the data itself. See the online documentation for details.

The DatagramSocket Class

DatagramSocket is used for creating UDP-based connections over a network. Services such as Sun's Network File System (NFS) use UDP as the underlying protocol. Java applications can also create and/or use UDP-based network connections by making use of this class. Only the advanced programmer will use this class. See the online documentation for details.

The InetAddress Class

InetAddress makes an object of Internet addresses. This makes them able to be manipulated as String objects or within other network classes.

Listing 13.1 is an example of using the InetAddress class.


Listing 13.1. An example of class InetAddress.
/* Notes:

      The main example simply accepts a hostname as an argument and
      prints out the following information.
         Hostname and IP address of system application is running on.
         IP address of hostname
         Additional IP addresses associated with hostname 
         (try www.microsoft.com)

      Syntax:
         MyMain <hostname>
*/

import java.net.*;

public class MyMain {

   public static void main (String args[]) {

      InetAddress inetAddr, inetAddrs[];
      int i;

      try {
         inetAddr = InetAddress.getLocalHost();
         System.out.println("Local System:");
         System.out.println("   " + inetAddr);
// print out hostname and IP address of local host
         inetAddr = InetAddress.getByName(args[0]);
// create an InetAddress object from a given hostname
         System.out.println(inetAddr.getHostName() + ":");
         System.out.println("   " + inetAddr);
// print out hostname and IP address in inetAddr
         System.out.println("Additional addresses:");
         inetAddrs = InetAddress.getAllByName(args[0]);
// get all addresses associated with a give hostname
         for (i=1; i < inetAddrs.length; i++) {
// loop through printing out all addresses
            System.out.println("   " + inetAddrs[i]);
         }
      }
      catch (UnknownHostException e) {
// handle unknown host exceptions
         System.err.println("ERROR: Hostname cannot be resolved");
         System.err.println("   hostname = " + args[0]);
      }
   }
}

Java Socket Handlers

Java has classes within its java.net package to govern the use of sockets. Sockets are a network-specific utility. Those of you who are familiar with UNIX network programming are probably well versed in sockets.

Sockets are network processes that connect two hosts and create a two-way "pathway" to carry data between the server and the client. Sockets communicate on a specific port (but which port is up to the programmer). The application notifies the operating system that it is listening for activity on a certain port. The application then goes into a waiting state until it is requested to wake up due to activity on the port. It is not necessary for it to constantly query the port; the operating system notifies the application when there has been activity. The application then wakes up, completes the connection, and does whatever the programmer told it to do.

Sockets are useful in Java for implementing communication streams between programs. Sockets are the primary method of communication on the Internet. Web pages, file transfers, and electronic mail all communicate with each other using sockets. Because of the Java socket interface, it is easy to create Java applications that can participate in this network connectivity.

Java uses three classes in java.net for sockets: ServerSocket, Socket, and SocketImpl. The first two are the classes most likely to be used by a programmer. The last, SocketImpl, is generally implemented by the Java-enabled operating system. In other words, most programmers will not have to delve into the inner workings of SocketImpl.

The ServerSocket Class

ServerSocket is the socket created for the server side. ServerSocket listens for a connection request from a client and accepts the connection for a port on the server side.

Two of the methods associated with ServerSocket are accept and close. accept is used to create the socket and attach it to a port on the server end. close is used to shut down the connection at the end of its useful life. It is best to manually shut down the socket as opposed to waiting for the Java garbage collector to shut it down.

Listing 13.2 shows the source code for a generic socket server.


Listing 13.2. A Java-based generic socket server or daemon.
/* Notes:

      This example implements a java based generic socket server or daemon.
      It uses a socket to communicate with a socket client   It can be used as
      the basis for almost any type of socker server based application. This
      particular example includes logging of the connections accepted and
      will accept a filename as part of the constructor which will be used
      for this logging capability.

      To better demonstrate it's use, a prototype of a POP server has been
      implemented.  The POP server does not actually retrieve mail, but
      does accept a connection on port 110 and will respond to a quit
      command.

      The main program simply creates a new JPop object and then executes
      its start method.  JPop will then loop waiting for and processing
      connections.  MyMain accepts a filename as an command line arguement.
      This is used for logging of the connections.

      Syntax:
         MyMain <logfile name>

*/

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

public class MyMain {

   public static void main (String args[]) {

      int i;
      JPop jp;                  // declare JPop object

      jp = new JPop(args[0]);   // create JPop instance
      jp.start();               // starts the JPop daemon
   }
}


class Daemon {                  // declare Daemon class

   public DataInputStream is;   // declare input stream variable
   public PrintStream os;       // declare output stream variable
   public String remoteHost;    // declare variable to hold remote hostname

   Socket s;                    // declare socket object
   ServerSocket ss;             // declare server socket object
   PrintStream lf;              // declare printstream object
   String logFile;              // declare logfile name variable
   int port;                    // declare port number variable
   String name;                 // declare application name variable

   // constructor for class Daemon
   public Daemon(int port, String name, String logFile) {
      this.port = port;         // save port number
      this.name = name;         // save application name
      this.logFile = logFile;   // save logfile name
      try {
         lf = new PrintStream(new FileOutputStream(logFile));   // open logfile
      }

      // handle file not found exceptions
      catch(FileNotFoundException e) {          
         System.err.println("ERROR: File not found");
         System.err.println("   File = " + logFile);
         System.exit(5);
      }

      catch(IOException e) {System.exit(6);}      // handle IO exceptions
writeLogEnt("daemon started");         // write daemon started log entry
   }

   void start() {               // method to start daemon
      try {
         ss = new ServerSocket(port);   // create server socket
         s = ss.accept();               // wait for incoming connection

         // get name of remote host for logging
         remoteHost = (s.getInetAddress()).getHostName();

         // write log entry
         writeLogEnt("connection accepted: port = " + s.getLocalPort() + ",
         Âhost = " + remoteHost);  
         // open a data input stream using the socket getInputStream method
         is = new DataInputStream(s.getInputStream());  
         // open an output PrintStream using the socket getOutputStream method
         os = new PrintStream(s.getOutputStream());
      }
      catch(IOException e) {            // handle exceptions
         System.err.println("ERROR: IOexception(1) encountered");
         this.exit(7);
      }
   }

   void stop() {                        // declare stop method
      writeLogEnt("daemon stopped");    // log daemon stopped message
      try {
         if (is != null)                // test input stream is not null
            is.close();                 // explicitly close input stream
         if (os != null) {              // test if output stream is not null
            os.flush();                 // explicitly flush output stream
            os.close();                 // explicitly close output stream
         }
         if (s != null)                // test if socket is null
            s.close();                  // explicitly close socket
      }
      catch(IOException e) {            // handle exceptions
         System.err.println("ERROR: IOexception(2) encountered");
         this.exit(8);
      }
   }

   String date() {                      // declare method for getting todays date
      Date d = new Date();
      return(d.getMonth()+"/"+d.getDate()+" "+d.getHours()+":
      Â"+d.getMinutes()+":"+d.getSeconds());
   }

   void writeLogEnt(String entry) {     // delcare method for writing log entries
      lf.println(date() + " " + name + ": " + entry);
   }

   void exit(int errCode) {             // declare common exit method
      // log exit message
      writeLogEnt("daemon exited with error code " + errCode);
      if (s != null) {                  // test socket is not null
         try {s.close();}                 // close socket
catch(IOException e) {
            System.err.println("ERROR: IOexception(3) encountered");
         }
      }
      System.exit(errCode);             // exit application
   }
}

class JPop {            // declare Java POP class

   Daemon d;            // declare Daemon object

   public JPop(String logFile) {        // constructor for class JPop
      d = new Daemon(110, "JPop", logFile);     // create new Daemon instance
   }

   void start() {       // declare start method
      int i;
      String resp;      // variable to hold response from client
      while (true) {    // loop forever handling incoming connections
         d.start();     // start daemon
         // send response to remote host
         write("+OK " + d.remoteHost + " JPOP server ready");
         procConnection();      // process POP connection
      }
   }

   // declare method to process POP connecitons
   public void procConnection() {

      // variable to hold response from client
      String resp;
      String cmd = "";

      // process commands until quit received
      while (! cmd.equalsIgnoreCase("quit")) {
         resp = read();                 // read response from client
         // tokenize client response for processing
         StringTokenizer st = new StringTokenizer(resp);
         cmd = st.nextToken();          // get command

         if (cmd.equalsIgnoreCase("quit")) {    // test for quit command
            // send server shutdown message to client
            write("+OK " + d.remoteHost + " JPOP server shutdown");
            d.stop();                   // stop connection to client
            continue;                   // go to top of loop
         }
         // send error message to client
         write("-ERR Invalid command; valid commands:  QUIT");
      }
   }

   void write(String cmd) {         // declare method to write string to client
      d.os.println(cmd);                // send string to client
   }

   String read() {                 // declare method to read string from client
      String resp = "";
      try {resp = d.is.readLine();}       // read response from client
catch(IOException e) {            // handle exceptions
         System.err.println("ERROR: read IOException");
         this.exit(10);
      }
      return(resp);                     // return exit code
   }
   void exit(int errCode) {             // declare exit method
      d.stop();                         // close connection to client
      System.exit(errCode);             // exit application
   }
}

When you start this program, on the command line, you must pass the name of the file that you want to log socket messages as an argument. You can do this as follows:

java MyMain socket.txt

In the example, the filename socket.txt is the name of the file that will log socket messages. While the program is running, you can test the socket connection and logging. To do this, start your browser and point to the IP address of your local system using port 110. If you are on a network-capable machine such as UNIX or Windows NT, you can use the local loopback IP address of 127.0.0.1 for testing. For example, the URL http://127.0.0.1:110 could be used to test port 110.

If you are able to start the program and test the socket connection, the text file will have entries similar to the following:

daemon started
connection accepted: port = 110,host = ppp-5.ts-4.dc.idt.net

The Socket Class

Socket is the class used to create network connections and processes. Socket is used in conjunction with SocketImpl to implement the actual socket operations. Socket and SocketImpl are separated so that the implementation can be changed depending on the kind of firewall being used. See Listing 13.3 for an example of using Socket.


Listing 13.3. A Socket usage example.
/* Notes:

      This example implements a java based version of the Unix "biff" utility.
      It uses a socket to communicate with a POP server to determine if there
      is any mail for the user.

      The main program simply calls the JBiff class methods every 60 seconds
      to determine if there is new mail present on the server or not. It
      accepts three command line arguments specifying the host to connect
      to, the username to use, and the password to use   Note that this
      is intended as an example only.  Specifying passwords on command
      lines is never recommended.  To make this fully functional, a separate
      dialogue box should be implemented to prompt for the password.
      That is beyond the scope of this chapter however.

      Syntax:
         MyMain <hostname> <username> <password>

      Historical note:
         Where does "biff" come from?  A program for checking mail was
         written many years ago for Unix systems.  The programmer
         decided to simply name it after his or her dog "biff".  Many other
         "biff" type programs have been written since then and many
         of these have carried on the historical fun of the "biff"
         name.  For this reason, the class implementing the "biff"
         functionality was called jbiff for Java biff.  On another
         note, I had thought about naming the class after one
         of our dogs, spot or buffy, but decided that a program that
         was constantly giving out false alarms was not a good idea.
*/

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

public class MyMain {

   public static void main (String args[]) {

      int i;
      JBiff jb;         // declare jbiff object

      // create jbiff instance
      jb = new JBiff(args[0], args[1], args[2]);

      // loop forever checking mail
      while(true) {               

         // check for new mail
         if (jb.chkMail()) {
            // print out have NEW mail message
            System.out.println("You have NEW mail");
         }
         else {
            // print out have mail message
            System.out.println("You have mail");
         }
         try {Thread.sleep(60000);}                       // sleep for 60 seconds
         catch(InterruptedException e){};                 // handle exceptions
}
   }
}


class  JBiff {

   Socket s;                    // declare socket object
   DataInputStream dis;         // declare data input stream object
   PrintStream dos;             // declare printstream object
   // variables for hostname, username, and password
   String hostname, user, pass;
   int mailBoxCnt;              // variable to hold last count of mail messages
   boolean mailBoxFlg = false;  // variable to hold status of New mail flag

   // constructor for class jbiff
   public JBiff(String hostname, String user, String pass) {
      mailBoxCnt = 0;           // initialize mail message count to zero
      this.hostname = hostname; // save hostname
      this.user = user;         // save username
      this.pass = pass;         // save password
   }

   // declare method for open socket connection to pop server
   void openMailBox() {
      String resp;              // variable to hold response from server
      try {
         // open new socket connection to hostname on port 110
         s = new Socket(hostname, 110);
         // open a data input stream using the socket getInputStream method
         dis = new DataInputStream(s.getInputStream());
         // open an output PrintStream using the socket getOutputStream method
         dos = new PrintStream(s.getOutputStream());
      }

      // handle file not found errors
      catch(UnknownHostException e) {
         System.err.println("ERROR: Host not found");
         System.err.println("   Host = " + hostname);
         System.exit(2);
      }
      catch(IOException e){};            // catch general IO exceptions
resp = read();                    // read first line from pop server
      resp = write("USER " + user);     // send username command to pop server
      resp = write("PASS " + pass);     // send password command to pop server
   }

   // declare method to check for mail on pop server
   public boolean chkMail() {     
      // variable to hold response from server
      String resp;
      // variable to hold current mail message count on server
      int cnt;                  
     // return code for method
      boolean rcode;
      // call method to open new connection to pop server
      openMailBox();
      // use STAT command to get count of mail messages on server
      resp = write("STAT");
   // tokenize server response for processing
      StringTokenizer st = new StringTokenizer(resp);
      // skip leading OK/ERR token
      st.nextToken();
      // get count of messages on server
      cnt = (new Integer(st.nextToken())).intValue();
      // send QUIT to close connection to pop server
      write("QUIT");

      // if more mail messages since last time, set new mail flag
      if (cnt > mailBoxCnt) {        
         mailBoxFlg = true;
      }

      // if fewer mail messages since last time, clear new mail flag
      if (cnt < mailBoxCnt) {
         mailBoxFlg = false;
      }
      mailBoxCnt = cnt;          // save number of messages for next comparison
      return(mailBoxFlg);               // return status of new mail flag
   }
   String write(String cmd) {   // declare method to write string to pop server
      dos.println(cmd);                 // send string command to pop server
      return(read());         // return string returned from read of pop server
   }

   String read() {             // declare method to read string from pop server
      String resp = "";                 // initialize response variable
      try {
         resp = dis.readLine();         // read response from pop server
         if (! resp.startsWith("+OK")) {        // test for acceptable response
            // report error returned by pop server
            System.err.println("ERROR: err returned by pop server");    
            System.err.println("   resp = " + resp);
            System.exit(7);
         }
      }
      catch(IOException e) {            // handle exceptions
         System.err.println("ERROR: read socket error");
         System.exit(6);
      }
      return(resp);                     // return string read from pop server
   }
}

Note
If you cannot access your host using the IP address, try the fully qualified domain and hostname, such as www.tvp.com.

The SocketImpl Class

SocketImpl is an abstract class, which means the programmer must subclass it to make it work. SocketImpl is used in conjunction with Socket to successfully adapt to different environments. One of the main reasons this is an abstract class is that the actual socket communications via Java are platform or firewall specific.

SocketImpl can be used with either streams or datagrams. It contains methods that can set a boolean as to whether the socket data is in a stream or a datagram.

The class defines other methods that accept, connect, close, and bind sockets to a port. These methods manipulate and control the socket. Of course, because they are part of an abstract class, the programmer must fill in the necessary details like IP address, port, and hostname.

Java URL-Related Classes

Java has classes in the java.net package that allow manipulation and connection to other locations on the Internet. These locations are denoted by a unique Uniform Reference Locator (URL).

The URL is set up to perform certain activities when a connection is made to it, such as loading a Web page or downloading a file. You can include these interfaces in a Java applet or application.

These URL classes are similar to sockets in that they allow easy integration of network applications into a program. URLs are a bit more user-friendly to work with than are sockets.

Note
Many of the examples in Chapter 10, "The java.lang and java.applet Class Libraries," use URLs to access files. Check out Listing 10.3, which shows a program that reads an audio file based on a URL.

The URL Class

The URL class transforms a URL string into an object that can then be manipulated using associated methods. These methods serve purposes such as getting the filename, protocol, and hostname.

URL also has methods that can open and maintain the data connection of an input stream. This is much easier to use than the socket classes.

The URLConnection Class

URLConnection is another abstract class used to create and control a connection to a platform- and firewall-specific location. It is a simplified version of connection interface for the URL class. See the online documentation for a full list of its methods.

The URLEncoder Class

URLEncoder takes a string of text and turns it into the into x-www-form-urlencoded format, which is a MIME format. This can then be used in conjunction with the URL class.

The URLStreamHandler Class

URLStreamHandler is an abstract class. Its purpose is to create a format for opening a stream connection to a specific URL. This class is concerned with the protocol of the URL. If a programmer wants to create a new URL stream protocol, it is necessary to implement the methods specified in this abstract class. This is most definitely only for advanced programmers.

Introducing the sun.tools.debug Library

The sun.tools.debug library provides the necessary framework for debugging Java programs. Whenever you use the Java debugger, you are accessing this library. If you accessed its classes directly, you could create your own debugger. Because most Java developers will not need to create their own debugger, this section provides only an overview of the classes the library contains to give you insight into the debugging process.

The DebuggerCallback Interface

The DebuggerCallback interface is a template for implementing communications between an application and a debugger. The basic syntax for the interface is as follows:

public interface DebuggerCallback {
  …
}

All communications between client applications and a debugger are asynchronous. Asynchronous communications are rather like the way you and I use e-mail: We send a message and the receiver responds to the message when he gets a chance. The sections that follow look at the abstract methods in the DebuggerCallback interface.

One of the key methods in this interface is breakpointEvent(), which is used to report when a breakpoint has been reached. The method accepts a RemoteThread object-the thread that reached the breakpoint-as a parameter. You use breakpoints during advanced debugging to help determine the behavior and state of the program at a certain point in the program's execution.

See Chapter 23, "Advanced Debugging and Troubleshooting" for more information on breakpoints.

Other key methods in this class include exceptionEvent(), printToConsole(), quitEvent(), and threadDeathEvent(). The exceptionEvent() method is used to report that an exception has occurred. It accepts a RemoteThread object and a String object as parameters.

The printToConsole() method is used whenever the DebuggerCallback interface prints messages to the console-which, as you will see when you use the Java debugger, is quite often. The printToConsole() method accepts a String object as a parameter.

The quitEvent() method is used to tell the debugger that the client has exited. Generally, clients exit either by returning from the main thread or by calling System.exit().

The threadDeathEvent() is used to report that a thread has died. This method accepts a RemoteThread object, which is the thread that died, as a parameter.

Methods that Deal with Remote Objects

As you might expect, most of the classes in a debugging library for an object-oriented programming language are designed to handle the debugging methods associated with objects. A complete listing of the types of remote objects handled by the debugger is as follows: arrays, booleans, bytes, characters, classes, doubles, fields, floats, integers, longs, shorts, strings, threads, thread groups, and variable values.

Each of these object types is handled in a separate class. These classes include

RemoteArray
RemoteBoolean
RemoteByte
RemoteChar
RemoteClass
RemoteDouble
RemoteField
RemoteFloat
RemoteInt
RemoteLong
RemoteShort
RemoteString
RemoteThread
RemoteThreadGroup
RemoteValue

Most of the methods used with specific objects types are similar to the methods used in the RemoteObject class. For example, the RemoteArray class is one of many classes in the sun.tools.debug package that allow remote debugging of specific object types. As you can probably tell by the name of the class, the RemoteArray class allows remote debugging of arrays.

What you probably cannot tell from the class name is that the RemoteArray class uses an extensive list of methods that handle every facet of debugging array objects. Still, the methods in the RemoteArray class are similar to the methods in the more generic RemoteObject class. Therefore, by examining the methods in the RemoteObject class, you can gain an understanding of most of the other classes in the sun.tools.debug package.

Table 13.1 is a summary of the methods in the RemoteObject class.

Table 13.1. Methods in the RemoteObject class.
MethodPurpose
description() Returns a description of the object
finalize() Performs this code when garbage collection is run
getClass() Returns the object's class
getField() Returns an instance variable
getFields() Returns the non-static fields of an object
getFieldValue() Returns the value of an object's instance variable
getId() Returns the ID of an object
setField() Sets an instance variable, specified by slot or name
toString() Returns the object as a string
typeName() Returns the object type

The RemoteStackFrame Class

Using the RemoteStackFrame class, the debugger can examine a suspended thread's stackframe. As a Java developer, you will often use the debugger to call the methods of this class.

The stackframe provides a frame of reference for everything related to a thread's stack. You can think of it as a snapshot of a thread's internals, including the thread's program counter, local variables, referenced methods, referenced classes, and the current line number of execution. The methods used to retrieve this information follow a very basic syntax. For example, when you call the getLineNumber() method, the method returns the current line number of execution. This and other methods in this class are shown in Table 13.2.

Table 13.2. Methods in the RemoteStackFrame class.
MethodPurpose
getLineNumber() Returns the current line number of execution
getLocalVariable() Returns a named stack variable in the current stackframe
getLocalVariables() Returns an array of all valid local variables and method arguments for the stackframe
getMethodName() Returns the method name that the stackframe references
getpc() Returns the program counter that the stackframe references
getRemoteClass() Returns the class that the stackframe references

The RemoteStackVariable Class

The RemoteStackVariable class is similar to the RemoteStackFrame class. Using this class, the caller can obtain information about a specific stack variable.

The class has three methods: getName(), getValue(), and inScope(). The getName() method is used to get the name of a variable on the stack. The getValue() method is used to get the value of a variable on the stack. The inScope method is used to tell whether the variable is accessible.

The RemoteDebugger Class

Because the RemoteDebugger class can instantiate a connection with the Java interpreter session being debugged, client applications use this class as an interface to the Java debugging classes. To create a client interface, you use a constructor. The RemoteDebugger class provides an overloaded constructor interface that allows for the two different ways the debugger can be instantiated.

The first constructor is used to attach the debugger to a current interpreter session. As you will learn in Chapter 23, you do this by first starting the Java interpreter with the -debug option. The interpreter passes back a password, which is then passed to the debugger when it is invoked. In addition to accepting the password as a parameter, the constructor accepts a String object that identifies

Here is the syntax for the first constructor:

public RemoteDebugger(String host,
                      String password,
                      DebuggerCallback client,
                      boolean verbose) throws Exception

The second constructor is used to create a remote debugger that will start a client interpreter session. When you start the debugger directly, it in turn starts the Java interpreter with any parameters you passed on the command line. For this reason, the second constructor accepts arguments that are to be passed on to the interpreter. Other parameters allow the client using the DebuggerCallback interface to receive messages from the debugger, and others set a verbose flag to turn on or off internal debugger message text.

The syntax for the second constructor follows:

public RemoteDebugger(String javaArgs,
                      DebuggerCallback client,
                      boolean verbose) throws Exception

Methods in the RemoteDebugger class enable debugging techniques you will use when you debug your programs. These methods are shown in Table 13.3.

Table 13.3. Methods in the RemoteDebugger class.
MethodPurpose
addSystemThread() Adds a system thread
close() Closes the connection to the remote debugger
findClass() Finds a specified class
freeMemory() Gets a report of the free memory available to the Java interpreter being debugged
gc() Frees all objects referenced by the debugger
get() Gets an object from the remote object cache
getExceptionCatchList() Gets the list of the exceptions on which the debugger will stop
getSourcePath() Gets the source file path the client is currently using
itrace() Turns instruction tracing on or off
listBreakpoints() Gets a list of the current breakpoints
listClasses() Gets a list of the currently known classes
listThreadGroups() Gets a list of the currently known thread groups
run() Loads and runs a runnable Java class
setSourcePath() Sets the search path for source files
totalMemory() Obtains a report of the total memory used by the Java interpreter being debugged
trace(boolean) Turns method call tracing on or off

The StackFrame Class

The StackFrame class provides a wrapper for the stackframe of a suspended thread. This class has a very basic constructor and a single method, called toString(), that returns an object as a string.

Summary

The java.net package contains classes that provide an easy-to-use interface to basic network programming. By using these classes, you can enable your Java application or applet to participate in the world of network connectivity. Some classes provide an abstract framework for protocol-specific programming. These abstract classes may be implemented in the Java-enabled operating-system interface.

The sun.tools.debug class library adds built-in debugging support to the Java programming language. Examining the classes in this library should have given you insight into the debugging process.

The mail server and mail client examples in this chapter are good demonstrations of how easy it can be to write complex network programs in Java, enabling even novice programmers to develop working network applications. This is yet another example of the power that has been built into Java, making it the programming language to use today!