Examples

One of the first large-scale Java programs was HotJava, a web browser that was easily the equal of the early versions of Mosaic. Today’s HotJava is easily comparable to Netscape Navigator, Opera, or Internet Explorer. It is completely possible to write commercial-quality applications in Java, and it is especially possible to write network-aware applications, both clients and servers. This section shows two network clients, finger and whois, to prove this point. I stop short of what could be done, but only in the user interface. All the necessary networking code is present. Indeed, once again we find out that network code is easy; it’s user interfaces that are hard.

Finger

Finger is a straightforward protocol described in RFC 1288. The client makes a TCP connection to the server on port 79 and sends a one-line query; the server responds to the query and closes the connection. The format of the query is precisely defined, the format of the response somewhat less so. All data transferred should probably be pure printable ASCII text, though unfortunately the specification contradicts itself repeatedly on this point. The specification also recommends that clients filter out any non-ASCII data they do receive, at least by default.[18] All lines must end with a carriage return/linefeed pair ( in Java parlance).

The simplest allowable request from the client is a bare carriage return/linefeed pair, which is usually interpreted as a request to show a list of the currently logged-in users.[19] For example:

% telnet rama.poly.edu 79
Trying 128.238.10.212...
Connected to rama.poly.edu.
Escape character is '^]'.

Login       Name            TTY    Idle    When    Where
jacola   Jane Colaginae    *pts/7       Tue 08:01  208.34.37.104
marcus   Marcus Tullius     pts/15  13d Tue 17:33  farm-dialup11.poly.e
matewan  Sepin Matewan     *pts/17  17: Thu 15:32  128.238.10.177
hengpi   Heng Pin          *pts/10      Tue 10:36  128.238.18.119
nadats   Nabeel Datsun      pts/12   56 Mon 10:38  128.238.213.227
matewan  Sepin Matewan     *pts/8     4 Sun 18:39  128.238.10.177
Connection closed by foreign host.

It is also possible to request information about a specific user or username by including that user or username on the query line:

% telnet rama.poly.edu 79
Trying 128.238.10.212...
Connected to rama.poly.edu.
Escape character is '^]'.
marcus
Login       Name            TTY    Idle    When    Where
marcus   Marcus Tullius     pts/15  13d Tue 17:33  farm-dialup11.poly.e

The information that finger servers return typically includes the user’s full name, where he’s connected from, how long he has been connected, and any other information he has chosen to make available in his .plan file.[20] A few servers put finger to other uses; for example, several sites give you a list of recent earthquake activity. It is possible to request information about users via their first name, last name, or login name. You can also request information about more than one user at a time like this:

% telnet rama.poly.edu 79
Trying 128.238.10.212...
Connected to rama.poly.edu.
Escape character is '^]'.
marcus nadats matewan 
Login       Name           TTY    Idle    When    Where
marcus   Marcus Tullius    pts/15  13d Tue 17:33  farm-dialup11.poly.e
nadats   Nabeel Datsun     pts/12   59 Mon 10:38  128.238.213.227
matewan  Sepin Matewan    *pts/17  17: Thu 15:32  128.238.10.177
matewan  Sepin Matewan    *pts/8     8 Sun 18:39  128.238.10.177
Connection closed by foreign host.

In this section, we’ll develop a Java finger client that allows users to specify a hostname on the command-line, followed by zero or more usernames. For example, a typical command-line will look like:

% java FingerClient 
               hostname user1 user2 ...

FingerClient connects to port 79 on the specified host. The socket’s OutputStream is chained to an OutputStreamWriter using the ISO 8859-1 encoding, which is used to send a line consisting of all the names on the command-line, followed by a carriage return and a linefeed. Next, the output from the server (which is input to the program) is taken from theSocket.getInputStream( ) and chained first to a BufferedInputStream for performance and then to an InputStreamReader so that the server response can be read as text. The server’s output is presented to the user on System.out. Example 10.8 shows the code.

