Mail System Design: External Mail Interface and Repository Functionality, Study notes of Software Engineering

The design of the external mail interface component and repository functionality in a mail system. The external mail interface is implemented using a single class responsible for choosing the correct account and accesses. The repository is primarily implemented by classes that implement other components, with the key piece being the abstract persistentobject class. The document also covers updating other repositories and the use of filters and mailmessage objects. Additionally, the importance of maintainable code, isolation, and debugging support is emphasized.

Typology: Study notes

Pre 2010

Uploaded on 08/30/2009

koofers-user-bfx-2
koofers-user-bfx-2 🇺🇸

10 documents

1 / 64

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Software Engineering
Course Notes
Fall 2004
Craig A. Damon
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27
pf28
pf29
pf2a
pf2b
pf2c
pf2d
pf2e
pf2f
pf30
pf31
pf32
pf33
pf34
pf35
pf36
pf37
pf38
pf39
pf3a
pf3b
pf3c
pf3d
pf3e
pf3f
pf40

Partial preview of the text

Download Mail System Design: External Mail Interface and Repository Functionality and more Study notes Software Engineering in PDF only on Docsity!

Software Engineering

Course Notes

Fall 2004

Craig A. Damon

copyright 2001-2004 Craig A. Damon. All rights reserved.

164 Brattleboro Mail Reader Code Design

posed for the first release; the first release could be made without the IMapRdrWtr class. On the other hand, future releases could easily include additional mail interface classes.

Mail Items

Although mail items are not one of the components in the system design, messages and folders are key parts in the system. Figure 45 illustrates the sub-hierarchy that implement these key pieces.

All mail items are (indirect) instances of the abstract class MailItem. The two concrete sub-classes of MailItem are MailMessage and MailFolder. The primary responsibil- ity for each message is to maintain information across many fields, but they have few significant behaviors. Folders primary responsibility is to hold other items (via the con- tains association). Any item can be held in a folder. Folders remember all folders in which they are contained (via the parent association). These two associations are inverses when the contained item is itself a folder.

EMI

acct1 acct2 acct

FA

repository

filters

network

UI

change log

change prop.

Figure 43:The system design for brattleboro

165

SearchFolder is the one sub-class of MailFolder and implements searches. This may seem counter-intuitive at first, but the result of a search is just a collection of mail items, presumably messages. The constructor for a search defines the search criteria and actually finds the group of messages to include.

Figure 44:The implementation of the external mail interface (EMI).

MailInterface

AcctMgr

Pop3Reader IMapRdrWtr SMTPSender

accessors

1..*

MailReader open read close

MailSender open send close

acctname password ip addr/name

read

send

Figure 45:The sub-hierarchy implementing the key mail items

MailItem

MailFolder MailMessage name

contains

1..* sort

from to subject date cc attachments SearchFolder searchCrit

searches1..*

1..*

parent 1..*

update

167

PersistentReferenceis another key supporting class for the repository. Each persis- tent object has a persistent reference once it has been saved. A persistent reference for a given object remains the same instance (meaning == works) during a single run of brattleboro, even if the associated object is removed from working memory. The persis- tent reference is responsible for encoding several characteristics of the persistent object (as it is saved) and restoring these characteristics when it is retrieved. These character- istics include the class of the object and the location in which it is stored within the repos- itory. The class itself is stored as a string in a global (to a repository) class table. By storing the class as a string, the class can be found again in a future invocation of brattle- boro. The location information stored in the reference is simply a key into a common stor- age table. This indirection both allows the object to be stored in a new location (if its size happens to change) and allows a larger repository to be accessed. (For simplicity and performance considerations, this decision may be revisited as coding progresses.) Finally, each persistent reference will return an int (32 bit) that can be used to reconsti- tute the object when needed.

PersistentReferenceitself is an abstract class defining this behavior and providing selected commonly used behaviors. Each subclass implements the int reference in a dif- ferent manner. The low order 4 bits of the int indicates the form of reference being used. The static factory method PersistentReference.restoreReferencecreates an instance of the appropriate subclass given an int reference. To initially create persistent references of the appropriate class, two static factory methods are provided, creat- eReference and createIntReference. One unusual case comes from intRef, which directly embeds any int values that can be expressed using a signed 31-bit number. The reference embeds this, along with a low order 1, to store the int directly. Therefore, all odd-numbered references reflect int values, which can be obtained by doing a one bit signed shift left. For traditional objects, the remaining 28 bits is used to hold the informa- tion about the storage location of the object. Once stored in the repository, the class is indicated as part of the stored information. The special case of an int ref of 0 indicates a null reference. Figure 47 shows these examples.

The next significant part of the repository is the abstract class PersistentPage and its subclasses. A persistent page is a clustering of information as it is actually stored on disk. The PersistentPage class knows how to store itself onto disk, including encryp-

Figure 47: Several common forms of int references

28-bit info 0000

0 (28 TIMES) 0000

31-bit 2’s complement int value 1

standard object reference

null object reference

small int value reference

168 Brattleboro Mail Reader Code Design

tion, if enabled. Each persistent page has a unique page id and holds information about where it is stored on disk. To prevent corruption, all pages are written to new locations on the disk and a 2-phase commit protocol is used to guarantee a consistent version. (Obvi- ously, unmodified pages that have been saved previously are not written back to disk and therefore stays in the same location.) Once the page is written to its new location and update is completely written to disk, the previous location of the page becomes free space.

The most common subclass of PersistentPage is PersistentIntPage, which stores its data as a sequence of 32-bit ints. Each page represents a collection of objects, stored as 32-bits. The first int is a header, which includes a code for the class (the index into the global class table) and the number of ints (not including the header) used to store this object. An array of ints of the indicated length (excluding the header int) is given as the constructor argument to build a Memento. This instance of Memento is the constructor argument, passed to the appropriate constructor of the class indicated in the header to finally reconstruct the object.

The exception to this mechanism comes with instances of String, Date, Collection and Map. Strings are stored in one of two special kinds of persistent pages, instances of PersistentShortStringPageand PersistentLongStringPage. Persistent- ShortStringPageshold all short strings (tentatively determined as length <= 32). Only one copy of each short string is stored, with each persistent reference referring to the same storage. Creating a new short string therefore requires searching the existing pages for the desired string, adding it if is not found. Long strings are stored individually in PersistentLongStringPages. These are just added to the page with appropriate space. Because long strings are not shared, they can be freed when the object that refer- ences it is deleted. If the string is long enough (tentatively greater than 8K), it will be stored in page to itself, allowing more efficient usage. PersistentLongStringPage will eventually implement some form of compression.

Dates are stored as pairs of ints. The first int represents the low order bits and the sec- ond int represents the high order bits of a long. This long is used as the argument to the constructor for Date to reconstitute the object when needed. Collections are stored as sequences of 32-bit int references. Similarly, maps are stored as sequences of key, value pairs of 32-bit int references.

When a persistent object is stored, it stores int references for each persistent object it references. In general, when the object is resurrected, all these objects are also resur- rected. To prevent the entire repository from being re-instantiated at once, two excep- tions are made to this practice. First, message body strings and full header strings are maintained as persistent references, assuming that they represent the bulk of the actual storage space. These strings are resurrected into memory only as needed and are flushed when no longer in use. The second mechanism for terminating the resurrection chain is the class PersistentCollection. Although this class is a subclass of Per- sistentObject, the name refers to its elements. When resurrected, a PersistentC- ollection only maintains PersistentReferencesto its elements. Instances of this class are used in two places: messages within a MailFolder and parents of a Mai- lItem, further limiting the active memory usage.

170 Brattleboro Mail Reader Code Design

  • the kind of control to be used (such as button, pulldown, list of icons, etc.)
  • the location, given as an area (such as header, east, north, body, table, etc.) and a position relative to some border or other action.
  • the name or icon to be used
  • the action to be performed on a click and a double click
  • the value to be displayed, if any

The action itself is an instance of a subclass of the abstract class Action, which will expand to include all the necessary behaviors. Initial actions will include all the default button behaviors (save, print, etc.), sorting on a column and opening a new window.

The values are described as instances of MailField. These instances can describe any publicly accessible property (defined by a get Property method) on MailItem or one of its subclasses. These values are also used in sort procedures, filters and queries.

Each UIAction instance can return an instance of JComponent that implements the action described. The method layoutActions, defined and implemented on Mai- lItem, takes an instance of JFrame and adds the indicated controls to it in the appro- priate locations. This method is a template method making use of refinable methods such as addTableColumnand addHeaderComponentto perform the actual layout. The UIOptions class guarantees that no UIOption will be returned that refers to an instance of UIOption not yet returned, thus significantly simplifying the layout problem in these methods.

Viewing Folders

Each folder that is displayed is shown in a FolderWindow, a subclass of MailItem- Window. FolderWindow adds supports for addTableColumn.

Figure 49: The interface structure for messages.

MailItem

MailItemWindow

MsgRdrWindow MsgWrtrWindow

UIOptions

displays

options

FolderWindow

171

