aspose file tools*
The moose likes Developer Certification (SCJD/OCMJD) and the fly likes Design of data access layer - please comment on Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Certification » Developer Certification (SCJD/OCMJD)
Bookmark "Design of data access layer - please comment on" Watch "Design of data access layer - please comment on" New topic
Author

Design of data access layer - please comment on

John Canavan
Greenhorn

Joined: Aug 17, 2003
Posts: 29
I would be really grateful if I could get some feedback from you on the data access layer design of my project (Bodgitt and Scarper). I have tried to keep the implementation as simple as possible and do not want to complicate things.
The interface DBMain supplied was as follows:
public String [] read(int recNo) throws RecordNotFoundException;
public void update(int recNo, String [] data) throws RecordNotFoundException;
public void delete(int recNo) throws RecordNotFoundException;
public int [] find(String [] criteria) throws RecordNotFoundException;
public int create(String [] data) throws DuplicateKeyException;
public void lock(int recNo) throws RecordNotFoundException;
public void unlock(int recNo) throws RecordNotFoundException;
public boolean isLocked(int recNo) throws RecordNotFoundException;
1. In Data.java, that implements DBMain, I have synchronized all the methods except the last three (locking ones).
2. I would see each client as getting it’s own Data object from the networking layer.
3. The locking mechanism is similar to the on in Max’s book – static Vector reservedRecs in Data.java.
4. I use RandomAccessFile (no caching). The randomAccessFile is private to Data.java.
5. I assume the key is a combination of location and name therefore in the update method I never update these two fields (therefore DuplicateKeyException can never be thrown – consistent with the interface).
6. In the create method I call the find function to see if the key has been used already. If the key is already used I read the record number and check the flag to see if it is marked as deleted. If deleted I set the flag to valid and update the rest of the record (the specification document states this is okay), if not deleted (i.e. the record is valid) the exception is thrown. If the key is not already there (i.e. the find returns an empty array) I just write out the record.
7. I have two other classes written and they are as follows: DataSchema.java – this verifies that the magicCookie is correct, stores the field names, the field lengths and where the data section starts. Obviously the reading here is done only once (when Data.java is created) and the information is stored accordingly. The second class is DataHelper class with the following two static methods:
public static String readFixedString(RandomAccessFile raf, int size) throws IOException
public static void writeFixedString(RandomAccessFile raf, short size, String str) throws IOException

I’m sure there are many issues with my work as I don’t have a much Java programming experience and this is only a start. A few (of the many) queries I have are.
1. Point 4 – would this be the correct way to do this i.e. each Data object has its own RandomAccessFile? I am not sure about the writing and reading to these files – will the reads and writes for a record be atomic? Anytime I want to write or read a record I call the relevant method in DataHelper – is this okay? I see some people are using filechannels – are these better (I have never used them and have limited experience even in file I/O)?
2. Are the mechanisms in points 5 and 6 okay?
I would really appreciate any help and advice you can give me.
Thanks,
John
Bharat Ruparel
Ranch Hand

Joined: Jul 30, 2003
Posts: 493
Hello John,
Though I have URLy Bird 1.3.1, your DBMain interface looks similar to mine. Instead of repeating your statements inside quotes in my response and since you have taken trouble to lay-out your design in numbered steps, I will reference the point numbers. Here we go:
1. That is what I have done. the last three methods will also need synchronization, but at the object level which is currently the static vector in your design.
2. Quite correct.
3. Well, that is how I started out and it is a good start, but you need to track a locking client's unique identity plus the record number being locked combination. The following is the reference to a rather long thread started by Davidd Smith in which Andrew and Max walked me through the entire design. Study it well and you should have your answer:
NX: About data consistent
http://www.coderanch.com/t/183941/java-developer-SCJD/certification/NX-data-consistent
4. That is fine. That is what I am doing too. Others are caching their data, using NIO channels etc. It is up to you how far to you want to go.
5. I don't think that you can make such an assumption. I do allow for updating name and location in my design, but I agree with you in that if the key is not clearly definable then DuplicateKeyException in the update method does not make much sense. Therefore, I don't declare it or catch it in the Update method either.
6. I am not sure about that. What I have done in my create method is to wrap both RecordNotFound and IOException in the DuplicateKeyException and rethrow it. It may not be correct. Let others respond to this point. I might learn something!
7. Looks good!
You wrote:

