Java Network Programming (3rd ed) [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Java Network Programming (3rd ed) [Electronic resources] - نسخه متنی

Harold, Elliotte Rusty

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








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\10>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\10>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:

The client opens a TCP socket to port 43 on the server.

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.

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

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  43
Trying 198.41.0.6...
Connected to .
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/dbcopyrightl
Rights restricted by copyright.
See http://www.nic.fr/outils/dbcopyrightl
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  43
Trying 198.41.0.6...
Connected to .
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 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 = ";
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, . 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 = ";
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

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 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.


/ 164