Viewing Messages

Each incoming message that is displayed is shown in a MsgRdrWindow, a subclass of MailItemWindow. MsgRdrWindow adds the methods reply and forward, which each create a new MsgWrtrWindow.

Composing Messages

Each unset message originating with this user is shown in a MsgWrtrWindow, a sub- class of MailItemWindow. MsgWrtrWindow adds numerous default behaviors to the body area, supporting the editing of the text.

Filtering Messages

As indicated in the system design, the automated filtering is accomplished by a sequence of indirect instances of the abstract class Filter. Upon receipt of a new incoming message, the MailInterface calls the primary method provided on Filter.filter on the first filter, which takes a MailMessage and a set of MailFolder as its arguments. Each filter examines the message and adds or removes folders from the set. Each filter method then recursively calls filter on the next method.The filter method returns the set of folders back to the MailInterface instance, which adds the message to each folder in the set.

In the initial implementation, the only subclass of Filter implemented is ExplicitFil- ter. An explicit filter holds three items: a set of folders to which messages should be added, a set of folders from which messages should be removed and a query condition. If a message satisfies the query condition, it is added and removed from the indicated sets. Otherwise, the filter does nothing.

Updating Other Repositories

The final piece of functionality described in the system design is the ability to send changes to other repositories. The basic approach is to encode each change as part of an update message sent as textual mail to the user’s mail accounts. The message will be held on the server for the standard duration of time. In the initial implementation, each repository, other than the originating repository, will be required to download the mail containing the change request before it is deleted from the server. In future releases, a repository will be allowed to request an update since a certain time and the first up-to- date repository receiving the request will generate a larger update message.

The messages will have a special subject indicating that it is a change message and should be handled separately. Upon recognizing this indicator, the MailInterface instance pulls the incoming message out of the normal stream. Instead, it uses the decode factory method on ChangeEntry to create new instances of ChangeEntry. These changes are then applied to the local repository.

Class Definitions

This section summarizes the classes that make up brattleboro.

AcctMgr

AcctMgr is an abstract class whose concrete sub-classes provides the basic interface to external mail servers. AcctMgr is a sub-class of PersistentObject.

173

DelegateOptions

DelegateOptionsis a sub-class of UIOptions. The basic behavior is to extend or oth- erwise modify an instance (direct or indirect) of UIOptions.

The class has a private field that references this base instance. The class provides two methods: hides, which indicates what options in the base should be masked and adds, which adds options to the those found in the base options.

ExplicitAddressEntry

ExplicitAddressEntryis a sub-class of AddressEntry that describes the e-mail address for a single named individual.

It remembers the associated e-mail address and implements the inherited abstract method addresses. It also supports alternative addressees to be saved, using the add- Alternative and removeAlternativemethods. An alternative address may become the preferred address with the makePreferred method. Each take a string containing the e-mail address.

ExplicitFilter

This concrete sub-class of Filter provides an explicit way of controlling where incom- ing messages are filed.

ExplicitFilter retains two new fields: condition, which holds a QueryCondition that controls firing of this filter, and actions, which holds a list of Actions that deter- mines what happens if this filter is fired.

Filter

This abstract sub-class of PersistentObject describes automatic actions that should be performed on newly arrived mail.

Filter defines a single abstract method, filter, which performs the filtering action implemented by this filter.

FolderWindow

This class implements the user interface representation of a folder. It is associated with a single MailFolder instance that it displays. It is a sub-class of the MailItemWindow.

The behaviors of FolderWindow are implemented through event handlers. The behaviors to be supported are indicated by a set of UIOptions retained by the MailFolder itself.

IMapRdrWtr

This concrete subclass of AcctMgr implements both the MailReader and MailSender interfaces. IMapRdrWtr provides read and send access to IMap mail servers.

It provides no new behaviors, but implements the behaviors required by its super- classes and the interfaces that it implements.

ImplicitFilter

ImplicitFiltercaptures understanding of user actions that modify the repository. It is a sub-class of PersistentObject. This class is not well understood yet.

The class provides a new static method, learnAction, that is given each change entry as it is entered into the ChangeLog.

174 Brattleboro Mail Reader Code Design

IndirectAddressEntry

IndirectAddressEntryis a subclass of AddressEntry. It provides an entry in the address book that does not directly describe an e-mail address. Instead, it refers to one or more other address book entries. Any mail addressed to this entry will use the address from each related entry.

It remembers a collection of other address entries and implements the inherited abstract method addresses.

LayoutPosition

LayoutPosition is an abstract subclass of PersistentObject. It provides a mecha- nism to describe the positioning used in laying out actions in a MailItemWindow.