1. Point 4 � would this be the correct way to do this i.e. each Data object has its own RandomAccessFile? I am not sure about the writing and reading to these files � will the reads and writes for a record be atomic? Anytime I want to write or read a record I call the relevant method in DataHelper � is this okay? I see some people are using filechannels � are these better (I have never used them and have limited experience even in file I/O)?
2. Are the mechanisms in points 5 and 6 okay?

It is not that each Data object has its own RandomAccessFile but rather each Data object has its own HANDLE to the same RandomAccessFile - two entirely different things. Since each Data object reads, writes, updates, and deletes the same physical file, you are synchronizing on your mutator methods given in the DBMain interface. Does it make sense?
I am not using FileChannels so somebody else has to help you out there.
Please see my comments above regarding points numbered 5 and 6.
Nice going thus far!
Regards.
Bharat


SCJP,SCJD,SCWCD,SCBCD,SCDJWS,SCEA
Vlad Rabkin
Ranch Hand

Joined: Jul 07, 2003
Posts: 555
Hi John,
I will comment only one point, which I think is dangerous:
1. Point 4 – would this be the correct way to do this i.e. each Data object has its own RandomAccessFile? I am not sure about the writing and reading to these files – will the reads and writes for a record be atomic?

I wouldn't do in case your Data is not a singleton. Lets take an example:
Solaris 5, as a default, allows only 256 open connections to the file.
If at the moment 300 client would connect to the server - your server would crash.
So, there are many other ways to avoid it:
Data is singletone
Data is not singleton, but RandomAccessFile is static member variable of Data.
You open/close FileChannel for read/write. (idea of Max, but I don't like it).

Anytime I want to write or read a record I call the relevant method in DataHelper – is this okay? I see some people are using filechannels – are these better (I have never used them and have limited experience even in file I/O)?

- NIO is nice idea. It offers: PARTIAL (!!!) thread-safety(read/write atomicy), IOInterruptedException and much better performance.
Best,
Vlad
John Canavan
Greenhorn

Joined: Aug 17, 2003
Posts: 29
Hi Bharat and Vlad,
Thanks very much for your comments. I need to re-examine the area pertaining to File I/O and will include a few questions tomorrow based on it, which hopefully you guys will respond to.
Bharat,
Thanks for pointing me to the thread where you discussed the locking mechanism with Andrew, Max etc. I found it really useful – this is one of the areas I have to brush up on. I would just like to ask you a couple of things about the locking mechanism you employed.
1. Is it correct you replaced the static vector with a static WeakHaspMap? I presume this was done because of it usefulness when a client goes down as documented it the thread you sent me to.
2. Just to clarify, does your lock method look something like this:
public void lock (int recNo) throws RecordNotFoundException {
Integer recNumber = new Integer(recNo);
Object client = this;
if (!validRecord(recNo))
throw new RecordNotFoundException(“Invalid record” ;
synchronized(lockedRecs) {
while (lockedRecs.containsValue(recNumber)) {
try {
lockedRecs.wait();
} catch (InterruptedException e) {

}
}
lockedRecs.put(client, recNumber);
lockedRecs.notifyAll();
}
}
3. For your validRecord method you check that the record is in the database and that it is not deleted, but did you make a decision on whether to include it in the unlock method? As mentioned in the thread, if it was included in the unlock method it would cause an exception in a lock – delete – unlock, so did you just omit it from the unlock method?
4. What is the best mechanism for dealing with an exception such as a InterruptedException? Did you just wrap it in a RecordNotFoundException? I’m not sure what to do when such an exception occurs.
5. In relation to the DuplicateKeyException on the update method I think that I will stick to what I said – I looked up many threads on this and it seems some people are doing this as well whereas others are treating it similar to the way you are. I suppose the important thing is to document why such decisions were made and Sun should accept it. Thanks for your opinions on this anyway.
Many thanks,
John
Bharat Ruparel
Ranch Hand

Joined: Jul 30, 2003
Posts: 493
Hello John,
Glad to be of service. OK, here we go again, point by point:
1. Yes I do use WeakHashMap.
2. John, you may want to use Code blocks so that your code retains the formatting. They are used as follows:

Before you get into your while loop within, the synchronization block, you may want to check if the record is already locked by this client? If it is then you have big problems, you should throw a very serious sounding exception, I call it FatalSystemException (more on it later..). Normally, it shouldn't happen, but should it happen and you don't check, it will lead to deadlocks.
Other than that, it looks good to me.
3. Yes I did make a decision to include this check in my unlock method. I am not sure where in the thread is this mentioned that it will cause an exception in the unlock method. I still have it in place. I also check whether the record is locked to begin with before I proceed to unlock it. If it is not locked then I simply return from the method. You may want to point out the specific place where you think this is being mentioned in the thread.
4. Dealing with Exceptions - You are ready for the following thread. Enjoy!
Topic: NX: URLy Bird 1.3.1 Explicit Fatal Exception Handling
http://www.coderanch.com/t/184081/java-developer-SCJD/certification/NX-URLy-Bird-Explicit-Fatal
5. Sounds good! I am not sure either and I had qualified my statement. If you do learn anything more substantial, be sure to bring it to my attention.
Regards.
Bharat
John Canavan
Greenhorn

Joined: Aug 17, 2003
Posts: 29
Hi Bharat,
1. In relation to point 3 in the previous post, surely an exception will be thrown in your unlock method during an unlock - deleteRecord - unlock sequence of events. The validRecord method will return false (in the unlock method) as the record now has a deleted flag! This was mentioned briefly also by Andrew in the thread you pointed me to (NX: About data consistent) on the 31st of August, you also refer to it in your reply on the 1st of September. Am I missing something here?
2.You state above that
Before you get into your while loop within, the synchronization block, you may want to check if the record is already locked by this client?
. After having a very quick look at the thread on exceptions (I mean 'very quick' as it will take me a long time to work through this thread!) I notice that in your lock method you are posted (6th of September) you do

. These are actually two different things - which check are you recommending? Am I wrong in saying that neither of these could (!!) take place if the protocol a client/thread is using follows the lock - update - unlock sequence?

Thanks for your help,
John
Bharat Ruparel
Ranch Hand

Joined: Jul 30, 2003
Posts: 493
Hello John,
I will again refer to your point numbers in my reply:
1. It would help me immensely to zero-in on the problem if you copy the sections of the text that are causing the confusion and state them here within quotes, i.e.,
....stuff goes here

That way there is no room for ambiguity. Still, you were kind enough in giving the dates so here we go: I am guessing that you are referring to my dialog with Andrew regarding lock-delete-unlock sequence right? You are correct in pointing out that the RecordNotFoundException will be thrown everytime this sequence occurs. But as Andrew points out: Sun has asked us to respect the given API, i.e., DBMain interface. If there is a certain sequence of events that will cause such an exception to be thrown then we are justified in pointing that out in our documentation! I think that both Andrew and and I agreed on that. Realize that this will only happen during the delete operation. May be the unlock method should check whether such as sequence, i.e., lock - delete - unlock has occured and then selectively call the validateRecord method based on that. I am not sure. The thing that comes to my mind at this point is to keep an instance variable such as:
1. int deletedRecord in the data class.
2. Initialize it to zero in the constructor for the data class
3. set it to the deleted record's value within the delete method
4. check in the unlock method if the deletedRecord value is equal to the passed record number? if it is then do not call the private validateRecord method but reset the deletedRecord value to zero since you acknowledge that this is the record being unlocked and no RecordNotFoundException should be thrown.
I hope that Andrew is following this conversation since we need his help at this point. I have taken you as far as I can without being on uncertain grounds on this one.
2. I was talking about the following check:

You wrote:

Am I wrong in saying that neither of these could (!!) take place if the protocol a client/thread is using follows the lock - update - unlock sequence?

I don't think that this is the right way to think about it. Implicit in your reasoning above is the understanding that you expect the sequence lock-update-unlock to be atomic! It isn't, it all depends on threads slicing in and out of the method calls as the scheduler decides. To safeguard against such possibilities, we create logical structures such as you are mentioning above. As a matter of fact, I would suggest that you consider the following sequence of operations in your book method:
lock - read - update only if the owner field is still blank - unlock.
Hope this helps.
Bharat
John Canavan
Greenhorn

Joined: Aug 17, 2003
Posts: 29
Hello Bharat,
Thanks for you speedy reply!

It would help me immensely to zero-in on the problem if you copy the sections of the text that are causing the confusion and state them here within quotes

Will do from now on, sorry!

1. int deletedRecord in the data class.
2. Initialize it to zero in the constructor for the data class
3. set it to the deleted record's value within the delete method
4. check in the unlock method if the deletedRecord value is equal to the passed record number? if it is then do not call the private validateRecord method but reset the deletedRecord value to zero since you acknowledge that this is the record being unlocked and no RecordNotFoundException should be thrown.

Yes something like this seems a good work around the problem.

Implicit in your reasoning above is the understanding that you expect the sequence lock-update-unlock to be atomic! It isn't, it all depends on threads slicing in and out of the method calls as the scheduler decides.

Yes, I think I understand this.
I may leave the exception details for a while and post them in a separate thread sometime - as I would like to do a bit more study on that side of things. I would however like to ask you a few things about the randomAccessFile and it’s connection to Data.java.
In your first reply in this thread you stated the following:

It is not that each Data object has its own RandomAccessFile but rather each Data object has its own HANDLE to the same RandomAccessFile - two entirely different things. Since each Data object reads, writes, updates, and deletes the same physical file, you are synchronizing on your mutator methods given in the DBMain interface. Does it make sense?

I have just been testing my data access layer via a simple DOS interface (I have not started the networking or GUI part yet) – through the DOS interface I am basically testing the standalone version making sure it reads, updates, creates records and so far it seems to be ok (obviously I can’t test the threading etc while doing this). I am concerned about my Data.java file as it seems from Vlad’s and your responses that I am using the randomAccessFile incorrectly, but I don’t really understand how to solve it. In my Data.java I have the following constructor and method:

Obviously this code is only to get me up and running (not properly commented, exception stuff not implemented yet, name of db file is hard-coded etc), but the general principles still apply. I have a few questions based on this code and your quote above.
1. As I stated previously, in the networking layer each client would get a copy of the Data object, therefore according to my design (which is still pretty much in the infant stage!).....
a) Each client would read the header information (basically DataSchema stores all the header information e.g. the field names, magic cookie value and I have static methods that I call when I need to get this information in Data.java). Is this the correct way of doing it – or should there only be one read of this information ever e.g. 10 clients connect but the data file header information is only read once?
b) It would appear from your quote above that creating a new randomAccessFile for each client is incorrect (I suppose the answer to the above part may answer this one). From what you are saying it seems that "new RandomAccessFile()" should only be called once – is this to be created at a higher level and then a reference to the same randomAccessFile passed down to the all the Data objects created e.g. it could be passed down in the constructor of the Data object. Where exactly did you create your randomAccessFile? I am confused as to how to implement this – maybe you could help me here.
Maybe I’m being a bit too general or vague here, I’m not sure. Essentially I need to know how to set up the data access layer to incorporate using a randonAccessFile when there are many clients, but I’m confused as to how to implement this.
I really appreciate any help you can give me,
Thanks again,
John
Ulrich Heeger
Ranch Hand

