Java, Java, Java!
Chapter 15: Sockets and Networking
In the Laboratory: The Client Server Stock Quoter

R. Morelli

©2000 Prentice-Hall


In the Laboratory: The Client Server Stock Quoter

Purpose

The purpose of this lab is to develop a stock market quote service by extending the generic client/server model developed in the generic client/server example. As the interface to this service, we will implement a minimal GUI interface. The objectives of this lab are

Problem Description

Design and implement a stock market quote service by extending the client/server model developed in the generic client/server. The stock market quotes will be stored in a tab delimited text file that is accessible to the server -- for example, in the directory of the server itself. The service works as follows: The client prompts the user for the stock's symbol. It then sends the symbol, as a query, to the quote server. The quote server looks up the stock in the file and returns, as a single line, the stock symbol, name, and current price. If the server can't find the stock, it should return a line which reports ``ERROR: Stock not found.'' The client should report this error as ``Sorry, the stock you requested cannot be found. Please make sure your symbol is correct and try again.''

The quote file stores the stock data as tab delimited text, with one stock per line. That is, there is a tab character, \t, between each field in the line:

    AppleC     Apple Computer   45.0
    Cisco      Cisco Systems    103.58
    Intel      Intel            129.25
    MCSFT      Microsoft        150.50
    Netscpe    Netscape	        63.87
    SunMic     Sun Microsystems 89.75

Note on Running the Solution

Running a solution to this lab depends on being able to deploy the server classes on a server computer. Your instructor will give you instructions on how this can be done for your lab.

User Interface

There are two possible interfaces for the client/server stock application, a keyboard interface and a simple GUI interface. Both interfaces require that the server is running on some computer available by its URL. To start the server you type:
    java StockServer portNumber
where portNumber gives the port number at which the service will be run. It's good to use high numbers, such as 10001.

For the keyboard interface, you start the client by typing:

    java StockClient serverURL serverPort
where the command line arguments give the server's URL and port number. Once the StockClient starts running, it will prompt the user to input a stock symbol and will query the server and report the result.

For the GUI interface, you start the client by typing:

    java StockClientGUI serverURL serverPort
This will open a top-level frame containing a TextField and an TextArea. Each time a stock symbol is typed into the text field the client will query the server and report the result.

A Graphical User Interface for the Client

If you implement the Graphical User Interface (GUI) rather than a command line interface, it should contain a JTextField into which the user can enter the stock's symbol, and a JTextArea where the client can report the stock quotes. It is not necessary to clear the JTextArea after every quote. For example, Figure 1 shows an appropriate design for a simple client interface.


Figure 1: A GUI design for the stock quote application.

Designing the Stock Quote Service

It is important that the user interface for this application be kept distinct from the client itself. That will make it possible for the client part of the service to be attached to other interfaces -- for example, a GUI.

Problem Decomposition

Our goal is to develop a client/server application to provide stock quotes. Given that we want the interface to be kept separate from the client, this suggests that we need three separate classes:

1.
StockServer will supply stock quotes.
2.
StockClient will request stock quotes on behalf of the user.
3.
ClientInterface will get the user input and pass it along to the StockClient object.

Design: StockServer

The StockServer class should be derived from the generic Server class. You should use the EchoServer as a model. What's new in StockServer is that it has to be able to read stock data from a file in order to supply answers to the queries it will receive. One strategy that the server could use is to read the entire file of quotes into some sort of memory structure when it starts up and then just look them up from there as needed. However, because it's important that the stock quotes be up to date, it's probably a better idea to search the quote file itself each time a query is received.

One method that the StockServer must implement is the provideService() method. Once a proper connection is established with the client, this method should carry out something like the following algorithm:

1.
Read a stock symbol from the client [readFromSocket()].
2.
Look up the symbol's record in stock file.
3.
Write a reply to the client writeToSocket().

What methods does the StockServer need, in addition to provideService()? Steps 1 and 3 of the above algorithm should be simple method calls. However, step 2 will require several statements, and possibly even additional methods. Therefore, let's design a method, named getQuoteBySymbol(), to handle this task.

The getQuoteBySymbol() method

