wood burning stoves 2.0*
The moose likes Developer Certification (SCJD/OCMJD) and the fly likes B&S 2.2.2 - Network Layer Query Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of The Java EE 7 Tutorial Volume 1 or Volume 2 this week in the Java EE forum
or jQuery UI in Action in the JavaScript forum!
JavaRanch » Java Forums » Certification » Developer Certification (SCJD/OCMJD)
Bookmark "B&S 2.2.2 - Network Layer Query" Watch "B&S 2.2.2 - Network Layer Query" New topic
Author

B&S 2.2.2 - Network Layer Query

Pete Palmer
Ranch Hand

Joined: Oct 21, 2008
Posts: 74
Hi All,

My assignment is the B&S 2.2.2. I am working on the network layer and using Andrews book and the forum to guide me. I am using RMI as, well simply, because I have better chance of appreciating this than sockets !!

Really looking for thumbs up or down of what I have done so far.

Below is my remote interface definition.



Below is a snippet of implementation of the above interface.




I have provided all the methods in the DBAccess interface on the remote interface. I have defined InvalidArgumentException exception which extends RunTimeException.

My questions :-
1) At any time, there will only be one instance of the Data class. Any problems with this approach ?
The other approach would be to define a new remote interface providing a single method to return an instance of the DatabaseRemoteImpl remote object back to client using a factory pattern. I am not sure what the benefit would for this approach. Any suggestions ?

2) Every time a client connects to the server, the Reguster.rebind(); is called to bind the remote interface into the register. Effectively, discarding an existing remote reference with the same name. This will happen for any new clients after the first one. Is there a performance or other negative issues with this behaviour ?

3) I have declared DBAcess excpetions ( RecordNotFoundException, SecurityException ) on the remote interface as well the InvalidArgumentException and RemoteException, is this correct ?

When I throw any of the above exceptions ( RecordNotFoundException, SecurityException, InvalidArgumentException ) from my data layer, I presume they will be propagated back to the client, or do I have to do something extra ?

4) I think I have to only implement Serializable for the RecordNotFoundException, DuplicateKeyException & InvalidArgumentException. Is this correct.

Thank you for your help.

Pete



John Grabowsky
Greenhorn

Joined: Dec 30, 2008
Posts: 26
Hi Pete,

im working on B&S 2.3.1. It helps me a lot to discuss the interfaces. What I write here is not tested, it is just what i consider and like to discuss.
Your Remote Interface exposed to the client is a wrapper around DBMain, seems correct to me. Another possible way is to include in the Remote Interface only things the client can do (book and search in my spec) and let the business logic residing in the server make the calls to the lower level DBMain Interface.

abt 1) It seems difficult to have multiple Data instances, how to synchronize access to the DB file?
abt 3) I see nothing in the spec saying this is not correct. But why an unchecked InvalidArgumentException? It a little bit a catch-all Exception. Alternative: one or more custom checked Exceptions, e.g. RecordAlreadyBookedException. RemoteException is also an Exception for an argument invalid in a special way.
abt 4) Exception and all its subclasses already implement Serializable. Try to throw one in your server and look whether it arrives in the client.


John<br />SCJP 5, 97%
Pete Palmer
Ranch Hand

Joined: Oct 21, 2008
Posts: 74
Hi John,

Another possible way is to include in the Remote Interface only things the client can do

This is true, but I am looking to take the approach that the business logic will reside on the client side and so I only need to publish the Sun provided methods on the remote interface. Are there pros & cons to this approach ?

Any ranchers applied this approach ?


abt 1) It seems difficult to have multiple Data instances, how to synchronize access to the DB file?


If I was to have go with multiple instance, hence apply the Factory Patterm, I would lookto synchronize on a static object when accessing the physical database file, therefore, ensuring only one thread is accessing the file at any one time.

What are the pros and cons for having multiple Data instances or single Data instance on the server ?

why an unchecked InvalidArgumentException
This would allow me to throw InvalidArgumentException from method calls createRecord(), findByCriteria() and not change the interface. I can check for this specific exception on the client side and hence the condition.


abt 4) Exception and all its subclasses already implement Serializable. Try to throw one in your server and look whether it arrives in the client.
I did try this but I just wanted re assuring that this was correct behaviour !.


Any feedback on my question 2) ?

Thank you

Pete


Alecsandru Cocarla
Ranch Hand

Joined: Feb 29, 2008
Posts: 158
1. You don't need multiple Data instances since you have cookies.
2. I don't think it's ok. You should reconsider your design.
3. Your remote interface should throw the same exceptions as the local one (plus RemoteException). I don't understand why you catch exceptions like FileNotFoundException and rethrow them as RemoteExceptions, instead of letting them bubble up as they are. Anyway, RemoteException is, more or less, a RMI framework specific exception, so (in my opinion) you should not create them yourself.