Joined: Jun 06, 2003
Posts: 266
Hi John,
I have similars troubles like you.
Maybe I�m being a bit too general or vague here, I�m not sure. Essentially I need to know how to set up the data access layer to incorporate using a randonAccessFile when there are many clients, but I�m confused as to how to implement this.

My idea is:
1. I want to create a Singleton-instance of DataSchema. While this is created, within its constructor, there is a call to readHeader() which will read all the header informations and store them as private members.So I create a RAF-Instance and will close it before leaving readHeader().
Also the Magiccookie will be checked if it corresponds to the MAGIC_COOKIE value. Further I have getxxx-methods to get the header-informations from outside of DataSchema-Class (Thank you, Bharat )
So this RAF-instance isn't interesting for thread safety, because it will be created and closed within a Singleton-instance and before a Data-Instance will have been created.
Ok, let's see Data-Class. I haven't begin this part, so I have to reduce me to assumptions:
As you and Bharat, I want to have one own Data-Instance for each client. Like Vlad suggested, I will have a static member for a RAF.
Because I want to use a mutex for exclusive writing and reading (Thank you, Vlad ), the threadsafety will also be guaranteed for RAF ( I hope so, I have to test it).
I think there is a flow, when should I close RAF?
Perhaps Vlad or Bharat could help?
Regards & Thanks
Ulrich
Vlad Rabkin
Ranch Hand

