CMSC 132 -- Project 3


Project Due: Friday 3/4/05 at 11:00 PM

Update: The project due date has been extended to Monday 3/7/05 at 11:00 PM

Closed Policy

This is a closed project.  You are expected to do all of the work on this project without consulting with anyone other than the CMSC 132 instructors and TAs.  Treat this project as though it were a take home exam.  If you have any questions about whether or not a certain topic is okay to discuss with classmates or friends, please ask your instructor.



The goals of this project are:



This project is really two projects in one:  the client application and the server application.

The typical interaction between the two components is as follows.  Once the client has established a connection to the server, the client will send a request that consists of a String containing two lines of text.  The first line is the name of a website.  The second line is an integer.  For example, a typical request from the client to the server may look like:

The server will attempt to contact the website mentioned in the request.  It will then attempt to find the nth image on the website, where n is the number located on the second line of the request.  It will then return to the client a String representing the URL of this image (not the image itself, but a String representing the URL of the image).  At the moment, the second image found on the CS department webpage is located at:

So in this example, the server would return a String containing the text that you see above.

We are providing a nice GUI for the client where you can type in the website and picture number that you are interested in.  The GUI will automatically make a two-line String out of these two parameters and pass it to the Client that you will write.  Your client will send the String to the server, and your server will send a String back with the URL of the corresponding image.  Your client merely returns this String to the GUI, which will go out on the internet, find the image and draw the image on the screen.  (Again, we have written the GUI for you -- you are not dealing with images, just Strings.)



Server GUI

We have provided a "Server GUI" which you should run to invoke the Server that you will write.  (The ServerGUI class has a main method which will instantiate and start the Server, which you will provide.)  Here is what it looks like when you run it:


Client GUI

Once you have the server running, you can run the "Client GUI", which will manage the Client that you will write.  (The ClientGUI class has a main method which will instantiate and control the Client, which you will provide.)  Here is what it looks like when you run it:


"Retrieve Picture" Button

Before you press one of the buttons, be sure to type in the port number that the server is running on!  Here is what you might see after you have entered the correct port and pressed the "Retrieve Picture" button:

Notice that the "Pic Number" field increments itself automatically, so if you want to see the next picture from the CS website, you just have to press the button again.  Notice the line at the bottom of the window which tells you what is going on.


"Send Ping to Server" Button

The "Ping" button sends a different kind of request to the server.  (See "Writing the Server" for more information about how the Server processes this special request.)  After pressing the "Ping" button, you might see something like this:

Notice the line at the bottom of the window which tells you that your client and your server are talking!  This is a good way to test whether or not your client and your server are able to communicate, even before you implement the project fully.


Wrapping of Classes

Sometimes you'll hear someone say "you'll need to wrap a class A object inside a class B object..."  What do they mean?

The idea is usually that there is a constructor for class B that takes a class A object as a parameter.  Suppose you have a class A object called classAObject.  You might wrap it inside a class B object like this:

B classBObject = new B(classAObject);

Why would you do this?  In some cases the interface for class B may be a more convenient way for you to manipulate the class A object than by using the interface provided with class A itself.


Sending Text Back and Forth

Many of the Java classes that you will be using for this project will read and write data using either an InputStream or an OutputStream.  These are not very convenient for us, because we are going to want to send text rather than "raw data".  It would be much more convenient to use a BufferedReader for input and a BufferedWriter for output.  The trick is to do some wrapping in order to use a BufferedReader to manipulate your InputStream, and a BufferedWriter to manipulate your OutputStream.  Hint:  You may have to wrap the InputStream in something else, and then wrap that object in a BufferedReader.  (A similar hint for output applies.)


Writing the Client

You will write just one method for the Client class:

public String queryServer(InetAddress host, int port, String message)

This method will connect to the Server.  The server's IP address and port number are the first two parameters of the method signature.  The method will then write the String "message" to the Server.  Then it will read the Server's response.  The response from the server will always be one line of text, which you should store as a String.  The method returns this String.


If you are sending text with a BufferedWriter, you may need to follow your message with a call to the newLine method.  That way the server will have an easier time reading the last line of the message.  You may also need to call the flush method to clear the buffer and send the message on its way to the server.

Important Notes:


Writing the Server