SCJP 1.4 100%
SCJD 99.5%
Pete Palmer
Ranch Hand

Joined: Oct 21, 2008
Posts: 74
Alecsandru,

Thank you for your reply. and have the following comments in response :-

1) I like the idea of a single Data instance on the server side, however this will mean less concurrency ie all client requests will have to go via the one instance but I guess for the assignment this is fine. Has this approach been employed successfully ?

2) Good point. Infact after reviewing what I have done and with reference to Andrew's book, the Register.rebind(); should be only called ONCE when the server has started.

3) I accept your point that I shouldn't wrap FileNotFoundException & IOException inside a RemoteException.

Thank you

Pete
Alecsandru Cocarla
Ranch Hand

Joined: Feb 29, 2008
Posts: 158
What do you mean by "less concurrency"? The same instance can be used by multiple threads. It does not matter how many instances of a class you have, this will not make your application "more concurrent". The methods of a class are loaded only once, every thread calls, in fact, the same method (the same place in memory) no matter how many instances of a class you have. It's just the data to which those methods apply which is different.

In "The Book", you'll see that the DvdDatabase contains a static DvdFileAccess, which means that there's only one DvdFileAccess no matter how many DvdDatabases there are. You could also follow the same approach, create as many Data instances as you want, but only one (singleton, if you like) file access instance.
Pete Palmer
Ranch Hand

Joined: Oct 21, 2008
Posts: 74
Alecsandru,

Thank you for the response on the "less concurrency" subject ... I will have to re read the section in the book.

Pete.
John Grabowsky
Greenhorn

Joined: Dec 30, 2008
Posts: 26
Hi Pete,

This is true, but I am looking to take the approach that the business logic will reside on the client side and so I only need to publish the Sun provided methods on the remote interface. Are there pros & cons to this approach ?

Both approaches seem appropriate. I tend to thin-client (biz logic on the server side) since the DBMain Interface is pretty restricted and i want to have the Inteface for the server-client communication at my disposal.
What are the pros and cons for having multiple Data instances or single Data instance on the server ?

If you have multiple instances and synchronize the underlying file access you have two layers of locking -- this and the locking required in the interface. But thats a design decision.
Pete Palmer
Ranch Hand

Joined: Oct 21, 2008
Posts: 74
John,


DBMain Interface is pretty restricted and i want to have the Inteface for the server-client communication at my disposal.

I think this is definitely valid if the the assignment had specified that future extensiblity would need to be taken into consideration.
As this is choice and I just wanted some pointers that I should consider when deciding on the thin or thick client approach.

If you have multiple instances and synchronize the underlying file access you have two layers of locking -- this and the locking required in the interface.


Would you be able elabarote on the two layers of locking concept please. ?


Pete
John Grabowsky
Greenhorn

Joined: Dec 30, 2008
Posts: 26
Hi Pete,

my spec says that the Data class implements the DBMain-Interface and provide lock/unlock methods and an update method and only clients holding the lock are able to update a record. If I have only one Data instance, this guarantees that the DBFile is not changed concurrently. If you have multiple Data instances, you must lock for the access of the DBFile, as you explained above:
If I was to have go with multiple instance, hence apply the Factory Patterm, I would lookto synchronize on a static object when accessing the physical database file, therefore, ensuring only one thread is accessing the file at any one time.

This are the two layers I mean. You cannot omit the first one, since it is required by the spec.
Alecsandru Cocarla
Ranch Hand

Joined: Feb 29, 2008
Posts: 158
You have to synchronize your access to the database file anyway, since otherwise the RandomAccessFile's methods will be called by multiple threads and make a mess out of your database.
It think that, on the contrary, you could avoid the second level of locking if you create a RandomAcessFile for every thread. You just have to synchronize or somehow avoid creating records at the same recNo. The concurrent update and delete are avoided by the (first level) lock manager.
John Grabowsky
Greenhorn

Joined: Dec 30, 2008
Posts: 26
Hi Alecsandru,

you are right, me must sync access to the database file anyway. But multiple instances of the Data class seem still difficult, e.g. you have to coordinate the locks requested via the lock method (spec'd in the DBMain Interface) on different Data instances but the same record. Can one open multiple RandomAcessFiles on the same pyhsical DB-file?
Pete Palmer
Ranch Hand

Joined: Oct 21, 2008
Posts: 74
Gents,
Taking what you have said the steps for an "update" would be

1) lock record to update - first level of lock
2) lock access to the physical database file, so that only thread is accessing it - second level of lock
3) update record
4) release lock 2)
5) release lock 1)


Is that about right ?


Thanks

Pete
Alecsandru Cocarla
Ranch Hand

Joined: Feb 29, 2008
Posts: 158
John Grabowsky wrote:Can one open multiple RandomAcessFiles on the same pyhsical DB-file?

Why not? It's possible, but I also did not choose to do so. To me it seems like creating a new RandomAccessFile for each thread (which might get you rid of the second level of lock) would even be slower than keeping the synchronized access with only one RandomAccessFile. So I wouldn't advise using this approach anyway, since it seems more complex and slower.

John Grabowsky wrote:you have to coordinate the locks requested via the lock method

You do that anyway. If a record is locked by a thread, you still can't update/delete it from another thread, no matter how many RandomAccessFiles you have.

... Now I see that my idea of getting rid of the second level of locking is not really good, since reading a record while another thread is updating should be synchronized (otherwise the record might be in an inconsistent state). Also access to the end of the file is troublesome if another thread is just creating a new record there. These problems can be solved, but it's too much work and too complex, so it's probably easier and better to just sync any access to the data file (be it with one RandomAccessFile or more).

Pete Palmer wrote:Taking what you have said the steps for an "update" would be
1) lock record to update - first level of lock
2) lock access to the physical database file, so that only thread is accessing it - second level of lock
3) update record
4) release lock 2)
5) release lock 1)

Is that about right ?

Yes, I think you got it right.
I think somebody talked (here or on some other thread) about synchronizing the entire update/delete/read methods for the second level of locking, but it is not really necessary. You just have to synchronize the "randomAccessFile.seek(offset); randomAccessFile.write(bytes);" blocks and it's enough. Don't forget that randomAccessFile.length() is also non-thread-safe, so you should also sync when calling this method.
Pete Palmer
Ranch Hand

Joined: Oct 21, 2008
Posts: 74

Thanks for the confirmation.

Don't forget that randomAccessFile.length()


Good reminder ... otherwise would have been missed.

Pete
Pete Palmer
Ranch Hand

Joined: Oct 21, 2008
Posts: 74
Just been looking at the usage of randomAccessFile.length() and it is used in two places :-




I think somebody talked (here or on some other thread) about synchronizing the entire update/delete/read methods for the second level of locking


After reading the length and before the read/write operation to the database file, there is a window when another thread could sneak in and modify the database file and effectively make the database length value previous read invalid. Is this not a case to make the methods synchronized ?


Alecsandru Cocarla
Ranch Hand

Joined: Feb 29, 2008
Posts: 158
It depends on what you are synchronizing on when you do read/write. In my case, I'm synchronizing on the RandomAccessFile instance, so I can't synchronize methods, I have to use synchronized(database) {} blocks.

I don't know what you're using that getAllRecords() for. If it's absolutely necessary that you get all records (for example you're initializing the cache or something), than you should synchronize the whole stuff together.

The second case: You're probably using that for creating new records? Again, you should synchronize the whole block. Or, maybe, if you use a cache, or always know how many records (valid and deleted) you have at a certain moment, you should not call length() at all in this case, because you know that the new record should be at (N * RECORD_LENGTH), or at (lastValidOffset + RECORD_LENGTH), or something similar.

You should consider delaying the length() call until exactly before doing the actual write/read (if possible). Something like this:



But the main reason I said you should synchronize calls to length() is because it can modify the pointer in file. It internally calls a seek(), or something similar (I think it's a native method). For example, two threads: one calls length(), and one just started writing something to the file: the second thread can end up writing in a completely different place because of length().

Don't you also have to call length() when verifying that you should throw a RecordNotFoundException or not? (don't you check that the record's offset is not greater than the database length)?
Pete Palmer
Ranch Hand

Joined: Oct 21, 2008
Posts: 74
Alecsandru,

When I get round to my GUI, it will invoke getAllRecords() to return all the records stored in the database on startup. It will also initialise a Map of "recNo" to "location of record in file". As you suggested, I will need to synchronize this block of code around the RandomAccessFile instance to ensure that the contents of the file don't get changed. Similarly with the length() when writing to the file, I will look to bringing this inside the sychronized block.

Don't you also have to call length() when verifying that you should throw a RecordNotFoundException or not?

The Map that get initialised by the getAllRecords() is used to determine if there is a record assoicated with the specificed recNo, if not I throw RecordNotFoundException. The other time I throw this exception is when I get EOF exception when reading a record. Note When I attempt to read a record, it is either one I have successfully created or read previously and therefore has a valid entry in the Map. Therefore, when I encounter EOF exception it is not expected and therefore, I throw a RecordNotFoundException.

At the moment, I am using length() during getAllRecords() and when creating a new record.

Pete





 
It is sorta covered in the JavaRanch Style Guide.
 
subject: B&S 2.2.2 - Network Layer Query