Joined: Jul 07, 2003
Posts: 555
Hi Ulrich,
I think there is a flow, when should I close RAF?

At the moment you shut down the server.
Best,
Vlad
Andrew Monkhouse
author and jackaroo
Marshal Commander

Joined: Mar 28, 2003
Posts: 11503
    
  95

Hi John,
You mentioned that most of Data's public methods are synchronized, but later you said that you have one instance of Data per connected client. So the synchronization does nothing for you, and causes a performce hit.
Regarding the "one RAF per Data instance" concept - as Vlad mentioned, this is not a good idea. I think you are still looking for ideas on this - if you did come up with a solution, feel free to ignore this idea. You could have another class which does all the real reading and writing from the data file. There would only be one instance of this class for all the instances of the Data class. You could then synchronize on that helper classes methods, to ensure that the seek() and the subsequent read()/write() methods were handled atomically.
Regarding the "lock-delete-unlock" sequence throwing an exception. Your methods do not require you to throw an exception if the user calls unlock() on a record that is not locked. Therefore you could unlock the record as the last step in your deletion. The call to unlock would therefore do nothing.
Regards, Andrew


The Sun Certified Java Developer Exam with J2SE 5: paper version from Amazon, PDF from Apress, Online reference: Books 24x7 Personal blog
Bharat Ruparel
Ranch Hand