You must write one method of the Server class, called run.  This method is called implicitly when your server is activated.  (Your server is a "thread".  When someone wants to invoke your Server, they will first instantiate it, then they will call it's start method.  The start method does a lot of things behind the scenes to activate the thread, and then it calls the thread's run method.  You'll learn more about threads later in the semester.  Take a look at the ServerGUI class to see how your server is created and started.)  The signature for the run method that you will write looks like this:

public void run()

Important:  You must use the field serverSocket, which is already provided, to refer to the ServerSocket that you are using.  Do not create another ServerSocket!

Your run method will contain an infinite loop!  Each pass through the loop will handle one transaction with a client.  Use the ServerSocket.accept method to wait for a connection and to obtain the Socket object that you will use to communicate with the client.  Once connected, the server will read in the request from the client.  Your server must be able to handle two distinct types of requests:  Ping Request and Picture Request.

Exception Handling

If any exceptions are generated while your server is attempting to process a request, you must catch them.  In this case, your server will return the String "no image found".  Be sure to spell this message exactly as shown between the quotes.  There is no punctuation.

Processing a Ping Request

If the request sent by the client is a single line of text that says:


then your server will simply return the message "I hear you".  Make sure you spell it exactly as shown between the quotes.  There is no punctuation.

Processing a Picture Request

All requests that are not ping requests will be "picture requests".  A picture request will consist of exactly 2 lines of text.  The first line of text will look like a URL.  The second line of text will look like an integer.  Below is an example:

You should construct a URL object out of the text on the first line.  This will allow you to read from the webpage.  Consult the URL class documentation to find out how to obtain a stream for reading from the webpage.  You may want to wrap the stream somehow to read it in a more convenient way.  One idea is to use a BufferedReader.

Your job is to parse the webpage looking for the nth image, where n is the value listed in the second line of the request.  (If you use a BufferedReader, then it is easy to read in the contents of the webpage as text, which can then be searched for substrings which represent images.)

Below is an example of a webpage that you might want to search for images.

Okay, here are some images. Let's see if your Server can pull them out...
<img src="Homer.jpg">
<img src="Bart.jpg">
<img src="Grandpa.jpg">
<img src="Selma.jpg">
That's it!

This example webpage contains four images.  They are Homer.jpg, Bart.jpg, Grandpa.jpg, and Selma.jpg.

Important:  We are only going to find images that occur in the webpage in one particular way (described below).  There are other ways that pictures can be drawn on webpages -- those don't count.  So when your server is asked to find the seventh image on a webpage, you must skip over the first six images of the kind we are interested in and return information about the seventh one.  Other types of images that may occur are ignored when counting.

The substrings representing images that we are interested in may look like these examples:

Officially, you must consider any substring that contains the sequence of symbols listed below to be an "image".  Anything that does not satisfy this description is not considered an image for this project.  The definition below does NOT conform to proper html formatting in all cases -- we are aware of that so don't bother mentioning it! 

If there are not enough qualifying images on the webpage to get to the one requested, your server should return the String "no image found", exactly as shown between the quotes, with no punctuation.

Important:  Once you have found the image, you must expand it into a full URL.  To do this, construct a URL object as follows:

URL returnValue = new URL(webPage, imageSubstring)

where webPage is the URL of the webpage you are searching, and imageSubstring is a String representing the name of the image.  This will automatically make a proper URL, prepending the webpage location only if necessary.  In other words, whether your imageSubstring looks like "pic1.jpg" or "", you can construct a URL object as shown above and the result will be a correctly formed URL.

Your method should then call the toString method for the returnValue object and write this String as it's response to the client.


If you are sending text with a BufferedWriter, you may need to follow your message with a call to the newLine method.  That way the client will have an easier time reading the message.  You may also need to call the flush method to clear the buffer and send the message on its way to the client.

Important Notes:


Test Cases(14)

The test cases will begin by instantiating your Server as a running Thread.  Then your Client class will be instantiated, and calls will be made to it's queryServer method.  Both of your classes need to do their jobs in order to pass the tests.

Public Tests(2)

  1. The first public test will instruct your Client to send the message "ping" to your Server.  Your Server should reply with "I hear you", and your Client should return this message.

  2. The second public test will instruct your Client to send the following message to the Server:


    The file testPage.html is provided with the project.  (Take a look at it.)  Your server should parse the file and determine that the first picture is "file:Homer.jpg".  This exact String should be sent back to your Client, and your Client should return this String.

Release Tests(12)

There will be 12 release tests designed to check if your server is finding images correctly according to this specification.  Please do not forget that discussions about what the tests are called and/or what they are testing are off-limits unless you are discussing them with course instructors or TAs.



Your grade on this project will be calculated as follows: