*
The moose likes Developer Certification (SCJD/OCMJD) and the fly likes Platform Independent Command Line 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 "Platform Independent Command Line" Watch "Platform Independent Command Line" New topic
Author

Platform Independent Command Line

Abhinav Anand
Ranch Hand

Joined: May 02, 2002
Posts: 113
Hi guys,
I use Windows OS. I use the command lines mentioned below to start my server and client respectively. These command lines are specific to Windows and I believe they won't work on Unix & Macintosh. So can anybody suggest a platform independent way of starting my server and client given the present command lines and class path settings I use.

Server Side
-----------
cd x:\scjd
set classpath=.;
start rmiregistry
java -Djava.security.policy=FBNPolicy suncertify.server.DataServer rmi://127.0.0.1:1099 db sf

Client Side
-----------
cd x:\scjd
set classpath=.;
java suncertify.client.DataClient

Any suggetions are welcome.
Nate Johnson
Ranch Hand

Joined: May 13, 2002
Posts: 301
I created executable jar files for my client and server... then tested on windows, mac, and linux and everything worked exactly the same... I did not use a policy file so I can't say how that works for this situation, but I am sure others have


scwcd, scjd, scjp<br /><a href="http://natejohnson.us" target="_blank" rel="nofollow">http://natejohnson.us</a><br /><a href="http://rice.kuali.org" target="_blank" rel="nofollow">http://rice.kuali.org</a>
Michael Morris
Ranch Hand

Joined: Jan 30, 2002
Posts: 3451
Like Nate says if you jar up the client and server, it all works fine on any OS. I did use a policy file and tested on various Windows platforms as well as Linux.
Michael Morris


Any intelligent fool can make things bigger, more complex, and more violent. It takes a touch of genius - and a lot of courage - to move in the opposite direction. - Ernst F. Schumacher
Mark Spritzler
ranger
Sheriff

Joined: Feb 05, 2001
Posts: 17250
    
    6

Executable Jar it.
Mark


Perfect World Programming, LLC - Two Laptop Bag - Tube Organizer
How to Ask Questions the Smart Way FAQ
Abhinav Anand
Ranch Hand

Joined: May 02, 2002
Posts: 113
Hey guys,
Come on I meant that the command on windows to start RMI Registry is
start RMIRegistry
What is the equivalent command on mac and unix.
Please help.
Thanks in advance
Nate Johnson
Ranch Hand

Joined: May 13, 2002
Posts: 301
Originally posted by Vishal Sinha:

Come on I meant that the command on windows to start RMI Registry is
start RMIRegistry
What is the equivalent command on mac and unix.

There's your problems... start it from the code like this...

and you will be all set no matter where you are
Abhinav Anand
Ranch Hand

Joined: May 02, 2002
Posts: 113
Wow
That's what was bugging my head all these days.
Starting rmi registry without setting class path created troubls like stub and skeleton not found.
By using executable jar files and starting the rmiregistry from within the code we don't have to type out any command to start our server or client. We can simply double click on the jar file and wow the application launches itself. That's both cool and simple.
Now, suppose my server needs some additional parameters like host name, port number, data file names etc. etc. then I suppose that i must display a simple dialog at server startup that prompts the user to input them all. I hope that this would look great and the examiners won't mind it at all. Am i right ?
Any suggetions.
Michael Morris
Ranch Hand

Joined: Jan 30, 2002
Posts: 3451
Hi Vishal,
It's up to you how you configure the client and server, but don't tell the assessor to just double click the jar files. You need to spell out the command line for each in your Readme.txt even if it is as simple as:

If additional configuration is required after entering the command line then make that clear. For example in my submission I noted that once the Server GUI came up that the database server would not be running until either the "Start Server" button was pressed or "Action->Start Server" was selected from the menu.
Hope this helps,
Michael Morris
Abhinav Anand
Ranch Hand

Joined: May 02, 2002
Posts: 113
Hi Morris,
It definately helps. So now my command lines look like the following.