Initial concrete subclasses will provide the ability to layout oriented towards the center or any edge (similar to BorderLayout, but with the ability to add multiple components to an area) and the ability to place the component below, above or to the left or right of another component already added.

MailField

MailField is a subclass of PersistentObject. It describes a single field in a mail message or folder, as stored in a mail folder.

MailField is responsible for indicating the name of the field (with getName()), returning a string representation of the value for the field (with getStringValue(MailItem)) and allowing two messages to be compared for sort purposes (using compare( MailItem,MailItem)).

MailFolder

MailFolder is one of the key classes in brattleboro. It is a subclass of MailItem. It is a named entity with a primary responsibility to contain mail items.

Each folder also knows what parent folder(s) within which it is contained. Every folder, except the top level folder named “All Mail”, has one or more parents. MailFolder pro- vides a sort method that takes two arrays. The first contains MailFields to be sorted on and the second is an array of bytes containing -1 or 1, indicating descending or ascending sort respectively. Each folder also maintains a collection of UI options that indicate how the folder should be displayed.

MailInterface

MailInterface is one of the most complicated classes in brattleboro. It is not a persis- tent object, being a direct subclass of Object. It is created at the start of each brattleboro session, initialized with the current set of mail accounts.

MailInterface has three major responsibilities:

  • Request each mail account to check for new mail at the appropriate times, either when starting up, when explicitly requested by the user or at the interval indicated by the account itself.
  • Translate incoming text mail messages into MailMessage objects and pass them onto the filters for sorting and filing. Similarly, MailInterface trans- lates outgoing MailMessage objects into text mail messages and invokes the appropriate mail account to send the message.

176 Brattleboro Mail Reader Code Design

  • isHtml — required boolean indicating whether this is an html message
  • headers — String including the original full headers of the message. Only on incoming messages.
  • from — a single e-mail address, required
  • to — one or more e-mail addresses, required in all but messages being composed
  • date — time message is sent, required in all but messages being com- posed
  • subject — simple text, optional
  • originatingAccount— the e-mail account from which this message was originally received, required for all messages sent to this user
  • priority — a five level rating from lowest to highest, required
  • wasRead — required boolean
  • wasSent — required boolean
  • forwardedAs — set of messages constructed from this message, required but possibly empty. For this field, the get accessor returns an iterator and the only modifier is an add method.
  • replies — set of messages sent back to the original sender, required but possibly empty
  • attachments — possibly empty set of files sent along with the message

MailMessage defines the method covertToText, which converts a message to plain text format for sending via e-mail. MailMessage defines the static factory method con- vertFromText, which creates a mail message from an incoming textual e-mail mes- sage.

MailMessage also defines the getComposer method which returns a MsgWtrWindow for this message (valid only if the message was local and not yet sent), the static factory method compose, which returns a MsgWtrWindow for a new message and the methods forwardMessage and reply, which return instances of MsgWtrWindow for forwarded and replied versions of this message, respectively.

MailReader

MailReader is the interface that defines the ability to read mail from an external e-mail server. It defines three operations: open, read and close, which perform the obvious actions.

MailSender

MailSender is the complement to MailReader. It is the interface that defines the abil- ity to send mail to an external e-mail server. It defines three operations: open, send and close, which perform the obvious actions.

Memento

Memento is a fundamental part of the repository save and restore behavior. Every sub- class of PersistentObjectmust implement a constructor that take a Memento instance as its only argument and a saveState method that creates a Memento from the instance. To support access to the memento, two sets operations plus one construc- tor is defined. One set of operations adds values to the memento with methods such as putObject, putInt, putDate, putCollection, putString, etc. The second set

177

grants access to previously created mementos, providing the functions getObject, getInt, getDate, getCollection, getString, etc. Each of the put methods takes an int tag and a value of the appropriate type. Each get method takes just an int tag and returns a value of the appropriate type. The public constructor takes a PersistentObject instance.

To work with the repository, the memento also provides a protected fill method to fill a subarray of ints with data representing the memento and a requiredSize method indi- cating how many slots in an array are needed. Memento also defines a protected con- structor that has an int subarray, indicated as an int array and an upper and lower bound within that array, as its arguments.

MsgRdrWindow

MsgRdrWindow is a subclass of MailItemWindow. A MsgRdrWindow is created using the display factory method on the underlying instance of MailMessage.

The constructor calls setBody using the body of the message. This can be overridden by an appropriate entry in the associated UIOptions.

MsgWtrWindow