Joined: Jul 30, 2003
Posts: 493
Hello John, Ulrich, Vlad & Andrew,
Back here with some answers which I am sure will raise more questions and if I am wrong, Andrew will bail me out.
John (and Ulrich),
you wrote:

Where exactly did you create your randomAccessFile? I am confused as to how to implement this � maybe you could help me here.

What I have done for the RAF is that I open it based on suncertify.properties parameters (see the thread I started on this topic) within the DataSchema class. Since my DataSchema class is a singleton, I am referring to the same instance variable when I call it from the Data class. The trick is to call DataSchema class before you call say getRAF method on this instance. In other words, let DataSchema class be instantiated fully by calling something like: dbSchema = DataSchema.getInstance(); in the Data class constructor. Now when you call getRAF() method on the DataSchema instance, e.g., dbSchema.getRAF(), you will receive the same handle on the RAF file for ALL Data instances.
Andrew,
You wrote:

Regarding the "lock-delete-unlock" sequence throwing an exception. Your methods do not require you to throw an exception if the user calls unlock() on a record that is not locked. Therefore you could unlock the record as the last step in your deletion. The call to unlock would therefore do nothing.

I am confused, I am sure that John and Ulrich are too. We were discussing it in the following thread:
About data consistent
http://www.coderanch.com/t/183941/java-developer-SCJD/certification/NX-data-consistent
Here is what you wrote in that thread:

Personally I agree that unlock() should not throw RecordNotFoundException. Since the lock had been granted, there should be no reason for the unlock not to work. And it will cause an exception to be thrown every time if you use the lock-delete-unlock sequence (does this mean that delete does an implicit unlock? or you just document this issue?). But my feelings are irelevant - Sun have told us what they want.

Am I missing something?
Regards.
Bharat
Andrew Monkhouse
author and jackaroo
Marshal Commander

Joined: Mar 28, 2003
Posts: 11503
    
  95

Hi Bharat,
Sorry - probably just me that is confused. Or asleep at the switch. Or both.
To me, throwing an exception for an expected and required sequence is just plain wrong. So my automatic reaction is to try and avoid it. I keep forgetting about that RecordNotFoundException. Since it exists in the interface, I guess Sun consider that throwing it is the right thing to do.
I go back to my position in the other thread: throw the RecordNotFoundException if someone tries to unlock a record they deleted.
Regards, Andrew
Bharat Ruparel
Ranch Hand

Joined: Jul 30, 2003
Posts: 493
Thanks Andrew for clarifying.
Regards.
Bharat
John Canavan
Greenhorn