These command lines are supposed to invoked from the installation directory where the submission jar file is un-jarred. Now do i have to tell the examiners/accessors that they have to change over to the installation folder and then type out the above command lines, or simply should i tell them that the command lines to activate the server and client are the ones just mentioned in codes above.
If I have to specify the instruction to change the current directory to the installation directory then the same problem of platform independet command line for the pupose pop ups again.
Please Advice,
Any suggetions are welcome.
Michael Morris
Ranch Hand

Joined: Jan 30, 2002
Posts: 3451

These command lines are supposed to invoked from the installation directory where the submission jar file is un-jarred. Now do i have to tell the examiners/accessors that they have to change over to the installation folder and then type out the above command lines, or simply should i tell them that the command lines to activate the server and client are the ones just mentioned in codes above.

The question is will your command line accept any path to the db and work? Or is any other parameter absolutely dependant on a specific directory? If so then you should make a note of that fact, otherwise no. I designed my command lines so that all parameters were optional. That way if no parameters were on the command line then defaults were used like db.db in the user's directory.
Here is an excerpt from my Readme.txt describing command lines:

Hope this helps,
Michael Morris
Abhinav Anand
Ranch Hand

Joined: May 02, 2002
Posts: 113
Hi Morris,
It seems that you did not use connect box in the client startup. I have a connect box that allows user to inter the remote host name, port number and the service name by which the server is bound to the rmiregistry.
In local mode the the user has to enter the database path + the db name in the service name field.
Do you think that I should switch over to a simple command line parameter.
Any suggetions.
Michael Morris
Ranch Hand

Joined: Jan 30, 2002
Posts: 3451
Hi Vishal,

Do you think that I should switch over to a simple command line parameter.

Your way is just fine. I only showed that excerpt to give you an idea of what you need to tell the assesor about startup and configuration.
Michael Morris
Abhinav Anand
Ranch Hand

Joined: May 02, 2002
Posts: 113
Hi Michael,
As i have mentioned in my previous posts, my server implementation requires the host ip address as one of the parameters. But I feel that it does not makes any sense to make user type in the server host name, I can simply assume that the server host name is the ip address of the machine on which the Server.jar is executed.
Any suggetions ?
Michael Morris
Ranch Hand

Joined: Jan 30, 2002
Posts: 3451
Hi Vishal:

Any suggetions ?

Just use "//localhost" for binding to the RMI registry. Don't worry about getting the node IP address.
Michael Morris
Abhinav Anand
Ranch Hand

Joined: May 02, 2002
Posts: 113
Hi Michael,
Please clarify one point about your design. Your server implementation requires the user to enter the command line in the following format:
java xxxxxxxxx [-f file] [-p port]
Now although the parameters are optional, a user by mistake enters the following command line:
java xxxxxxxxx -ff xxx.xx
or
java xxxxxxxxx -pd xxxxxx
Notice that the user typed in -ff instead of -f and -pd instead of -p. In this situation what is the behaviour of your server implementation. Does it displays an error message and terminates, or the server displays a warning message and loads default values or the server simply ignores the invalid parameter and loads default value.
In my server implementation if the user commits any mistake the server terminates after printing an error message and a proper usage message like:
Invalid parameter string
USAGE: java suncertify.server.DatServer xx yyy zzz
Is this behavior acceptable. Moreover when I package my server into a jar file what should be the proper usage message. Should i modify my DataServer class to print the following:
USAGE:
java -jar Server.jar xx yyy zzz
Michael Morris
Ranch Hand

Joined: Jan 30, 2002
Posts: 3451
Hi Vishal,

In my server implementation if the user commits any mistake the server terminates after printing an error message and a proper usage message like:
Invalid parameter string
USAGE: java suncertify.server.DatServer xx yyy zzz

That's Unix programming 101. Been doin' it that way for I can't remember how long.

Moreover when I package my server into a jar file what should be the proper usage message. Should i modify my DataServer class to print the following:
USAGE:
java -jar Server.jar xx yyy zzz

