CMSC 202/Advanced Section Project 2

You've Got Mail!
Assigned Tue Sep 28
Program Due 9:00AM Tue Oct 12
Weight TBD
Objectives
To establish your proficiency with:
Description

The goal of the remaining projects for the term is to lead you through the process of designing a large-scale, practical Java application, in an incremental, layered fashion. The end product will be a Java program and class library that should provice hours of amusement, and maybe even act as the seed for the first application release for your Android-centered startup!

We are going to be building a mail client program: an application that a user can run to read and manage a mailbox with a bunch of emails in it. (It is a client program to distinguish it from a mail server, which is a program that runs on server hosts maintaining a large collection of e-mailboxes.) The client app will be able to perform a variety of tasks, including displaying a listing of all email headers, printing out the full contents of a specific email, and even deleting some mail. Nice!

However, you have to learn to walk before you can run. To that end, I will be providing a library of utility classes that will do some of the low-level work for you. If we want to strain another metaphor, you can think of Project 2 as the cycling stage of a triathlon: someone is providing a nice bike for you, with multiple gears and maybe even working brakes, but you will still have to figure out how to use it skillfully to complete this leg of the challenge.

For Project 2, you will be building the upper layer of the mail-reading application. You will be provided with a set of utility class libraries that will provide simple primitives to manage an on-disk mail file that contains multiple email items. You will use these provided classes to create an executable program that can both read existing mail and create new mail.

To test that you know how to create classes as well as use them, your main task will be to build a specific class-- EmailClient-- that performs most or all of the work. It will have three public methods: First, it will have a constructor that accepts as an argument the name of the mail file to be opened. (Not to worry: you do not have to be familiar with file I/O yet; you will simply pass this filename through to one of the utility class methods.) On an instance of the EmailClient class, we will invoke the second public method-- execute()--that will implement a continuous loop prompting the user for the next command, collecting any additional input necessary for the specific command, and then executing the command through the necessary logic and calls to the utility classes. Lastly, you will write a main() method that you can use to test your class. At a minimum, it will instantiate an EmailClient object with a test mail file, and then invoke its execute() method.

A more rigorous, detailed description of these methods and what they must do is given in the "Requirements and Specification" section below.

To make the program a little more relevant, the input file will be structured as a real IMAP mail file (in fact, I will be creating it by sending myself test emails from several other accounts, and then just giving you a copy of that file as it is maintained on the CS mail server). While the utility classes will be parsing the file for you, I think it is still neat that the combination of your application and the provided library can work on real mail files.

Project Requirements and Specification
As stated above, you will be writing most of the upper-middle layer of a standalone mail program. We will be providing the lower layer for you as a library, and we will also be testing your program by running our own class's main(), acting as a client to your class.