MsgWtrWindow is a subclass of MailItemWindow. A MsgWtrWindow is created using either the static factory method MailMessage.compose or one of the methods respond or forward on MailMessage.

The constructor calls setBody using the body of the message. This can be overridden by an appropriate entry in the associated UIOptions.

MulipleAddressEntry

MultipleAddressEntryis a concrete subclass of AddressEntry.

It references multiple other instances of AddressEntry, giving the functionality of a mail- ing group. In addition to the inherited addresses method, it also provides addAddress and removeAddress methods.

PersistentCollection

PersistentCollectionis a subclass of PersistentObjectthat implements the Collection interface. It holds weak references to its elements, allowing them to be gar- bage collected. If necessary, the Iterator yielded by iterator will reconstitute the elements before yielding them.

PersistentIntPage

PersistentIntPageis the subclass of PersistentPage that holds most stored objects. Each object is stored as a sequence of 32-bit ints. The first int for each object is the object header, which describes the class (using the index into the global table), the number of ints required for this object and the object number within this page.

This page provides the operation getObject(int objectNumber), which returns the reconstituted object. To save a new persistent object, the operation putObject(Class cl,Memento m)is used, returning an object number (a 32-bit int). To replace an existing persistent object already stored in this page, the operation replaceObject(int objectNumber,Memento m)is used, which has no return value. Finally, the method deleteObject(int objectNumber)removes an object from the page.

179

PersistentShortStringPage

PersistentShortStringPageis the subclass of PersistentPage that holds short strings (tentatively <= 32 characters). The short strings are inherently shared. Unlike other pages, these strings have no header info. The object number for this page encodes the length and offset directly. The unusual method for this page is hasString(String s), which returns an object number if the page contains that string and the object num- ber 0 if it does not. Other methods include addString(String)and get- String(int). No delete method is offered.

Pop3Reader

Pop3Reader is the subclass of AcctMgr that can read mail from a pop3 mail server. It implements the MailReader interface. It offers no functionality beyond that defined by AcctMgr and MailReader.

QueryCombination

QueryCombinationis a subclass of a QueryCondition. It combines two conditions with an and or an or. It provides no new functionality.

QueryCondition

QueryCondition is an abstract subclass of PersistentObject. It provides the basic ability to ask whether a MailItem satisfies some condition. It provides 3 methods: sat- isfies(MailItem), satisfiesAll(Collection)and satisfiesAny(Collec- tion), Each of these methods return a boolean indicating whether the instance or collection satisfies the query.

QueryElement

QueryElement is an abstract subclass of PersistentObject. It describes a single value used in a query term. The only new method provided is getValue(MailItem), which returns an object reflecting the indicated value.

QueryTerm

QueryTerm is a subclass of QueryCondition. It describes a single predicate or com- parison applied to one or more instances of QueryElement.

QueryValue

QueryValue is a subclass of QueryElement. It holds a specific object for later use.

QueryVariable

QueryVariable is a subclass of QueryElement. It extracts a value from an instance of a MailItem using an instance of MailField.

Repository

Repository is one of the key classes in brattleboro, although it relies on other classes to do most of the work. It is a subclass of Object, and is therefore not persistent itself, although it provides persistence to other objects. It is a singleton class (at this time at least), with only one Repository open at a time. The methods include open(String filename), commit() and close().

The repository also performs all actual disk i/o for the persistent pages and maintains the global class index table.

180 Brattleboro Mail Reader Code Design

SearchFolder

SearchFolder is a subclass of MailFolder. The contents are defined by the execution of a query, rather than explicitly storing elements. To cover this requirement, the search folder will hold a query condition and a source, which is another MailFolder. The initial implementation will re-execute the query when the folder is opened and upon request. Future implementations may choose to add listeners to specific folders waiting for mail items to be added, modified or removed.

SMTPSender

SMTPSender is a subclass of AcctMgr that implements MailSender. It adds no func- tionality beyond this, but implements the ability to send mail to an SMTP mail system.

SpellingDictionary

To be defined. Missing in the initial implementation.

UIAction

UIAction is a subclass of PersistentObject. It provides the description of behaviors that should be supported somewhere, somehow in a MailItemWindow. For each action, it includes an Action, a name and/or icon, a LayoutPosition and an indication of what form of control should be used. The details of this class are still undecided.

UIOptions

UIOptions is a subclass of PersistentObject. It maintains a collection of UIActions.

WindowLayout

WindowLayout is a subclass of Object and implements LayoutManager and LayoutManager2. The add method taking a component and a constraint is used, sup- porting the constraint in the form of a LayoutPosition instance.