That's up to you. I didn't even put the call to the java interpreter in my error message. I just did something like this:
FBNServer: Missing flag or parameter
usage: FBNServer [-f file.db] [-p port]
Hope this helps,
Michael Morris
Abhinav Anand
Ranch Hand

Joined: May 02, 2002
Posts: 113
Hi Michael,
Sorry for the delay in replying. A nasty accident kept me away from work.
Now I get your point regarding command lines. Its absolutely clear now.
I think that I have nearly completed my assignment now. The documentation stage is almost over. I hope to submit my assignment by the end of this month.
One more thing. In your other posts you have posted that your server had 6 exit codes and client had 24 exit codes. Does that mean:
System.exit(1); System.exit(20) etc...
What is the purpose & reason of doing so ?
Michael Morris
Ranch Hand

Joined: Jan 30, 2002
Posts: 3451
Hi Vishal,

System.exit(1); System.exit(20) etc...

Yep.

What is the purpose & reason of doing so ?

Goes back to how I learned. If you run a program from a startup script, you can trap the exit code and log it. If you know what each exit code means you can trouble shoot the problem and go straight to the code that's causing the problem.
You certainly don't have to do it like I did though. But you should exit with non-zero for abnormal termination.
Michael Morris
Abhinav Anand
Ranch Hand

Joined: May 02, 2002
Posts: 113
Hi Michael,
I need some comments about my locking architecture.
I have implemented my DataServer on the basis of Singleton pattern. I allow only one instance of data/remote data class for a single file. All clients will have the same instance of Data class.
My data class contains a single instance of LockManager class that handles locking and unlocking.
I have modified the Data class's lock and unlock method signatures to include client id. These methods do nothing but simply call the corresponding methods with same signature in the LockManager class.
Client ID is generated at the client side by concatenating java.rmi.server.UID() + ":" + InetAddress.getHost(). This client id is used by the client while requesting locks on records.
The LockManager class contains a HashMap that maps record numbers with clients.
In addition to this I have implemented a unlockAll() method in the DataClass, that instructs the lock manager to release all locks. This method signature has not been included in the DataInterface. So a calling class can access this method only when it directly instantiates the Data class by using


The unlock all method is called by DataServer class, whenever the DataServer is unreferenced.
Another thing, when the user tries to shut down server, i start a timer of 60 seconds before I finally shut down server. In the mean time I try to attempt a database lock (-1) on the database, if the database lock is successful the server shuts down immediately, otherwise a server shuts down forcefully after 60 seconds and unbinds the Data class instance from the rmiregistry.
Any Suggetions ?
[ September 12, 2002: Message edited by: Vishal Sinha ]
Michael Morris
Ranch Hand

Joined: Jan 30, 2002
Posts: 3451
Hi Vishal,

I have implemented my DataServer on the basis of Singleton pattern. I allow only one instance of data/remote data class for a single file. All clients will have the same instance of Data class.
My data class contains a single instance of LockManager class that handles locking and unlocking.

That looks fine. I assume that in the event that there were multiple Data objects representing different files that each would have its own LockManager. One thing though, you might want to let the LockManager be independant, ie don't keep a reference to it in Data, but keep a reference to Data in the LockManager. There is a reason for this that I will make clear in a moment.

I have modified the Data class's lock and unlock method signatures to include client id. These methods do nothing but simply call the corresponding methods with same signature in the LockManager class.

I really think that candidates are skating on thin ice when they start changing signatures on the provided methods. This is why I said you may consider keeping LockManager independant of Data. You can just leave Data's lock() and unlock() as empty methods and keep a reference to the LockManager in your remote implementation class. Then instead of calling data.lock(), you call lockManager.lock() instead. Of course that means that the remote implementation class will have to play nice by verifying that it has the lock on record before modifying it instead of letting Data verify it.

Client ID is generated at the client side by concatenating java.rmi.server.UID() + ":" + InetAddress.getHost(). This client id is used by the client while requesting locks on records.