Specific requirements for your program are:

  1. You will primarily implement the class EmailClient; in fact, this will likely be the only class you will create.

  2. The EmailClient class is required to have at least the three public methods listed below. You must not change the method header for any of these specific methods listed above. That means you cannot change the method name, parameter names or types, nor method return value type.

    • First, a 1-argument constructor:

      public EmailClient(String emailFolder)

      The parameter emailFolder is the name of the disk file that we wish to use as our mail folder (we will hereafter call it a "mail folder", even though it is a single physical file, because it contains a sequence of multiple email items). Internally, this constructor will then invoke the proper method(s) from the provided library to actually open up that file as the email folder.

    • Second, EmailClient must also define the public method:

      public boolean execute()

      This method will manage the main command loop of the email client. For each turn of the loop, it will prompt the user for, and read in, a command request using the console I/O you learned about in Lab 2. Some commands will have additional arguments beyond just the command name (e.g., the "print" command requires an item#); these should be parsed from the rest of that command line. (Additionally, the "send" command has special I/O requirement; see below.)

      The execute() method must return a boolean indicating whether there were any errors or not, including errors triggered by bad requests from the user. (A return value of true indicates there were errors.)

    • Last, EmailClient must define the public method:

      public static void main(String[] args)

      This is for you to use to test your class, and consist, at a minimum, of an instantiation of a EmailClient object, followed by an invocation of its execute() method. You might want to elaborate on that to do other things, like reading the filename from the console, but that is completely up to you, and will not enhance your grade.

  3. The execute() method is the heart of the control structure of your program. It will iteratively read from the console, and execute, a sequence of commands until the user requests an exit. The commands to be implemented are (along with required arguments):

    • list
      First, outputs a line indicating the name of the folder, and the number of emails the folder contains. Then, outputs a listing of the header information for all the emails in the folder, printing out the item number, "From", "Date", and "Subject" fields, one line per email item. The "Date" field should be edited to save space, as per the sample output. The "From" and "Subject" fields should be padded with spaces, or truncated, as necessary to make each line of the output fit on an 80-character-wide screen, and to make the columns line up neatly. (Actually, the "Subject" field doesn't have to be padded, just truncated if necessary.) A sample output is given at the end of this document.
    • print <item#>
      Prints out the requested email message, first printing out the full header, one field per line, including labels, then printing out the full text of the body of the email.
    • delete <item#>
      Deletes the requested email message
    • send <username@somehost>
      This command composes and delivers a new email, addressed to the user specified as part of the command (e.g., "send park@umbc.edu"). It must first prompt for a "Subject:", and read 1 line. It will then loop continuously reading lines of the message body until the user enters "END" on a line all by itself, to indicate the end of the message. It will then call the appropriate low-level library method to deliver the message. Note that for this project, the delivery doesn't actually send anything.
    • exit
      Terminates the loop, causing execute() to return "false" if all went okay, "true" if there were one or more errors, including any illegal requests by the user (e.g., asking to print out a non-existent message.

    Note that some of the commands take an "<item#>" argument, specifying which message to operate on; messages are numberd from "1", not "0" As they are internally numbered.

  4. Even though this is an OOP class, that does not mean you should ignore good procedural programming principles! You should still write clear, modular code. This means breaking up oversized, monolithic methods into smaller, logically divided submethods, abstracting out common, repeated chunks of logic into private helper methods, giving your variables meaningful names, etc.--you know the drill.
  5. You are free to design other helper classes besides EmailClient, but only if it really makes your code "better", by which I mean "clearer" or "easier to understand" or "more logical". Recall what I said in lecture: performance is definitely a lesser consideration than clarity for any application that we will assign in this course, and even in the real world, it is rare that sacrificing clarity for efficiency is a Good Thing. Also note that trying to show off by making your code unnecessarily clever/complex just annoys and antagonizes the graders.
  6. You are not to use any classes other than those in the standard Java libraries, and those specifically provided by me, without first checking with me.
  7. Important: As with the prime number project, there are probably many implementations on the web of projects similar to this one. It would be best not to refer to those: this project should be simple enough that you don't need outside help at the design level, and you will only risk inadvertantly copying too much of someone else's idea.
Provided Classes and Sample Files
Two utility classes that implement the lower layers of mailbox-handling will be provided for you, as follows:

The MailRepository class:

    No public instance variables.

    Public constructors:

	/**
	* returns an instance with the file param bound as the input mailbox
	MailRepository(String mailboxFile);
	/* 2-arg constructor for the Extra Credit option: */
	MailRepository(String mailboxFile, delayedCommit);

    Public methods:

	int        getNumMessages();
	MailHeader getMessageHeader(int msgIndex);
	String     getMessageBody(int msgIndex);
	boolean    deleteMessage(int msgIndex);
	boolean    sendMessage(MailHeader header, String body);
	String     getMessageStatus(int msgIndex); // For Extra Credit option
	boolean    commitChanges(); // For Extra Credit option


The MailHeader class:

    No public instance variables.

    Public constructors:

	MailHeader();
	MailHeader(String fromAddr, String toAddr, String sentDate,
		   String subject);

    Public methods:

	String getFromAddr();
	String getToAddr();
	String getSentDate();
	String getSubject();
}


We have provided two files for you to download: a JAR file containing the library classes described above, and a sample mail file to use as input to test your program. Download them from these links:

Once you've downloaded these two files, copy theem to the top-level directory for your project in Eclipse. Do no put them down into the src or bin subdirectories. (Unix "directory" == Windows "folder")

Now, you have to configure Eclipse to see and use the library of classes inside the .jar file you just downloaded. Launch Eclipse, go into your Proj2 project, then follow these steps:

  1. Under the "Project" menu, select "Properties...". This will pop up the "Properties for Proj2" dialog box.
  2. Select "Java Build Path" in left-hand pane.
  3. Then, select "Libraries" tab in right-hand pane.
  4. Click "Add External Jars..." button on far-right. This will bring up the "Jar Selection" dialog box. Select the proj2MailUtil.jar file from where you copied it to.
Now, you should be able to successfully run your program.

Two things about the file argument:

  1. The way Eclipse configures your runtime environment, your program will be expecting any filenames to be relative to the top-level directory for your project, so that's why it was recommended above to put the P2TestFolder.in file there.
  2. If you aren't familiar with how to handle runtime command line arguments (in other words, the String[] parameter to main()), you can just hard-code the filename into your main()'s call to the EmailClient constructor; our test harness will obviously use some other name.

Project Hints and Notes
  1. The "send" command is implemented by collecting the necessary information, as outlined above in the description for the command. Then, you should call the full version of the MailHeader() constructor: the one that takes four parameters specifying all of the header fields. Pass this newly-constructed MailHeader object, along with the body of the message (type String), to the sendMessage() method. Note that the header's toAddr field must be non-null and non-empty; the other fields can be null, but only if that is what the user specified.
  2. There is a rudimentary sample EmailClient class definition, here, in EmailClient.java. It is not a real implementation of the class as it should be for Project 2: it is just to demonstrate how you might use the MailRepository and other library classes I am providing.
Sample Output
Command: list "test_mail_folder": 3 messages. 1 jdoe1@umbc.edu Sep 26 22:38 "Hello, how are you?" 2 prince@natlbank.co.ni Sep 27 01:15 "Offer to share unclaimed account" 3 reallylongname@trun... Sep 28 10:15 "I tend to blather on and on and on a"... Command: print 3 From: reallylongname@truncatedsitename.somedomainthatdoesntmatter.com Date: Tue, 28 Sep 2010 10:15:02 -0400 Subject: I used to blather on and on and on and on and on! Hello, there! How are you??? I just did my twenty-third hit of Red Bull, and I don't feel a thing!!??!!??!! Yahhhh!!!!!! Don't remember why I started writing this. Bye! Command: delete 3 Command: list "test_mail_folder": 2 messages. 1 jdoe1@umbc.edu Sep 26 22:38 "Hello, how are you?" 2 prince@natlbank.co.ni Sep 27 01:15 "Offer to share unclaimed account" Commmand: exit

Extra Credit Option
The extra credit option will be to create a version of the mail reader application that allows a person to defer actually deleting a message, to give them a chance to change their minds.

First, you will be using the 2-arg version of the constructor for MailRepository:

	MailRepository(String mailboxFile, delayedCommit);
The delayedCommit argument should be "true" -- this will cause the MailRepository instance to be created in a special, delayed-delete mode.

Now, when you use the MailRepository.deleteMessage() method, it will not actually remove the message from the list; instead, it will be marked for later deletion. You can spot this by using the method:

	String     getMessageStatus(int msgIndex);
This method currently returns one of two values: either the String "D", meaning "Deleted", or the empty String "", which means it is a plain, not-deleted message. You should augment your "LIST" command to add a column displaying a "D" for those messages marked for deletion. You should also print out a "Status:" line in the output of the "PRINT" command, right after the header, that indicates what the status is (just output the String returned by getMessageStatus()).

Next, you should add an "expunge" command that calls invokes the MailRepository method:

	boolean    commitChanges();
This will cause the MailRepository to actually remove the messages marked for deletion, which will cause the remaining messages to be renumbered.

Here's a brief example of how the extra credit version should work:

Command: list "test_mail_folder": 3 messages. 1 jdoe1@umbc.edu Sep 26 22:38 "Hello, how are you?" 2 jdoe1@umbc.edu Sep 26 23:01 "Are you there?!" 3 jdoe1@umbc.edu Sep 26 23:15 "Why don't you answer me?" Command: print 2 From: jdoe1@umbc.edu Date: Sun, 26 Sep 2010 23:01:02 -0400 Subject: Are you there?! Status: I'm tired of waiting for a reply... Command: delete 2 Command: list "test_mail_folder": 3 messages. 1 jdoe1@umbc.edu Sep 26 22:38 "Hello, how are you?" 2D jdoe1@umbc.edu Sep 26 23:01 "Are you there?!" 3 jdoe1@umbc.edu Sep 26 23:15 "Why don't you answer me?" Command: print 2 From: jdoe1@umbc.edu Date: Sun, 26 Sep 2010 23:01:02 -0400 Subject: Are you there?! Status: D I'm tired of waiting for a reply... Command: expunge Command: list "test_mail_folder": 2 messages. 1 jdoe1@umbc.edu Sep 26 22:38 "Hello, how are you?" 2 jdoe1@umbc.edu Sep 26 23:15 "Why don't you answer me?" Commmand: exit

If you will be doing the extra credit, you should download a fresh copy of the .JAR file, from the same link given above. There were some changes.


Grading
The standard grading rules will apply to this project.


Project Submission
Before submitting your project, be sure to compile and test your code on the GL system. See the Project Compiling section of the course projects page for details on how to run and execute your project on GL.

(Note that the above section has been augmented to cover how to get files to the GL filesystem, and how to unpack JAR files to test your program.

To submit your project, type the command

submit cs202 Proj2a MailClient.java <any other .java files you created> See the Project Submission page for detailed instructions.

You may resubmit your files as often as you like, but only the last submittal will be graded and will be used to determine if your project is late. For more information, see the projects page on the course web site.

Do not submit the provided library or test input file--we have those already :-)

More complete documentation for submit and related commands can be found here.

Remember -- if you make any change to your program, no matter how insignificant it may seem, you should recompile and retest your program before submitting it. Even the smallest typo can cause errors and a reduction in your grade.