I wanted my remote implementation of the lock method first call the "local" lock method of Data to do some validation and then call the lock manager's lock method to do the actual locking. If the "local" lock method of the Data class would throw a DatabaseException (the reason I started this thread), I could also easily let the lockmanager's lock method throw a DatabaseException in which I wrap the InterruptedException the same way you do. In this way only DatabaseExceptions will be exposed to the clients and I think that is a consistent implementation.
I am vary curious why you choose to throw the RecordNotFoundException in the catch of the InterruptedException. Why didn't you leave it blank or throw a RuntimeException for example ? (What I said this should be really the subject of another thread )
I think there are three main options:
1. Leaving the catch empty, so if an interrupt occurs, you are ignoring it and stil trying to catch the lock.
2. Throwing a RuntimeException, not supporting any interrupts
(What about Thread.currentThread.interrupt() ??), or
3. supporting interrupts by wrapping the exception and throwing some kind of new exception to the client. In a next iteration of the project we can
support interruption in this way.
Is there anybody that can provide me with pro's and con's ?
Does your client still invoke the lock/unlock methods of whatever interface it uses to access the services of the database?
I provided the implementation for locking and locking within the empty methods provided by Sun.
I'm with Damian.
By an assertion do you mean the keyword assert like in the following statement (just asking to assure I interpret your english in the right way):
How did you assure that only the client that has locked a record may unlock the record ?
Originally posted by Damian Ryan:
Just to clarify matters: I agree completely with Max when he describes his preference for the three-tier architecture he describes. This is in fact how I implemented my solution.
It seems we just disagree about whether or not it's acceptable to alter the signature of the methods provided by Sun. I respect Max's (and Andrew's) opinion that one should not alter the signatures. I just don't feel as strongly about it.
I would argue as follows (and all I can do is reiterate that the examiner who marked my assignment didn't mark me down for it): we know that the code we are provided is flawed. It makes deprecated method calls, it contains logical flaws, it isn't thread safe, some of the class names are obtuse (DataInfo for a record? Oh, puh-lease).
I chose to address the shortcomings in the Data class by modifying it rather than subclassing it, as others may have chosen to do. [One of my justifications, among others, for this is that the deprecated method calls took place in private methods that couldn't therefore be overridden in a subclass. Not without the cardinal sin of cut/paste editing anyway].
Under these circumstances, altering the signature of a single method doesn't really seem like a big deal to me. I cannot see any justification for insisting that the lock(int) method should throw an IOException. None. It doesn't make any implementation or business sense to me, and I have argued as much (v.s.)
So instead of declaring that the lock(int) method throws an exception that in my implementation could never arise, I chose to have it throw a DatabaseException that signalled when the database had been unable to comply with the lock request (which could happen, for instance, if the record number was bad).
I really think that if a candidate can provide a reasoned argument for their design choice that isn't just plain wrong (and I humbly suggest that mine isn't) then they should be on firm ground. After all, isn't that a great part of what the SCJD is testing - the candidate's ability to take consistent, justifiable decisions in the face of subomptimal constraints, when there is usually no black and white, right or wrong answer?
Ok, I promise to shut up now
[ August 21, 2003: Message edited by: Damian Ryan ]
Originally posted by Damian Ryan:
thanks for your reply. I'll make a couple of final points and then leave this alone. I promise!
public void lock(int record)
public void unlock(int record)
Record locking must be implemented using the methods public void lock(int) and public void unlock(int). These methods are required to allow concurrent use of the database when booking flights from multiple clients. Note that the locking required is effectively a "write" lock only. If updates occur to a record that is being displayed on another client, then no change occurs at the other client. However, if two clients attempt to perform the sequence lock, read, modify, write, unlock concurrently, then both modification attempts will be handled correctly. The aim is to ensure that if two customers attempt to book a seat on the same flight concurrently, then the number of available seats is definitely reduced by two, unless there was only one seat, in which case, one customer will be advised at booking time that no more seats are available.
The lock method should block until the requested lock can be applied. The integer argument indicates the record to be locked. If the method is called with an argument of -1, the entire database should be locked.
The unlock method simply removes the lock from the specified record. If an attempt is made to unlock a record that has not been locked by this connection, then no action is be taken.
A clear design, such as will be readily understood by junior programmers, will be preferred to a complex one
coding standards and readability (23)
clarity and Maintainability maintainability of the design and implementation (12)
I don't like the idea of changing the signature of the lock and unlock method to add an extra parameter like the clientId.
Then you could think of supplying a Data instance for every client that connects and using this as client identification. In this case we have to modify the Data class to such an extent that we break another restriction imposed by Sun that the Data class is fully functional (except for lock, unlock and criteriaFind) and that we shouldn't modify it.
Damian, more and more I begin to believe the socket implementation fits all instructions, maybe I should reconsider using RMI.
Originally posted by Damian Ryan:
here's what my version of the instructions said (under the section "Extending suncertify.db.Data"):
positively cries out for the developer to leave the code in a "better" shape than he or she found it, to ease the task of future programmers who must understand the code before they can alter or build on it.
(And one of the ways I felt I could make the code clearer to someone else was to remove a daft IOException from a method that had no need to throw it).
And to reiterate, I felt justified altering the signature because I interpreted the context described in my instructions as stating that I was THE developer tasked with developing 100% of the system, and that therefore alteration of a published interface wasn't going to upset the project, because I was the only consumer of that public interface.
You'll have gathered by now no doubt how I tend to write quite a lot when I'm trying to explain a point and I apologise for the lack of concision in my arguments. I think (well, I hope) I managed to be a bit more concise when I submitted my documentation (with the benefit of a bit more time to edit it ) but I want to make the point that I think it's very important to supply every assumption you've made so that your examiner can use this as a basis to judge your offered justifications (assuming of course, that your assumptions are not considered invalid).
[ August 22, 2003: Message edited by: Damian Ryan ]
anyway, I'm not sure there's a point to my rant, but I hope you haven't decided that the SCJD forum is the borg, intent on sucking out the creative impulse in the future generation of SCJD developers
I didn't use RMI even though the prevailing opinion seems to be that it's just THE way to do it
I just wanted to say to those who have yet to submit their assignments that they should have the courage of their convictions if they believe thay have made sound, justifiable choices, even if these don't always meet with the approval of the great and good here.
I mainly come to do the last one, but sometimes can't resist the temptation to put my oar in
Andrew: I am not convinced that RMI is the way to do it. However I (like many others) approached this assignment as a learning experience, and I have done enough with sockets over the past few years, so I decided to use RMI.
I don't see that using the services of a separate class (the lock manager) has to mean that the lock and unlock methods of Data are left empty. Could you not just have the Data.lock(int) and Data.unlock(int) methods delegate the implementation of the locking mechanism to the lock manager class you have written?
You might be interested in reading No need for a locking manager? - it is very long, and some of the ideas that I suggested in there I am no longer convinced about, but you will see quite a few comments talking about how to track ownership of locks for FBNS.
I think I am on the right way to track the ownership of locks, but that means that my lock and unlock methods of Data are empty. And that means that I am not strictly adhering to the instructions to implement locking in lock and unlock of Data. But as stated above, I don't see any other solution.
Is it possible to have Connection.lock(int) call an overloaded lock method in Data?
You would have to decide for yourself whether this is allowable and whether it will help.
Max believes that I did not meet the requirements
[ August 26, 2003: Message edited by: Andrew Monkhouse ]
Is it true that you store the locks at two places ? One storage (client-nr combination) in the Connection object and one general storage (nr) in the Data object.