Example 10-8. A Java Command-line Finger Client

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

public class FingerClient {

  public final static int DEFAULT_PORT = 79;

  public static void main(String[] args) {

    String hostname = "localhost";

    try {
      hostname = args[0];
    }
    catch (ArrayIndexOutOfBoundsException e) {
      hostname = "localhost";
    }

    Socket connection = null;
    try {
      connection = new Socket(hostname, DEFAULT_PORT);
      Writer out = new OutputStreamWriter(
       connection.getOutputStream(  ), "8859_1");
      for (int i = 1; i < args.length; i++) out.write(args[i] + " ");
      out.write("
");
      out.flush(  );
      InputStream raw = connection.getInputStream(  );
      BufferedInputStream buffer = new BufferedInputStream(raw);
      InputStreamReader in = new InputStreamReader(buffer, "8859_1");
      int c; 
      while ((c = in.read(  )) != -1) {
       // filter non-printable and non-ASCII as recommended by RFC 1288
        if ((c >= 32 && c < 127) || c == '	' || c == '
' || c == '
') 
        { 
          System.out.write(c);
        }
      }
    }
    catch (IOException e) {
      System.err.println(e);
    }
    finally {
      try {
        if (connection != null) connection.close(  );
      }
      catch (IOException e) {} 
    }

  }

}

Whois

Whois is a simple directory service protocol defined in RFC 954; it was originally designed to keep track of administrators responsible for Internet hosts and domains. A whois client connects to one of several central servers and requests directory information for a person or persons; it can usually give you a phone number, an email address, and a U.S. mail address (not necessarily current ones though). With the explosive growth of the Internet, flaws have become apparent in the whois protocol, most notably its centralized nature. A more complex replacement called whois++ is documented in RFCs 1913 and 1914 but is not yet widely implemented.

Let’s begin with a simple client to connect to a whois server. The basic structure of the whois protocol is:

  1. The client opens a TCP socket to port 43 on the server whois.internic.net. When you’re using whois, you almost always connect to this server; there are a few other servers, but these are relatively rare; there’s also a separate whois server for the U.S. Department of Defense. This is actually the protocol’s biggest problem: all the information is concentrated in one place. Not only is it inefficient and subject to network outages, but when the one company maintaining the central server decides to unilaterally change the protocol for its own private benefit, as Network Solutions has indeed done several times, everybody else’s client software breaks instantaneously.

  2. The client sends a search string terminated by a carriage return/linefeed pair ( ). The search string can be a name, a list of names, or a special command, as discussed below. You can also search for domain names, like oreilly.com or netscape.com, which give you information about a network.

  3. The server sends an unspecified amount of human-readable information in response to the command and closes the connection.

  4. The client displays this information to the user.

The search string the client sends has a fairly simple format. At its most basic, it’s just the name of the person you’re searching for. Here’s a simple whois search for “Harold”. The phone numbers have been changed, and about two-thirds of the hits have been deleted. If this is what you get with a search for “Harold”, imagine a search for “John” or “Smith”:

% telnet whois.internic.net 43
Trying 198.41.0.10-..
Connected to rs.internic.net.
Escape character is '^]'.
Harold
The Data in Network Solutions' WHOIS database is provided by Network
Solutions for information purposes, and to assist persons in obtaining
information about or related to a domain name registration record.
Network Solutions does not guarantee its accuracy.  By submitting a
WHOIS query, you agree that you will use this Data only for lawful
purposes and that, under no circumstances will you use this Data to:
(1) allow, enable, or otherwise support the transmission of mass
unsolicited, commercial advertising or solicitations via e-mail
(spam); or  (2) enable high volume, automated, electronic processes
that apply to Network Solutions (or its systems).  Network Solutions
reserves the right to modify these terms at any time.  By submitting
this query, you agree to abide by this policy.
Aborting search 50 records found .....
Harold (SAGECAT-DOM)                                               SAGECAT.COM
Harold (ANYTHING-GETS-DOM)                                   ANYTHING-GETS.COM
Harold (CAJUNGLORY-DOM)                                         CAJUNGLORY.COM
Harold (BONITAMENSCLUB-DOM)                                 BONITAMENSCLUB.ORG
Harold (HA793-ORG)      [email protected]             780-555-4032
Harold (HA1305-ORG)    [email protected]               (408)555-7698
Harold (MYSTICSINTHEWORKPLACE-DOM)                   MYSTICSINTHEWORKPLACE.COM
Harold (KOC-COLORADO-DOM)                                     KOC-COLORADO.ORG
Harold (PSYCHRESOURCES3-DOM)                                PSYCHRESOURCES.NET
Harold (PHONESECURE-DOM)                                       PHONESECURE.COM
Harold  Adams (HJADAMS3-DOM)                                       HJADAMS.NET
Harold  E.  Fields (LOWSURF-DOM)                                   LOWSURF.COM
Harold  E., Fortner (FH3120)    [email protected]         828-555-0597

To single out one record, look it up with "!xxx", where xxx is the
handle, shown in parenthesis following the name, which comes first.
Connection closed by foreign host.

Although the previous input has a pretty clear format, that format is regrettably nonstandard. Different whois servers can and do send decidedly different output. For example, here are some results from the same search at the main French whois server, whois.nic.fr :

% telnet whois.nic.fr 43
telnet whois.nic.fr 43
Trying 192.134.4.18...
Connected to winter.nic.fr.
Escape character is '^]'.
Harold

Rights restricted by copyright.
See http://www.ripe.net/db/dbcopyright.html
Tous droits reserves par copyright.
Voir http://www.nic.fr/info/whois/dbcopyright.html


person:      Harold Schilmper
address:     Toshiba Elec. Europe GmbH
address:     Elitinger Str. 61
address:     D-W-7250 Leonberg
address:     Germany
phone:       +49 7152 555530
fax-no:      +49 7152 555545
e-mail:      hs%[email protected]
nic-hdl:     HS6091-RIPE
changed:     [email protected] 19920422
changed:     [email protected] 19920424
changed:     [email protected] 19990615
source:      RIPE

person:      Harold Cawood
address:     N G Bailey and Co Ltd
address:     Heathcote, Kings Road
address:     Ilkley
address:     West Yorkshire LS29 9AS
address:     United Kingdom
phone:       +44 943 555234
fax-no:      +44 943 555391
nic-hdl:     HC744-RIPE
changed:     [email protected] 19930913
changed:     [email protected] 19990615
source:      RIPE

Here each complete record is returned rather than just a summary. Other whois servers may use still other formats. This protocol is not at all designed for machine processing. You pretty much have to write new code to handle the output of each different whois server. However, regardless of the output format, each response likely contains a handle, which in the Internic output is in parentheses, and in the nic.fr output is in the nic-hdl field. Handles are guaranteed to be unique, and are used to get more specific information about a person or a network. If you search for a handle, you will get at most one match. If your search only has one match, either because you’re lucky or you’re searching for a handle, then the server returns a more detailed record. Here’s a search for oreilly.com. Because there is only one oreilly.com in the database, the server returns all the information it has on this domain:

% telnet whois.internic.net 43
Trying 198.41.0.6...
Connected to rs.internic.net.
Escape character is '^]'.
oreilly.com
The Data in Network Solutions' WHOIS database is provided by Network
Solutions for information purposes, and to assist persons in obtaining
information about or related to a domain name registration record.
Network Solutions does not guarantee its accuracy.  By submitting a
WHOIS query, you agree that you will use this Data only for lawful
purposes and that, under no circumstances will you use this Data to:
(1) allow, enable, or otherwise support the transmission of mass
unsolicited, commercial advertising or solicitations via e-mail
(spam); or  (2) enable high volume, automated, electronic processes
that apply to Network Solutions (or its systems).  Network Solutions
reserves the right to modify these terms at any time.  By submitting
this query, you agree to abide by this policy.

Registrant:
O'Reilly & Associates (OREILLY6-DOM)
   101 Morris Street
   Sebastopol, CA 95472

   Domain Name: OREILLY.COM

   Administrative Contact, Technical Contact, Zone Contact:
      Pearce, Eric  (EP86)  [email protected]
      707-829-0515 x221
   Billing Contact:
      Johnston, Rick  (RJ724)  [email protected]
      707-829-0515 x331

   Record last updated on 05-Jan-99.
   Record created on 27-May-97.
   Database last updated on 30-Aug-99 04:05:23 EDT.

   Domain servers in listed order:

   NS.ORA.COM                   207.25.97.8
   NS1.SONIC.NET                208.201.224.11

Connection closed by foreign host.

It’s easy to implement a simple whois client that connects to whois.internic.net and searches for names entered on the command line. Example 10.9 is just such a client. It would not be hard to expand this client to allow searching other servers. You’d simply have to provide a command-line flag like -h to allow the user to choose a different host.

Example 10-9. A Command-line Whois Client

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

public class WhoisClient {

  public final static int DEFAULT_PORT = 43;
  public final static String DEFAULT_HOST = "whois.internic.net";

  public static void main(String[] args) {

    InetAddress server;
    
    try {
      server = InetAddress.getByName(DEFAULT_HOST);
    }
    catch (UnknownHostException e) {
      System.err.println("Error: Could not locate default host "
       + DEFAULT_HOST);
      System.err.println(
        "Check to make sure you're connected to the Internet and that DNS is " 
      System.err.println("Usage: java WhoisClient host port");         
      return;
    }       
    
    int port = DEFAULT_PORT;

    try {
      Socket theSocket = new Socket(server, port);
      Writer out = new OutputStreamWriter(theSocket.getOutputStream(  ), 
       "8859_1");
      for (int i = 0; i < args.length; i++) out.write(args[i] + " ");
      out.write("
");
      out.flush(  );
      InputStream raw = theSocket.getInputStream(  );
      InputStream in  = new BufferedInputStream(theSocket.getInputStream(  ));
      int c;
      while ((c = in.read(  )) != -1) System.out.write(c);
    }
    catch (IOException e) {
      System.err.println(e);
    }

  }

}

The class has two final static fields: the port, 43, and the hostname, whois.internic.net. This client always connects to this host and port; it doesn’t give the user a choice because there are few situations in which you need to use another server. The main( ) method begins by opening a socket to whois.internic.net on port 43. The Socket’s OutputStream is chained to an OutputStreamWriter. Then each argument on the command-line is written on this stream and thus sent out over the socket to the whois server. A carriage return/linefeed is written, and the writer is flushed.

Next, the Socket’s InputStream is stored in the variable raw and this is buffered using the BufferedInputStream in. Since whois is known to use ASCII, bytes are read from this stream with read( ) and copied onto System.out until read( ) returns -1, which signals the end of the server’s response. Each character is simply copied onto System.out.

The whois protocol supports several flags you can use to restrict or expand your search. For example, if you know you want to search for a person named “Elliott” but you aren’t sure whether he spells his name “Elliot”, “Elliott”, or perhaps even something as unlikely as “Elliotte”, you would type:

% whois Person Partial Elliot

This tells the whois server that you want only matches for people (not domains, gateways, groups, or the like) whose name begins with the letters “Elliot”. Unfortunately, you need to do a separate search if you want to find someone who spells his name “Eliot”. The rules for modifying a search are summarized in Table 10.1. Each prefix should be placed before the search string on the command line.

Table 10-1. Whois Prefixes

Prefix

Meaning

Domain

Find only domain records.

Gateway

Find only gateway records.

Group

Find only group records.

Host

Find only host records.

Network

Find only network records.

Organization

Find only organization records.

Person

Find only person records.

ASN

Find only autonomous system number records.

Handle or !

Search only for matching handles.

Mailbox or @

Search only for matching email addresses.

Name or :

Search only for matching names.

Expand or *

Search only for group records and show all individuals in that group.

Full or =

Show complete record for each match.

Partial or suffix

Match records that start with the given string.

Summary or $

Show just the summary, even if there’s only one match.

SUBdisplay or %

Show the users of the specified host, the hosts on the specified network, etc.

These keywords are all useful, and you could use them with the command-line client of Example 10.10, but they’re way too much trouble to remember. In fact, most people don’t even know that they exist. They just type “whois Harold” at the command-line and sort through the mess that comes back. A good whois client doesn’t rely on users remembering arcane keywords. Rather, it shows them the options they have to choose from. This requires a graphical user interface for end users and a better API for client programmers.

Example 10.11 is a more reusable Whois class. The state of each Whois object is defined by two fields: host, an InetAddress object, and port, an int. Together these define the server that this particular Whois object will connect to. Five constructors set these fields from various combinations of arguments. The getPort( ) and getHost( ) accessor methods return the values of these fields. However, there are no setter methods so that these objects will be immutable and thread safety will not be a large concern.

The main functionality of the class is in one method, lookUpNames( ). The lookUpNames( ) method returns a String containing the whois response to a given query. The arguments specify the string to search for, what kind of record to search for, which database to search in, and whether an exact match is required. We could have used strings or int constants to specify the kind of record to search for and the database to search in, but since there are only a small number of valid values, we define public inner classes with a fixed number of members instead. This provides much stricter compile-time type checking and guarantees we won’t have to handle an unexpected value.

Example 10-10. The Whois Class

import java.net.*;
import java.io.*;
import com.macfaq.io.SafeBufferedReader; // see Chapter 4


public class Whois {

  public final static int DEFAULT_PORT = 43;
  public final static String DEFAULT_HOST = "whois.internic.net";

  private int port = DEFAULT_PORT;
  private InetAddress host;

  public Whois(InetAddress host, int port) {
    this.host = host;
    this.port = port;     
  }

  public Whois(InetAddress host) {
    this(host, DEFAULT_PORT);     
  }

  public Whois(String hostname, int port)throws UnknownHostException

    this(InetAddress.getByName(hostname), port); 
  }

  public Whois(String hostname) throws UnknownHostException {
    this(InetAddress.getByName(hostname), DEFAULT_PORT);     
  }

  public Whois(  ) throws UnknownHostException {
    this(DEFAULT_HOST, DEFAULT_PORT);     
  }

  // Items to search for
  public static class SearchFor {
        
    public static SearchFor ANY = new SearchFor(  );
    public static SearchFor NETWORK = new SearchFor(  );
    public static SearchFor PERSON = new SearchFor(  );
    public static SearchFor HOST = new SearchFor(  );
    public static SearchFor DOMAIN = new SearchFor(  );
    public static SearchFor ORGANIZATION = new SearchFor(  );
    public static SearchFor GROUP = new SearchFor(  );
    public static SearchFor GATEWAY = new SearchFor(  );
    public static SearchFor ASN = new SearchFor(  );
    
    private SearchFor(  ) {};  
        
  }
  
  // Categories to search in
  public static class SearchIn {
        
    public static SearchIn ALL = new SearchIn(  );
    public static SearchIn NAME = new SearchIn(  );
    public static SearchIn MAILBOX = new SearchIn(  );
    public static SearchIn HANDLE = new SearchIn(  );
    
    private SearchIn(  ) {};  
        
  }

  public String lookUpNames(String target, SearchFor category, 
   SearchIn group, boolean exactMatch) throws IOException {
      
    String suffix = "";
    if (!exactMatch) suffix = ".";
  
    String searchInLabel  = "";
    String searchForLabel = "";
    
    if (group == SearchIn.ALL) searchInLabel = "";
    else if (group == SearchIn.NAME) searchInLabel = "Name ";
    else if (group == SearchIn.MAILBOX) searchInLabel = "Mailbox ";
    else if (group == SearchIn.HANDLE) searchInLabel = "!";
    
    if (category == SearchFor.NETWORK) searchForLabel = "Network ";
    else if (category == SearchFor.PERSON) searchForLabel = "Person ";
    else if (category == SearchFor.HOST) searchForLabel = "Host ";
    else if (category == SearchFor.DOMAIN) searchForLabel = "Domain ";
    else if (category == SearchFor.ORGANIZATION) {
      searchForLabel = "Organization ";
    }
    else if (category == SearchFor.GROUP) searchForLabel = "Group ";
    else if (category == SearchFor.GATEWAY) {
      searchForLabel = "Gateway ";
    }
    else if (category == SearchFor.ASN) searchForLabel = "ASN ";
    
    String prefix = searchForLabel + searchInLabel;
    String query = prefix + target + suffix;
        
    Socket theSocket = new Socket(host, port);
    Writer out 
     = new OutputStreamWriter(theSocket.getOutputStream(  ), "ASCII");
    SafeBufferedReader in = new SafeBufferedReader(new 
     InputStreamReader(theSocket.getInputStream(  ), "ASCII"));
    out.write(query + "
");
    out.flush(  );
    StringBuffer response = new StringBuffer(  );
    String theLine = null;
    while ((theLine = in.readLine(  )) != null) {
      response.append(theLine);
      response.append("
");
    }
    theSocket.close(  );
    
    return response.toString(  );

  }
  
  public InetAddress getHost(  ) {
    return this.host;     
  }

  public int getPort(  ) {
    return this.port;     
  }

}

Figure 10.1 shows one possible interface for a graphical whois client that depends on Example 10.11 for the actual network connections. This interface has a text field to enter the name to be searched for and a checkbox to determine whether the match should be exact or partial. A group of radio buttons lets users specify which group of records they want to search. Another group of radio buttons chooses the fields that should be searched. By default, this client searches all fields of all records for an exact match.

A graphical whois client

Figure 10-1. A graphical whois client

When a user enters a string in the Whois: text field and presses Enter or the Find button, the program makes a connection to the whois server and retrieves records that match that string. These are placed in the text area in the bottom of the window. Initially, the server is set to whois.internic.net, but the user is free to change this. Example 10.11 is the program that produces this interface.

Example 10-11. A Graphical Whois Client Interface

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;


public class WhoisGUI extends JFrame {

  private JTextField searchString = new JTextField(30);
  private JTextArea names = new JTextArea(15, 80);
  private JButton findButton = new JButton("Find");;
  private ButtonGroup searchIn = new ButtonGroup(  );
  private ButtonGroup searchFor = new ButtonGroup(  );
  private JCheckBox exactMatch = new JCheckBox("Exact Match", true);
  private JTextField chosenServer = new JTextField(  );
  private Whois server;
  
  public WhoisGUI(Whois whois) {
  
    super("Whois");
    this.server = whois;    
    Container pane = this.getContentPane(  );
    
    // whois.internic.net assumes a monospaced font, 72 columns across
    Font f = new Font("Monospaced", Font.PLAIN, 12);
    names.setFont(f);
    names.setEditable(false);
    
    JPanel centerPanel = new JPanel(  );
    centerPanel.setLayout(new GridLayout(1, 1, 10, 10));
    JScrollPane jsp = new JScrollPane(names);
    centerPanel.add(jsp);
    pane.add("Center", centerPanel);   
    
    // You don't want the buttons in the south and north
    // to fill the entire sections so add Panels there
    // and use FlowLayouts in the Panel
    JPanel northPanel = new JPanel(  );
    JPanel northPanelTop = new JPanel(  );
    northPanelTop.setLayout(new FlowLayout(FlowLayout.LEFT));
    northPanelTop.add(new JLabel("Whois: "));
    northPanelTop.add("North", searchString);
    northPanelTop.add(exactMatch);
    northPanelTop.add(findButton);
    northPanel.setLayout(new BorderLayout(2,1));
    northPanel.add("North", northPanelTop);
    JPanel northPanelBottom = new JPanel(  );
    northPanelBottom.setLayout(new GridLayout(1,3,5,5));
    northPanelBottom.add(initRecordType(  ));
    northPanelBottom.add(initSearchFields(  ));
    northPanelBottom.add(initServerChoice(  ));
    northPanel.add("Center", northPanelBottom);

    pane.add("North", northPanel);
      
    ActionListener al = new LookupNames(  );  
    findButton.addActionListener(al);
    searchString.addActionListener(al);
        
  }
  
  private JPanel initRecordType(  ) {
  
    JPanel p = new JPanel(  );
    p.setLayout(new GridLayout(6, 2, 5, 2));
    p.add(new JLabel("Search for:"));
    p.add(new JLabel(""));
    
    JRadioButton any = new JRadioButton("Any", true);
    any.setActionCommand("Any");
    searchFor.add(any);
    p.add(any);

    p.add(this.makeRadioButton("Network"));
    p.add(this.makeRadioButton("Person"));
    p.add(this.makeRadioButton("Host"));
    p.add(this.makeRadioButton("Domain"));
    p.add(this.makeRadioButton("Organization"));
    p.add(this.makeRadioButton("Group"));
    p.add(this.makeRadioButton("Gateway"));
    p.add(this.makeRadioButton("ASN"));

    return p;
  
  }

  private JRadioButton makeRadioButton(String label) {
    
    JRadioButton button = new JRadioButton(label, false);
    button.setActionCommand(label);
    searchFor.add(button);
    return button;
    
  }

  private JRadioButton makeSearchInRadioButton(String label) {
    
    JRadioButton button = new JRadioButton(label, false);
    button.setActionCommand(label);
    searchIn.add(button);
    return button;
    
  }

  private JPanel initSearchFields(  ) {
  
    JPanel p = new JPanel(  );
    p.setLayout(new GridLayout(6, 1, 5, 2));
    p.add(new JLabel("Search In: "));

    JRadioButton all = new JRadioButton("All", true);
    all.setActionCommand("All");
    searchIn.add(all);
    p.add(all);

    p.add(this.makeSearchInRadioButton("Name"));
    p.add(this.makeSearchInRadioButton("Mailbox"));
    p.add(this.makeSearchInRadioButton("Handle"));

    return p;
  
  }
  
  private JPanel initServerChoice(  ) {
  
    JPanel p = new JPanel(  );
    p.setLayout(new GridLayout(6, 1, 5, 2));
    p.add(new JLabel("Search At: "));

    chosenServer.setText(server.getHost().getHostName(  ));
    p.add(chosenServer);
    chosenServer.addActionListener( new ActionListener(  ) {
      public void actionPerformed(ActionEvent evt) { 
        try {
          InetAddress newHost 
           = InetAddress.getByName(chosenServer.getText(  ));
          Whois newServer = new Whois(newHost);
          server = newServer;  
        }
        catch (Exception e) {
          // should use an error dialog here, but that
          // doesn't teach much about networking
          chosenServer.setText(server.getHost().getHostName(  ));
        }
      }
    } );
    
    return p;
  
  }

  class LookupNames implements ActionListener {

    public void actionPerformed(ActionEvent evt) {
        
      Whois.SearchIn group = Whois.SearchIn.ALL;
      Whois.SearchFor category = Whois.SearchFor.ANY;
      
      String searchForLabel = searchFor.getSelection().getActionCommand(  );
      String searchInLabel = searchIn.getSelection().getActionCommand(  );
      if (searchInLabel.equals("Name")) group = Whois.SearchIn.NAME; 
      else if (searchInLabel.equals("Mailbox")) {
        group = Whois.SearchIn.MAILBOX;
      }
      else if (searchInLabel.equals("Handle")) {
        group = Whois.SearchIn.HANDLE;
      }

      if (searchForLabel.equals("Network")) {
        category = Whois.SearchFor.NETWORK;
      }
      else if (searchForLabel.equals("Person")) {
        category = Whois.SearchFor.PERSON;
      }
      else if (searchForLabel.equals("Host")) {
        category = Whois.SearchFor.HOST;
      }
      else if (searchForLabel.equals("Domain")) {
        category = Whois.SearchFor.DOMAIN;
      }
      else if (searchForLabel.equals("Organization")) {
        category = Whois.SearchFor.ORGANIZATION;
      }
      else if (searchForLabel.equals("Group")) {
        category = Whois.SearchFor.GROUP;
      }
      else if (searchForLabel.equals("Gateway")) {
        category = Whois.SearchFor.GATEWAY;
      }
      else if (searchForLabel.equals("ASN")) {
        category = Whois.SearchFor.ASN;
      }

      try {
        names.setText("");
        String result = server.lookUpNames(searchString.getText(  ), 
         category, group, exactMatch.isSelected(  ));
        names.setText(result);
      }
      catch (IOException e) { 
        names.setText("Lookup failed due to " + e);
      }
    }
    
  }

  public static void main(String[] args) {
  
    try {
      Whois server = new Whois(  );
      WhoisGUI a = new WhoisGUI(server);
      a.addWindowListener(new WindowAdapter(  ) {
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      });
      a.pack(  );
      a.show(  );     
    }
    catch (UnknownHostException e) {
      System.err.println("Error: Could not locate default host "
       + Whois.DEFAULT_HOST);
      System.err.println("Check to make sure you're connected to the"
       + " Internet and that DNS is" funtioning");
      System.err.println("Usage: java WhoisGUI");         
      return;        
    }

  }

}

The main( ) method is the usual block of code to start up a standalone application. It constructs a Whois object, then uses that to construct a WhoisGUI object. Then the WhoisGUI ( ) constructor sets up the graphical user interface. There’s a lot of redundant code here, so it’s broken out into the private methods initSearchFields( ), initServerChoice( ), makeSearchInRadioButton( ), and makeSearchForRadioButton( ). As usual with LayoutManager-based interfaces, the setup is fairly involved. Since you’d probably use a visual designer to build such an application, I won’t describe it in detail here.

When the constructor returns, the main( ) method attaches an anonymous inner class to the window that will close the application when the window is closed. (This isn’t in the constructor because other programs that use this class may not want to exit the program when the window closes.) It then packs and shows the window. From that point on, all activity takes place in the AWT thread.

The first event this program must respond to is the user’s typing a name in the Whois: text field and either pressing the Find button or hitting Enter. In this case, the LookupNames inner class passes the information in the text field and the various radio buttons and checkboxes to the server.lookUpNames( ) method. This method returns a String, which is placed in the names text area.

The second event this program must respond to is the user typing a new host in the server text field. In this case, an anonymous inner class tries to construct a new Whois object and store it in the server field. If it fails (e.g., because the user mistyped the hostname), then the old server is restored. It would be a good idea to tell the user this by putting up an alert box, but I omitted that to keep this example a more manageable size.

This is not a perfect client by any means. The most glaring omission is that it doesn’t provide a way to save the data and quit the program. Less obvious until you run the program is that responsiveness suffers because the network connection is made inside the AWT thread. It would be better to place the connections to the server in their own thread, then use callbacks to place the data in the GUI as the data is received. However, implementing these would take us too far afield from the topic of network programming, so I leave them as exercises for the reader.



[18] Failure to filter non-printable characters allows mischievous users to configure their .plan files to reset people’s terminals, switch them into graphics mode, or play other tricks accessible to those with intimate knowledge of VT-terminal escape sequences. While amusing to experienced users who recognize what’s going on and appreciate the hack value of such .plan files, these tricks do confuse and terrify the uninitiated.

[19] Vending machines connected to the Internet return a list of items available for purchase instead.

[20] The .plan file is a peculiarity of Unix; you won’t find this file on other operating systems.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.11.98