Joined: Aug 17, 2003
Posts: 29
Hi there,
Thanks for the feedback, I think I’m gradually getting my head around this! I spent sometime today going through some old threads, especially ones pertaining to ‘singleton’ – I’m afraid I did not know what was meant by a singleton, but I have a good idea now!
I relation to the design therefore the following is my understanding of what others (including Bharat and Ukrich) did. I realise there are many ways this can be achieved, but is this similar to the approach you guys took?
1. The DataSchema class is a singleton, therefore only one instance is ever created. The instance created did the initial reading of the header information (such as verifying the magic cookie, getting the lengths of each record, retrieving the field names etc). The static getInstance() method will create the DataSchema object (if not already created) or if it is created it will return a reference to the created object. Is this (and the code below) correct? Does the static getInstance() method have to be synchronized? I noticed in one of the threads it was.

2. The randomAccessFile is private to the DataSchema (along with information such as record lengths, field names etc). Only one object of this type created therefore only one randomAccessFile is ever created.
3. Data class holds a reference to the DataSchema instance e.g. something like..


The handle for the randomAccessFile can be got through dataSchema.getRandomAccessFile() and other header information can also be got similarly (eg dataSchema.getRecordLength()).
4. This may seem like a silly question but here goes anyway. The clients are running on different JVMs to each other and each client will receive it’s own Data.java object. However the Data objects are created on the server side (I intend to use RMI, but only know a little about it – I intend going through the tutorial on the sun website), therefore only one copy of DataSchema (the singleton) is created on the server. I presume this is correct, is it? Really what I’m asking here if each client is running on a separate JVM and each client gets its own Data object then, in fact, a DataSchema object is not created on each client as all the Data objects received by the client are created on the server!
5. Another question which I have is as follows. I made all the public methods in my Data class synchronized e.g.

This is similar to what Max did in his book. Is this incorrect? Andrew seems to think it is:

You mentioned that most of Data's public methods are synchronized, but later you said that you have one instance of Data per connected client. So the synchronization does nothing for you, and causes a performce hit.

Can you give me some advice on this?
6. Also, in relation to the lock – delete – unlock sequence, it is correct that Andrew seems to be suggesting that it is okay to create an exception here. I suppose it is correct if you take Sun’s specification literally:

Any methods that throw RecordNotFoundException should do so if a specified record does not exist or is marked as deleted in the database file.

It isn’t an issue really with the project as the GUI does not allow you to deleted a record. Therefore it would probably just need to be documented in the choices.txt document.
I look forward to your replies (I think I would have given up at this stage if it was not for the help I was getting here!).
John
[ September 26, 2003: Message edited by: John Canavan ]
Ulrich Heeger
Ranch Hand

Joined: Jun 06, 2003
Posts: 266
Hi Bharat, hi Andrew,
my unlock-method in the interface has following signature:

These are the little nuances in the assignment
Greetings & Regards
Ulrich
Ulrich Heeger
Ranch Hand

Joined: Jun 06, 2003
Posts: 266
Hi John,
I will have a look on your questions tomorrow and will post my thoughts.
Greetings
Ulrich
Tony Collins
Ranch Hand

Joined: Jul 03, 2003
Posts: 435
John it's a bit late now but I think if your MetaData is a static instance that is created before any access's to the Database are made you'll be ok.You may also want to cache your data at this point.
Remember a static member is effectively a singelton across a class, not sure if thats the correct speak but ...
Tony
Bharat Ruparel
Ranch Hand

