1. String versus Transfer Object
Created Contractor.java to use as a DTO to put an O-O framework around
the String arrays that represent the record data. This makes it easier
for the UI client to interact with individual fields of data.
2. Schema information hard-coded versus dynamically read
Decided to hard-code the schema information in Contractor.java
rather than read it each time from file as it is ulikely that this
information will change. Although not part of Contractor data, I
included values like the magic cookie and the offset to the first record
because they are also tied to the database, and unlikely to change.
3. Data singleton versus instance for each client
Decided to make the Data.java class a singleton assuming that
at any moment, only one program will need to access the database and
only one instance of Data.java will be required.
Decided to make the RandomAccessFile an instance variable
to avoid multiple RandomAccessFile references from pointing to the same
file. This instance is also used for synchronising, so only one thread
may use the RAF to access the file at any given time.
5. Logical record numbers
Decided to assign logical record numbers to each record in the database
starting with zero as a record number.
6. Facade for Data.java
Decided to use the facade design pattern which allows single
responsiblilty classes to be designed which should be easier to
understand for the junior programmer. The Data.java facade uses
DataAccessManager.java which is responsible for data accessing and the
LockManager.java which is responsible for the locking of records. The
facade also means the implementation details of the database accessing
and the locking are hidden from the user of Data.java which allows
these classes to be changed without affecting the user of Data.java
7. Validate magic cookie only versus validate all schema
Decided to validate more than just the magic cookie as I felt that the
additonal offset validation, record length validation and field length
validations are needed to ensure that no corruption takes place of any
incorrectly provided databases. Should the validation succeed then it
is safe to use the DTO to access and append data to the database in a
format that is supported by any existing application that shares the
database as instructed.
8. Adapter pattern
Decided to use the Adapter pattern to adapt how the lock method
operates so that it can check if the record exists by using the
existing read method of the Data class. This allows for code to be
kept to the minimum as instructed as the alternative is to code new
classes/methods for these operations.
9. Reuse deleted records while creating a record versus none reuse
Decided not to reuse deleted records when creating new records and
decided to append each newly created record to the end of the database
to simplify the implementation of a record creation. This allows for
the code to be kept to minimal as required.
10. Locking API
Decided to use LockManager.java to implement the lock and unlock
methods. The class uses the thread id as the lock cookie value that is
returned when locking the record, and then uses that value when
unlocking the record. As all locking and unlocking calls are made from
within the context of a single method call, it guarantees they occur
within a single thread of execution so they avoid the possibility of a
deadlock. Any users of the locking api must invoke lock/process/unlock
as a sequence in a single method to ensure no deadlock occurs.
Decided against using a sort for the JTable. It is not required and
autoCreateRowSorter method will not work for the data in the database.
To implement a custom sort could make the code more complicated for the
junior programmer to understand.
3. Hardcoded GUI text versus localised text.
Decided to provide gui all text from a ListResourceBundle. This is to
provide a framework for localization in the future as it is currently
not fully implemented as it is not required. The current
ListResourceBundle only provides english text.
Created list resource bundles to i18n format around error messages.
This is because error messages are logged providing advanced users the
ability to understand the cause. i18n however is not fully implemented
and no thought is given to the Local or language when instantiating the
ReourceBundles as I decided that english should be enough for
the scale of the project. All error messages are dynamically created
to avoid unecessary instantiation of uneeded ResourceBundle or String
objects as they are only needed if and when an error occurs.
Decided to use logically assigned record numbers as primary keys
of the records which is incremented for each new record as no
record number generating algorithm is specified. As these assigned
record numbers will always be unique, there is no need to to throw
3. Wrap low level exceptions in DatabaseException
Decided to wrap all low level exceptions like FileNotFoundException in
a DatabaseException during initialization of the database and
implemented RecordNotFoundException, DuplicateKeyException and
SecurityException as subclasses of DatabaseException.java . This allows
users to define their own database level exception and code them as
subclasses of DatabaseException so that they may be caught as well.
4. Made DatabasException as subclass of IOException versus Exception
Decided that the benefit of making DatabaseException a subclass of
IOException was a good idea initially for the scale of the application
and would of allowed only a single exception to be caught by the UI but
changed it to a subclass of Exception as this may be confusing for the
Decided to wrap IOException in unchecked SystemException as it cannot
be thrown directly in Data because the interface provided does not
throw IOException. The SystemException is unwrapped and is thrown back
as an IOException by the server implementation so that the UI client
catches the Exceptions as IOExceptions and deals with them as
IOExceptions. This also makes the code easier for the junior programmer
to understand as the IOExceptions are easier to understand.
6. Throw RecordNotFoundException in unlock() versus not throw
Decided not to throw RecordNotFoundException in the unlock method
because lock method already throws RecordNotFoundException. There is no
point in throwing it after the record has been deleted.
Decided to throw IllegalArgumentException in the following cases:
a: When the length of a field is greater than the maximum
length as per schema.
b: When the number of fields is less than the number of fields
per record as per schema.
c: When the argument value is null.
8. BusinessException and AlreadyBookedException
Decided to create BusinessException.java and made
AlreadyBookedException a subclass of BusinessException. This is because
a record can only be booked if the owner id field is blank and this is
considered a business requirement. This also allows user to define
other business level exceptions as subclasses of BusinessException so
that they will be caught as well by one BusinessException.
Decided to wrap all exceptions coming from the server level in a
GuiControllerException to allow the the UI client to catch one
GuiControllerException and show the message associated with it without
worrying about the cause.
1. RMI versus Sockets
Decided to use RMI for the network communication as it is easy to
implement and understand for the junior programmer as it allows for
java objects to be treated as objects over the network.
2. 2 tier versus 3 tier
Decided to use a 3 tier approach with a Services interface providing
the book and find methods for the network client so that only business
methods are exposed to the client and the implementation of the
business layer book method in ServicesImpl.java calls the data layer
update method of the DB to update the owner id in the database.
3. Expose locking API versus not expose
Decided not to expose the locking API to the GUI client so that the
locking is handled by the server itself. By calling the lock and unlock
methods of DB in a single thread in the server, there is no problem of
thread reuse during RMI so that the thread id can be used to identify
the client of the locking API.
4. Empty RemoteServices interface
Decided to make the RemoteServices interface for RMI connectivity
extend Services interface so that it does not have to declare any new
methods and to be used only for remote reference and kept empty.
RemoteServicesImpl extends UnicastRemoteObject and implements
RemoteServices so can be exported to the RMI registry. It delegates all
work to ServicesImpl singleton which is the implementation of services.
These functionalities were not provided as they are not part of the requirement
but can be added in the future:
1. Allow the GUI client to un-book a record.
2. Allow the GUI client to search, filter and sort on all the fields in the
3. Allow the GUI client to switch to another database via a menu.
4. Allow the GUI client to make a database selection as default so that they
are not asked to give the database location every time at startup.
5. Allow the GUI client to specify the schema of the database to be connected
which is currently hard coded in the application.
6. Internationalizatio which is not fully implemented yet with english being
the only currently supported language by the application.
7. Allow case-insensitive searching, currently done is case-sensitive.
SCJP 6.0, SCJD (400/400), SCBCD for JEE 5, SCWCD 1.4 I do videos for development at
I am probably the only developer ever to have had an orange sized brain tumor in my brain while learning development!!