First of all,
I would like to thank you ranchers
for all the insightful advices and tireless help!
I couldn't do this without you guys.
here's the summary:
General Con: 85/100
The spec defines a limited number of exceptions that each method can throw, and I went around this by wrapping all other exceptions with runtime exception objects, so that I can deal with them in service layer (and output proper messages to the user), without having to propagate them through higher layers. I've never been satisfied with this idea, since it complicates the code, and I'm thinking that's where I lost points. But this won't be an issue in real life, since I can always fix the methods to throw more types of exceptions.
Documentation: 65/70
-5 for documentation? I couldn't care less.
OOD: 30/30
I guess you just have to make sure each class doesn't serve clearly separable objectives. For instance, I initially had a fat and nasty client gui class, which was eventually devided into several classes according to their purpose, such as the one dealing with the properties file, file chooser, table, etc.
GUI: 28/40
I'm confident about my gui. I wonder why so many points were deducted here. :/
Locking: 80/80
I tried something risky here, so I'd been a bit worried about this section. My urlybird is the version with no cookie files. Most people seem to check the lock owner by checking the Data instance, but I didn't check the owner at all, because it would prevent me from having a simple code that I had in mind, and severely damage the efficiency. (But I clearly documented that any operation should be done in lock-op-unlock sequence.).
My locking mechanism is quite simple. It consists of a <Integer,Integer>map holding <recNo,lockingCode>. lockingCode is a Integer value holding -1(write lock), or a positive number(# of read locks).
If <0,-1> is in the map, that means a
thread is holding a writelock. All other threads have to wait until the writelock is released.
If <0,2> is in the map, there are 2 threads holding a readlock. If there is a thread wishing to get a read lock, the lockingCode is simply incremented. If there are threads trying to obtain a write lock, they have to wait till all the read locks are released, meaning the lockingCode becomes 0, and the pair is removed from the map.
When a readlock is released, the lockingCode is decremented by 1. when the lockingCode becomes 0, the pair is removed from the map.
When a writelock is released, the lockingCode is incremented by 1. thus the lockingCode becomes 0, and the pair is removed from the map.
This approach is simple and allows multiple threads to read the same record. It works fine, as long as all operations are done in lock-op-unlock sequence.(I actually have different locking methods for read, write and create). The only problem is that if different threads consistantly read the same record(thus the lockingCode stays positive), a thread wishing to write or delete the record may never get the chance! I thought about this after I turned in the assignment, so I didn't get to fix the problem.
Data Store: 40/40
Network Server: 40/40
I used rmi. Each end user is assigned an rmi object, which has its own Data object as a member variable.
Total: 368/400
It's quite rejuvenating for to see the score after months of waiting..
(I first submitted the package early June, then again in early July,
and in mid July for the last time. My jar file was corrupted, and I didn't
realize that, since it worked fine with my unzip utility..
)
Oh, I spent about 5hrs a day, 6 days a week, for 5 weeks, if anyone cares to know.
Thanks and good luck to you all!
Jon Park
[ August 08, 2008: Message edited by: Jon Park ]