9.6 Examples

     

HotJava was one of the first large-scale Java programs; it's a web browser that was easily the equal of the early versions of Mosaic. HotJava has been discontinued, but there are numerous network-aware applications written in Java, including the LimeWire Gnutella client, the Eclipse IDE, and the JBoss application server. 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 illustrate 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.

9.6.1 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, although 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. All lines must end with a carriage return/ linefeed pair ( \r\n in Java parlance).

Failure to filter nonprintable 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.


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. 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. A few servers put finger to other uses; for example, several sites give you a list of recent earthquake activity. Vending machines connected to the Internet return a list of items available for purchase. 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 sends 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 the server response can be read as text. The server's output is presented to the user on System.out. Example 9-8 shows the code.

Example 9-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 ex) {       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("\r\n");       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 == '\t'  c == '\r'  c == '\n')          {            System.out.write(c);         }       }     }     catch (IOException ex) {       System.err.println(ex);     }     finally {       try {         if (connection != null) connection.close( );       }       catch (IOException ex) {}      }   } } 

Here are some samples of this program running:

 D:\JAVA\JNP2\examples>  java FingerClient rama.poly.edu  Login    Name              TTY      Idle    When      Where jacolag  Jane Colaginae   *pts/7            Tue 08:01  208.34.37.104 hengpi   Heng Pin          pts/9    5       Tue 14:09  128.238.18.119 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   1:05    Mon 10:38  128.238.213.227 nadats   Nabeel Datsun     pts/12   1:05    Mon 10:38  128.238.213.227 matewan  Sepin Matewan    *pts/8    14      Sun 18:39  128.238.10.177 D:\JAVA\JNP2\examples>  java FingerClient rama.poly.edu marcus  Login    Name              TTY      Idle    When       Where                   Marcus   Marcus Tullius    pts/15   13d     Tue 17:33  farm-dialup11.                                                        poly.e 

9.6.2 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 snail 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 has not been 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.

  2. The client sends a search string terminated by a carriage return/linefeed pair ( \r\n ). 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":

 %  telnet whois.internic.net 43  Trying 198.41.0.6... Connected to whois.internic.net. Escape character is '^]'.  Harold  Whois Server Version 1.3 Domain names in the .com and .net domains can now be registered with many different competing registrars. Go to http://www.internic.net for detailed information. HAROLD.NET HAROLD.COM To single out one record, look it up with "xxx", where xxx is one of the of the records displayed above. If the records are the same, look them up with "=xxx" to receive a full display for each record. >>> Last update of whois database: Tue, 16 Dec 2003 18:36:16 EST <<< NOTICE: The expiration date displayed in this record is the date the registrar's sponsorship of the domain name registration in the registry is currently set to expire. This date does not necessarily reflect the expiration date of the domain name registrant's agreement with the sponsoring registrar.  Users may consult the sponsoring registrar's Whois database to view the registrar's reported date of expiration for this registration. TERMS OF USE: You are not authorized to access or query our Whois database through the use of electronic processes that are high-volume and automated except as reasonably necessary to register domain names or modify existing registrations; the Data in VeriSign Global Registry Services' ("VeriSign") Whois database is provided by VeriSign for information purposes only, and to assist persons in obtaining information about or related to a domain name registration record. VeriSign does not guarantee its accuracy. By submitting a Whois query, you agree to abide by the following terms of use: You agree that you may 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, telephone, or facsimile; or (2) enable high volume, automated, electronic processes that apply to VeriSign (or its computer systems). The compilation, repackaging, dissemination or other use of this Data is expressly prohibited without the prior written consent of VeriSign. You agree not to use electronic processes that are automated and high-volume to access or query the Whois database except as reasonably necessary to register domain names or modify existing registrations. VeriSign reserves the right to restrict your access to the Whois database in its sole discretion to ensure operational stability.  VeriSign may restrict or terminate your access to the Whois database for failure to abide by these terms of use. VeriSign reserves the right to modify these terms at any time. The Registry database contains ONLY .COM, .NET, .EDU domains and Registrars. 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 the first couple of 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 Tous droits reserves par copyright. Voir http://www.nic.fr/outils/dbcopyright.html Rights restricted by copyright. See http://www.nic.fr/outils/dbcopyright.html person:      Harold Potier address:     ARESTE address:     154 Avenue Du Brezet address:     63000 Clermont-Ferrand address:     France phone:       +33 4 73 42 67 67 fax-no:      +33 4 73 42 67 67 nic-hdl:     HP4305-FRNIC mnt-by:      OLEANE-NOC changed:     hostmaster@oleane.net 20000510 changed:     migration-dbm@nic.fr 20001015 source:      FRNIC person:      Harold Israel address:     LE PARADIS LATIN address:     28 rue du Cardinal Lemoine address:     Paris, France 75005 FR phone:       +33 1 43252828 fax-no:      +33 1 43296363 e-mail:      info@cie.fr nic-hdl:     HI68-FRNIC notify:      info@cie.fr changed:     registrar@ns.il 19991011 changed:     migration-dbm@nic.fr 20001015 source:      FRNIC 