Joined: Jul 30, 2003
Posts: 493
Hello John,
Sorry couldn't respond sooner. Here we go again point-by-point:
1. Do a search on Singleton on google. Tons of useful examples. Singleton is one of the most basic design patterns that can be used. I am sure Sun's site also has examples. Anyway, you have to synchronize your getInstance method. The reason is, the method starts to execute, you don't want another thread (which respects the lock acquired on the "this" object, i.e., the current instance of DataSchema) to slice in and mess the things up.
2. That is how I understand it.
3. That is how I am have it.
4. Fair question. A bit of RMI knowledge will clear up lots of doubts for you. You may want to go through the Sun's tutorial on RMI anyway. May be just skim through it to get the fundmental concepts right and then dive into the details when you are ready for it? When we say that each client gets its own copy of the Data object in an RMI environment, it doesn't mean that the Data object instance is physically shipped over the wire to the client, rather, the client is given a means to communicate with its dedicated instance of the Data instance which still very much lives on the server. This is achieved using stubs and skeletons. These are essentially "proxies" for the remote object and for all practical purposes allow the client to interact with the remote object as though it was available to it in its own JVM locally! The locational transperancy is one of the biggest benifits of RMI. If you have a bit of EJB background, that is how EJBs operate too. Now if Data instances are created on the server, then the DataSchema object also is instantiated on the server. Does this make sense? If it doesn't, stop, go through the tutorial until you get a basic grasp of how the RMI works and then pick up where you left-off. In my opinion, merely finishing the assignment should not be your overriding goals. Spend some time going through the tutorial, you will be surprised how good is it. After that, you will be marching much more confidently.
5. I would defer this point to Andrew, since I don't understand it myself. I have my public methods synchronized except lock, unlock, and islocked.
6. You wrote:

It isn’t an issue really with the project as the GUI does not allow you to deleted a record. Therefore it would probably just need to be documented in the choices.txt document.

That is my plan too! The GUI only has to provide two basic functions in my case: 1. Book the hotel room and 2. Find rooms matching the criteria. Therefore, I don't have to deal with the lock-delete-unlock (throws RecordNotFoundException for sure) case, but just document it for the grader.
I hope you are not offended when I say "stop and read-up". A few times when I was trying to race ahead without really understanding what I was doing, Andrew did that for me, and I followed his advice reading up in the library until I had a good enough knowledge base to tackle the issue more effectively. Remember, above all, this is a learning experience!
Regards.
Bharat
Arun Kumar
Ranch Hand

Joined: Aug 29, 2003
Posts: 67
I dont get this. Whats the advantage/disadvantage of having a unique data instance for each client, against single data instance.