This will probably work, but have you considered just using the remote implementation object itself as the ID? Are you using a ConnectionFactory to issue each client a unique connection? If not, you should seriously consider it. Then, ID becomes "this". Creating the ID on the client side complicates the client's interface into Data. It requires you to either change the signature of lock and unlock or add a method like setClientID to the interface.

The LockManager class contains a HashMap that maps record numbers with clients.

Same thing I did.

In addition to this I have implemented a unlockAll() method in the DataClass, that instructs the lock manager to release all locks. This method signature has not been included in the DataInterface. So a calling class can access this method only when it directly instantiates the Data class by using ...
The unlock all method is called by DataServer class, whenever the DataServer is unreferenced.

I didn't think of doing that. Sounds like a good idea though.

Another thing, when the user tries to shut down server, i start a timer of 60 seconds before I finally shut down server. In the mean time I try to attempt a database lock (-1) on the database, if the database lock is successful the server shuts down immediately, otherwise a server shuts down forcefully after 60 seconds and unbinds the Data class instance from the rmiregistry.

I did essentially the same thing, except that I waited for the time out before attempting to lock the database. But your way is fine. Are you notifying the client of the impmeding doom? I did, but it requires implementing an event mehcanism. You may or may not want to go to that much trouble.
Hope this helps,
Michael Morris
Abhinav Anand
Ranch Hand

Joined: May 02, 2002
Posts: 113
Hi Michael

I really think that candidates are skating on thin ice when they start changing signatures on the provided methods. This is why I said you may consider keeping LockManager independant of Data. You can just leave Data's lock() and unlock() as empty methods and keep a reference to the LockManager in your remote implementation class. Then instead of calling data.lock(), you call lockManager.lock() instead. Of course that means that the remote implementation class will have to play nice by verifying that it has the lock on record before modifying it instead of letting Data verify it.

There you make your point. I know this a serious disadvantage in implementing the locking architecture in the way I have done it. I have two options one is to document the whole god damn thing and accept the shortcomings with the advantages of the design. The second option is to implement it in the way every body in this group is doing it.
Michael, can you give me some leads in this regard as to how to implement the locking architecture without modifying the lock and unlock signature ?
Michael Morris
Ranch Hand

Joined: Jan 30, 2002
Posts: 3451
Hi Vishal,

Michael, can you give me some leads in this regard as to how to implement the locking architecture without modifying the lock and unlock signature ?

Well the most common approch is to use a ConnectionFactory and issue a unique connection to each client. Object identity being one of the three main premises of object orientation then guarantees that each client can now be indenified by its connection. With that in place, there is no need to generate an ID on the client side. So, the client now calls lock(record) (note that we haven't changed the signature of Data's public lock() method) on its connection object which then calls lockManager.lock(this, record). Now when the client calls modify() the connection object calls something like lockManager.isRecordLocked(this, record) before allowing the record to be modified. I threw a DatabaseExcption if an attempt was made to modify() a record of which the client did not own the lock.
LockManager will need a reference to Data to verify that the requested record is valid, ie is <= the total number of records in Data, but Data doesn't have to know anything except how to retrieve/add/modify/delete records.
Hope this helps,
Michael Morris
Abhinav Anand
Ranch Hand

Joined: May 02, 2002
Posts: 113
Hi Michael,
The scenario you described is clearly more advantageous than the one i have implemented.
Now as far as i understand you....
On the server side you bind a ConnectionFactory instance which generates a new instance of RemoteData for each client. So since each client has a unique RemoteData instance we can call lockManager.lock(record, this) in the lock(record) method of the RemoteData. Now you verify that the record is locked each time the user tries to edit/delete a record. This part is clear, and i am definitely going to change my locking scheme.
But one part is not clear to me. That is how do you create new RemoteData instance. Suppose:
I have a ConnectionFactory instance bound to the registry that has a getConnection() method.
The getConnection() method creates a new instance of RemoteData:
RemoteData remotedata = new RemoteData(xx.db)
Now if you are creating the remote data instance in the quoted way then how do you implement your lock manager.
In my views it can be done in this way:

