General Considerations (maximum = 100): 99 Documentation (maximum = 70): 70 O-O Design (maximum = 30): 30 GUI (maximum = 40): 37 Locking (maximum = 80): 44 Data store (maximum = 40): 40 Network server (maximum = 40): 40
Overall I think it's a pretty good score, but I'm annoyed that I got caught by that damn 44/80 locking bug! I had a whole heap of JUnit tests for the locking mechanism too! If not for that then it would been near perfect. Still 90% is a score that I would be "over the moon" with back in my university days, I suppose I'm getting pickier with old age
I'd like to thank you all for your advice and spirited discussion (even when I didn't agree with you ). My advice for anyone taking the assigment now is to: 1. keep participating in this forum! 2. keep the back-end stuff simple, I think my over-complicated data access system masked the locking problem to some extent 3. don't neglect the GUI, documentation, etc as that is where you can make or lose a lot of marks and seems to be more open to interpretation
Next stop for me is most likely SCEA, but it won't be until well into next year before I begin.
The following is a heavily abridged version of my design choices file, so that you can see the design I used. I have omitted justifications - you'll need to think through the situations yourselves to see if the techniques are suitable for your solution! Also I have omitted some trivial things like interpretation of 48hr rule.
Time for that beer, you still up for it Andrew? Raj.
GENERAL CONSIDERATIONS ======================
Additional Materials -------------------- I include the following additional materials that I used to develop this application: - JUnit tests for server and data tiers - Client testing mode functionality, see "User Interface" These are provided on an as-is basis, do not form part of the core assignment solution; the JUnit test suite can take up to approx 30 secs to run as it runs up to 500 threads at a time.
DOCUMENTATION ============= User Guide ---------- The user guide / help guide is an HTML file and an embedded image.
Javadoc ------- In the interests of maintainence I have written Javadoc for public, protected, package level constructs and for private methods.
OBJECT-ORIENTED DESIGN ====================== Overall Design -------------- This is a three tier solution. The client tier is a generic viewer that can be used to display and book data from an arbitrary database. The server tier provides a connection portal and enforces all business logic. The data access tier provides a logical database service and implements this through a physical file on disk.
The business logic server - client connection is structured in an API-like manner. This enables development of new user interfaces directly on top of the existing server/database system. For example, a desired future direction of the company is to move to web-based presentation...
The server - database connection is similarly structured in an API-like manner.
Data Access Architecture ------------------------ Data access is provided by a Factory object pattern. Database table structure is read by a stream based SchemaSource to instantiate a DBSchema object. Physical record access is provided by an implementation of the DataSource interface (currently RandomAccessFileDataSource) that performs file seeks, reads and writes; it uses DBSchema to understand the file's structure. Data executes logical record manipulations, but delegates the physical read/write to DataSource. LockManager implementations provide row-level locking permission for Data to perform each logical access; Data directly waits on locking permission before executing DataSource operations. Finally, the whole subsystem is wrapped behind a DataFactory instance that coordinates creation of all these objects. See the section "Data" for an in-depth discussion.
User Interface -------------- I follow the standard Model-View-Controller pattern for the UI. The UI is separated into a top-level frame and several sub-panels: CriteriaPanel for selecting ways to retrieve records, ResultsPanel for showing matching records and optionally extra information about a selected record, and BookPanel for making a booking on a given record.
I use a seperate DataModel that coordinates and controls access to the models underlying each GUI element such as JTable and JComboBox. This ensures that data is centrally coherent; the inbuilt event mechanisms of each model automatically notify the corresponding GUI elements of updates.
USER INTERFACE ============== Server UI ---------- The server GUI is a relatively simple application. Further issues of the server are discussed in "Server" below.
Client UI --------- The client GUI has been designed for ease of use by adhering to common GUI design guidelines. Normal flow of interaction is designed in the optimal left-to-right, top-to-bottom style (same as a page of text). From top to bottom the major components of the GUI are: 1. Standard menu bar 2. Criteria pane with several tabbed criteria modes: get all and search 3. Results pane (selectable JTable) 4. Information pane that shows auxiliary information 5. Book pane that allows placement of bookings 6. Status bar (not currently used)
The specification noted that the system may be altered or extended in the future, so the GUI specifically makes allowances for additions (8 possible additions listed...).
The requirements do not explicity state that users should be able to cancel an operation that is executing. The user has to wait until the operation completes or fails, and this can be easily handled by executing the operation in the AWT event thread and showing a wait cursor.
Flexible Query Tools -------------------- One of the most striking features of the client UI is the tabbed pane containing query modes. I chose this so that other modes of selecting records could be seamlessly added in the future without disturbing the current UI.
The search tab is particularly useable. The search bar is a single row JTable that contains the possible choices for searchable columns. The search bar columns line up with the results table columns underneath and dynamically resize to match their column widths. Searchability is defined in the server tier, so you can add more searchable fields (e.g. a specific price) simply by changing the array in UrlybirdHotelSchema.searchableFields.
Client Test Mode ---------------- The client (both standalone and network versions) can be invoked in test mode using the -test flag: java -jar runme.jar -test java -jar runme.jar alone -test
LOCKING ======= Lock Management --------------- An interface and 2 implementations are provided for locking records. The LockManager does not need to ensure that a record exists before attempting to lock it; ensuring existence is the responsibility of the calling Data object.
LockManager creation is done via the factory object pattern within DataFactory. DataFactory ensures only one LockManager is created per database file. Different LockManagers are provided for network and standalone modes as discussed below.
Auto-Lock and Manual Lock Methods --------------------------------- Methods in the Data class that specify a row index to access are not auto locked; these are read(), update() and delete(). Methods that do not specify a row index are auto-locked; these are find() and create(). It is the responsibility of manual locking methods to remove any locks they place. Delete() and update() will throw exceptions if the requested record is not locked for the requesting client.
DATA CLASS ========== Data Access Architecture ------------------------ There are three elements to the design: data schema, locking, and record level access. These are provided by the DBSchema class, LockManager interface and Data class respectively. The whole system is governed by a factory object pattern.
DBSchema is used to hold the database structural details (number of fields, field names, offsets, etc). Any data access must consult the Schema instance to determine where to seek to, what to read/write and how many bytes are involved. When a database file is loaded a SchemaSource object builds a DBSchema; the DBSchema is then retained for all Data access in that session.
Every Data object is bound to a corresponding DataSource object upon creation.I use a delegation pattern to encapsulate all physical (file related) activities of data access into DataSource. The same pattern holds for DBSchema and SchemaSource.
DataFactory provides a facade for creating Data instances that are correctly connected to the relevant schemas, locks and sources. Neither the DataFactory, DBSchema or LockManager are singletons. This allows for distinct Data-DBSchema-LockManager object clusters to exist for each database file.
Data objects and threading -------------------------- My solution uses a single instance of Data that can concurrently run from multiple threads. However, the design can also extend (and has been tested) to run as multiple Data instances in seperate threads that simultaneously read and write different sections of the database file.
Creating new rows ----------------- Create(), like the other methods in DBMain, is multithreaded. This poses significant race problems for identifying and locking candidate rows for new data. The search for candidate row and lock if not already locked (lockFirstVacantRow()) is synchronised. This ensures that each create() thread can find and lock a row for its new data uninterrupted by other create() threads. It also allows two or more create() threads to write sequential new rows beyond EOF at the same time without error.
Server Design ------------- The business logic server consists of two main classes that form a natural synergy with the data layer. UrlybirdHotelServer acts as a business logic server for records transactions through DBMain. UrlybirdHotelSchema acts a business logic structure layer that provides a presentation ready version of the database structure in DBSchema. Servable is the RMI interface for UrlybirdHotelServer, and UrlybirdHotelServer delegates client structure requests to UrlybirdHotelSchema.
The Servable interface is fairly generic; the specific business logic is coded into UrlybirdHotelServer and UrlybirdHotelSchema. This means that the same client can be used with the same Servable interface to access another database with different business rules. Each implementation of Servable is registered with the RMI service through a launcher, raising the possibility that other databases/business servers can come online with the same service and the client can connect to either of them equally.
RMI Networking -------------- I chose to use RMI for the networking component due to the programmatic ease that it offers.