According to our specification, the server should receive a symbol string from the client and return a string to the client. This suggests that the getQuoteBySymbol() method should take a String parameter, the stock symbol, and return a String result, the complete stock quote, as described in the specification. Because this method will be used only within this class, it should be declared private.

In terms of an algorithm for this method, have we already solved the problem of reading data from a text file? Yes. The following algorithm from Chapter 14 can easily be adapted to read the stock file:

    BufferedReader inStream =                  // Create an input stream
        new BufferedReader (new FileReader(fileName));  // And open the file
    String line = inStream.readLine();         // Read one line
    while (line != null) {                     // As long as there are more lines
        display.append(line + "\n");           //  Display the current line
        line = inStream.readLine();            //  Read the next line
    }
    inStream.close();                          // close the input stream

What sort of adaptations will we need to make? One change is that it may not be necessary to read every line of the file. If we find the stock we're searching for before the end of the file, we can just execute a return statement, thereby exiting the while loop before the end of file. Some of the other design questions you should think about in designing this method are

The StockClient Class

The StockClient class should be derived from Client. Therefore, it must implement the requestService() method. What's new in this class is that we want to separate its communication functions from its interface functions. Recall that EchoClient used a method named readFromKeyBoard() to get the user's query. We want StockClient to get its input -- a stock symbol -- from the interface. Another difference is that StockClient should respond to user's initiatives, rather than simply run through a loop that prompts the user on each iteration. How can these changes be done?

Another design issue for StockClient concerns how long it should keep open the connection. The user will initiate queries, so it would not be good design to keep the connection open if the user hasn't even requested a stock quote. To do so would prevent other clients from accessing the server. A better strategy would be to create a new StockClient each time the user initiates a query, have the client make one query, and then say goodbye.

It looks as if we don't want our StockClient to run() in a loop at all. To prevent this we just won't call its start() method. Instead, we want to be able to call a method (from the interface class) that causes the StockClient to send a query to the StockServer. Thus, we need a public method that takes a String parameter, a stock symbol, and returns a String, the stock quote. An appropriate algorithm for the getQuote() method would be

    public String getQuote( String symbol ) {
        String replyStr = "";
       // write the symbol to the socket
       // read the server's reply
       // say "goodbye" to the server
       return replyStr;
    }

Given this design for getQuote(), each time the user initiates a query through the interface, the interface class would execute something like the following code:

    StockClient sc = new StockClient( host, port );
    String result = sc.getQuote( symbol );

What about the requestService() method? How would our design make use of it? It looks as if we don't even need to use this method, so we can just implement it as a stub method -- that is, we can leave its body empty. In other words, this subclass of client will not use the run() or the requestService() methods.

Design: Client/Server Communication

In designing a client/server application, you must take care that both the server and the client know each other's protocol. For example, after the connection is made, will the server say ``hello'' rather than just waiting for a query from the client? If so, then the client must read the ``hello'' before sending a query. Similarly, how will the client indicate that it is done? The server must be aware of how the client says ``goodbye'' so that it can close the connection. The important point is that both the client and server must agree upon the protocol they will use for communication. Otherwise communication will inevitably break down.

Design: ClientInterface

Develop a simple frame-based interface to test your client. This class should implement the GUI described earlier.

Debugging and Testing

Testing a client/server application presents a real challenge because there are so many different kinds of things that can go wrong. That's why it is especially important to develop your application in stages using stepwise refinement. Here's a stepwise development plan that you might wish to follow:

1.
Test that StockServer successfully waits for a connection.
2.
Test that StockServer can find a symbol in the data file.
3.
Test that StockClient successfully makes a connection to StockServer
4.
Test that StockServer and StockClient can successfully communicate an appropriate ``hello'' and ``goodbye'' protocol.
5.
Test that StockClient can send an appropriately formatted query to StockServer.
6.
Test that StockServer returns the correct result.

One advantage of using a tab-delimited text file, instead of a binary file, is that you can easily type in some test data.

Handin

Hand in properly documented copy of your source files: StockServer.java, StockClient.java and, if you created a GUI interface, StockClientGUI.java.

Good Work Class



Ralph Morelli {Faculty}
12/21/1999