Here each complete record is returned rather than just a list of sites. 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 a domain name, 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 whois.internic.net. Escape character is '^]'.  oreilly.com  Whois Server Version 1.3 Domain names in the .com and .net domains can now be registered with many different competing registrars. Go to http://www.internic.net for detailed information.    Domain Name: OREILLY.COM    Registrar: BULKREGISTER, LLC.    Whois Server: whois.bulkregister.com    Referral URL: http://www.bulkregister.com    Name Server: NS1.SONIC.NET    Name Server: NS.OREILLY.COM    Status: ACTIVE    Updated Date: 17-oct-2002    Creation Date: 27-may-1997    Expiration Date: 26-may-2004 >>> Last update of whois database: Tue, 16 Dec 2003 18:36:16 EST <<< ... 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 9-9 is just such a client. The server can be changed using the WHOIS_SERVER system property, which can be set on the command line using the -D option. I won't claim this is an exemplary user interface, but it's simple enough to code and lets the example focus more on the interesting network parts of the problem.

Example 9-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) {     String serverName = System.getProperty("WHOIS_SERVER", DEFAULT_HOST);            InetAddress server = null;     try {       server = InetAddress.getByName(serverName);     }     catch (UnknownHostException ex) {       System.err.println("Error: Could not locate whois server "        + server);       System.err.println("Usage: java -DWHOIS_SERVER=hostname                                               WhoisClient name");                return;     }            try {       Socket theSocket = new Socket(server, DEFAULT_PORT);       Writer out = new OutputStreamWriter(theSocket.getOutputStream( ),         "8859_1");       for (int i = 0; i < args.length; i++) out.write(args[i] + " ");       out.write("\r\n");       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 ex) {       System.err.println(ex);     }   } } 

The class has two final static fields: the DEFAULT_PORT , 43, and the DEFAULT_HOST , whois.internic.net. The host can be changed by setting the WHOISE_SERVER system property. The main() method begins by opening a socket to this whois server 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 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 , which 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, signaling 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 names begin 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 9-1. Each prefix should be placed before the search string on the command line.

Table 9-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 9-9, 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. Supplying this requires a graphical user interface for end users and a better API for client programmers.

Example 9-10 is a more reusable Whois class. Two fields define the state of each Whois object: host , an InetAddress object, and port , an int . Together, these define the server that this particular Whois object connects to. Five constructors set these fields from various combinations of arguments. Furthermore, the host can be changed using the setHost() method.

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, lookUpNames() defines public inner classes with a fixed number of members instead. This solution provides much stricter compile-time type-checking and guarantees the Whois class won't have to handle an unexpected value.

Example 9-10. The Whois class
 import java.net.*; import java.io.*; import com.macfaq.io.SafeBufferedReader; 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 + "\r\n");     out.flush( );     StringBuffer response = new StringBuffer( );     String theLine = null;     while ((theLine = in.readLine( )) != null) {       response.append(theLine);       response.append("\r\n");     }     theSocket.close( );          return response.toString( );   }      public InetAddress getHost( ) {     return this.host;        }   public void setHost(String host)      throws UnknownHostException {     this.host = InetAddress.getByName(host);        } } 

Figure 9-1 shows one possible interface for a graphical whois client that depends on Example 9-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.

Figure 9-1. A graphical whois client
figs/jnp3_0901.gif

When a user enters a string in the Whois: text field and presses the Enter or 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 setting. Example 9-11 is the program that produces this interface.

Example 9-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( );          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( ) {        final 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 ex) {            JOptionPane.showMessageDialog(p,               ex.getMessage( ), "Alert", JOptionPane.ERROR_MESSAGE);         }       }     } );          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("");         server.setHost(chosenServer.getText( ));         String result = server.lookUpNames(searchString.getText( ),           category, group, exactMatch.isSelected( ));         names.setText(result);       }       catch (IOException ex) {          JOptionPane.showMessageDialog(WhoisGUI.this,            ex.getMessage( ), "Lookup Failed", JOptionPane.ERROR_MESSAGE);       }     }        }   public static void main(String[] args) {        try {       Whois server = new Whois( );       WhoisGUI a = new WhoisGUI(server);       a.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);       a.pack( );       EventQueue.invokeLater(new FrameShower(a));     }     catch (UnknownHostException ex) {       JOptionPane.showMessageDialog(null, "Could not locate default host "        + Whois.DEFAULT_HOST, "Error", JOptionPane.ERROR_MESSAGE);         }   }   private static class FrameShower implements Runnable {        private final Frame frame;          FrameShower(Frame frame) {       this.frame = frame;     }          public void run( ) {      frame.setVisible(true);     }        } } 

The main( ) method is the usual block of code to start up a standalone application. It constructs a Whois object and, 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.) main() then packs and shows the window. To avoid an obscure race condition that can lead to deadlock this needs to be done in the event dispatch thread. Hence the FrameShower inner class that implements Runnable and the call to EventQueue.invokeLater( ) . 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), the old server is restored. An alert box informs the user of this event.

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 and use callbacks to place the data in the GUI as the data is received. However, implementing callbacks would take us too far afield from the topic of network programming, so I leave them as exercises for the reader.



Java Network Programming
Java Network Programming, Third Edition
ISBN: 0596007213
EAN: 2147483647
Year: 2003
Pages: 164

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net