In the above code i am having a single Data and LockManager instance for the whole connection factory but a different but unique RemoteData instance for each clients. Is there any other better way of achieving the purpose ?
Any Suggetions ?
Mark Spritzler
ranger
Sheriff

Joined: Feb 05, 2001
Posts: 17250
    
    6

Is there any other better way of achieving the purpose ?

No that is the best and a very elegant solution.
Mark
Michael Morris
Ranch Hand

Joined: Jan 30, 2002
Posts: 3451
Hi Vishal,

In the above code i am having a single Data and LockManager instance for the whole connection factory but a different but unique RemoteData instance for each clients. Is there any other better way of achieving the purpose ?

That looks fine. Of course you know that your getConnection() method will have to return the Remote interface. My ConnectionFactory did not keep a reference to Data and LockManager, instead I built a composite. I had a DataFactory class that associated Data objects with files and in the buildLockManager() method of the composite builder kept a static map of LockManager objects which were mapped to Data objects. I used lazy instantiation for each, ie the Data and/or LockManager was not created until the first call to get them. My ConnectionFactory had a constructor that took the file path (as a String) which it kept a reference to for passing to the builder classes. It also had getDBPath() and setDBPath() methods for convenience.
Hope this helps,
Michael Morris
Abhinav Anand
Ranch Hand

Joined: May 02, 2002
Posts: 113
Hi guys,

No that is the best and a very elegant solution.

Thanks. I am implementing this solution


That looks fine. Of course you know that your getConnection() method will have to return the Remote interface.

Of course. The only difference according to me between ConnectionManager and RemoteData regarding implementing the remote interface is that RemoteData will not need to extend the UnicastRemoteObject class.

My ConnectionFactory did not keep a reference to Data and LockManager, instead I built a composite.

I suppose this is because passing a number of parameters like Data, LockManager, File name etc. to different classes is not a very elegant solution. So it is better to design a composite that encapsulates all these different but interrelated classes into one class. In this way we can simply pass the composite as a parameter to different class's constructors which in turn can refer to the different class instances encapsulated by the composite using getXxx() method.

I used lazy instantiation for each, ie the Data and/or LockManager was not created until the first call to get them.

Why not simpy create them in the composite constructor itself ?
[ September 18, 2002: Message edited by: Vishal Sinha ]
[ September 18, 2002: Message edited by: Vishal Sinha ]
Michael Morris
Ranch Hand

Joined: Jan 30, 2002
Posts: 3451
Hi Vishal,

Why not simpy create them in the composite constructor itself ?

Because I used a Builder Pattern. The composite was built "on the fly" when getConnection() was called on the ConnectionFactory. So, the first client to connect caused Data and its LockManager to be constructed. In local mode, the ConnectionDirector (the main class of the Builder Pattern) was called directly to build a local object.
Michael Morris
Abhinav Anand
Ranch Hand

Joined: May 02, 2002
Posts: 113
Hi Michael,
Can you elaborate on your builder pattern implementation as you mentioned above.
Vishal
Michael Morris
Ranch Hand

Joined: Jan 30, 2002
Posts: 3451
Hi Vishal,
Here is my ConnectionDirector's createConnection() method:

DataBuilder is the parent class of all of the connection builders. The buildData(), buildSearch() and buildLockManager() methods are all empty in DataBuilder, but are overridden as required in the specialized builder classes. For example, all of the specialized builders overload buildData(), but the LocalDataBuilder class does not override buildLockManager(), leaving it an empty method since locking is not required in local mode. The getData() method returned a LocalDataConnection for LocalDataBuilder and a RemoteDataConnection for RemoteDataBuilder. Both LocalDataConnection and RemoteDataConnection implemented DataAccess which is the return type of getData().
Hope this helps,
Michael Morris
 
 
subject: Platform Independent Command Line