In the unique data instance case, each instance is waiting for the RandomAccessFile object which is static in Data class. So the read and write to the file can be atomic. (those who are not using the static RAF object, as andrew suggested, you might be using a single DataHelper class instance which is common for all data instances).
IN the case of single isntance (i know in RMI this instance can be handed to multiple threads) you end up synchronizing all the read/write methods and the lock/unlock is synchronized on the weakhashmap.
So in both cases, only one client gets the rights to read/write the db file.
[Vlad: You open/close FileChannel for read/write. (idea of Max, but I don't like it).]
Vlad can you pls explain this option?
Arun
[ September 26, 2003: Message edited by: Arun Kumar ]

SCJP (1.4), SCWCD, SCJD
Andrew Monkhouse
author and jackaroo
Marshal Commander

Joined: Mar 28, 2003
Posts: 11503
    
  95

Hi Arun,
Whats the advantage/disadvantage of having a unique data instance for each client, against single data instance.

If you look at John's signature for the lock method in the first post, you will see that it does not return a cookie. Therefore some other method is required to uniquely identify the client to ensure that only the client who locks the record can modify or unlock it. The easy way to do this is by having a unique data instance per client, in which case the instance of the data class acts as the unique identifier.
[Vlad: You open/close FileChannel for read/write. (idea of Max, but I don't like it).]
Vlad can you pls explain this option?

Max suggested opening a RAF/FileChannel before each database operation and then closing it again afterwards. This should allow more concurrent clients to be connected than the maximum number of open files allowed by the operating system. However there are other issues with doing this.
Regards, Andrew
Ulrich Heeger
Ranch Hand

Joined: Jun 06, 2003
Posts: 266
Hi everyone,

the second parameter "rw" is ok or it's better to use "rws"?

Hi Bharat,
you wrote:

Now when you call getRAF() method on the DataSchema instance, e.g., dbSchema.getRAF(), you will receive the same handle on the RAF file for ALL Data instances.

Does it mean the one and only RAF-File will be closed when the Server shuts down, like Vlad suggested?
Greetings & Regards
Ulrich
[ September 27, 2003: Message edited by: Ulrich Heeger ]
Ulrich Heeger
Ranch Hand

Joined: Jun 06, 2003
Posts: 266
Hi John, Bharat, Andrew,
I'm a little bit confused about the discussion concerning the Exception which might be thrown from the unlock-method.

Due to my signature I can't throw RecordNotFoundException.
The signatures of my lock-method and the update-method:


I can do following assumptions: Because the lock-method returns a lockCookie which is an unique identifier for the Record, the RNF-Exception doesn't need to be thrown from the unlock-method. But why it is thrown from the update-method?
Or perhaps it implies following assumption: the unlock-method should be called within the update-method.
But do you think about that?
Thanks & Regards
Ulrich
[ September 27, 2003: Message edited by: Ulrich Heeger ]
Ulrich Heeger
Ranch Hand

Joined: Jun 06, 2003
Posts: 266
Hi Bharat, hi John,
Another question which I have is as follows. I made all the public methods in my Data class synchronized e.g.

Concerning this point I would refer to NX: Thread Safety
Greetings
Ulrich
John Canavan
Greenhorn

Joined: Aug 17, 2003
Posts: 29
Thanks everyone for all your help throughout this thread.
Hello Ulrich,
It seems that your method does not have to throw a RecordNotFoundException, so I probably would not worry about it. In the interface I have a RecordNotFoundException can be thrown from this method. At the start of the unlock method we check to see if the record is valid (it is not as we have just deleted it!!), therefore we throw this exception. It seems strange, but we are just complying with what the specification requires! Is that okay?
Hello Bharat,
Your help has really been appreciated. I must say you responses and comments, not just in this thread but any of the others I have read, have been both comprehensive and understandable which is what beginners like myself need!

I hope you are not offended when I say "stop and read-up".

Absolutely not! You’re right, I realise that I have a lot of work (including much reading) to do. Although I studied Java as part of my Computer Science degree, my experience is relatively limited. Threading (apart from the programmer exam), Networking along with other areas such as Design Patterns are totally new to me. I have done a small bit of GUI and exception handling so I have a lot to learn there as well.

Anyway, you have to synchronize your getInstance method

I understand now why I must make the getInstance() method synchronized.
Just a quick question….
I have a DataHelper class which has the two static methods below included (this approach was advocated in a few threads – I think you may have used this approach as well). The readFixedString method is called from the singleton DataSchema class and the Data class. The writeFixedString method is called from the Data class. My question is, should these methods be syschronized?

Regards,
John
Bharat Ruparel
Ranch Hand

Joined: Jul 30, 2003
Posts: 493
Hello Ulrich,
You asked:

the second parameter "rw" is ok or it's better to use "rws"?

That is what I have used. I want my RandomAccessFile updates to occur immediately in a synchronous mode.
You asked:

Does it mean the one and only RAF-File will be closed when the Server shuts down, like Vlad suggested?

I believe so. My file has never been corrupted in extensive testing. It would if I wasn't receiving an exclusive and only handle to it.
Hello John,

I have a DataHelper class which has the two static methods below included (this approach was advocated in a few threads � I think you may have used this approach as well). The readFixedString method is called from the singleton DataSchema class and the Data class. The writeFixedString method is called from the Data class. My question is, should these methods be syschronized?

I have the same methods in the DataHelper class and I synchronize them.
Hope this helps.
Regards.
Bharat
[ September 28, 2003: Message edited by: Bharat Ruparel ]
Ulrich Heeger
Ranch Hand

Joined: Jun 06, 2003
Posts: 266
Hi Bharat,
Thanks for your help
Regards
Ulrich
John Canavan
Greenhorn

Joined: Aug 17, 2003
Posts: 29

Hi Bharat,
Thanks for your help

I'd second that!
Thanks for your help,
John
Vlad Rabkin
Ranch Hand

Joined: Jul 07, 2003
Posts: 555
Hi,
Sorry for writing so late.
1) one question addressed to by was answered by Andrew (open/close FileChannel for every operation). Andrew, thanx!
2) Regarding unlock and RecordNotFoundException. My delete method unlock the record automatically (it doesn't call Data.unlock(), it deletes lock directly from clooection). It means that the client should perform following : lock-delete (instead of lock-delete-unlck). This way avoid throwing RecordNotFoundException while trying to unlock a deleted record.
Of, course I will document this.
Best,
Vlad
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Design of data access layer - please comment on