Bank Server (CORBA)
A. First Edition
This is a quite complicated assignment, at least I think so because it costs me almost a whole week. And I indeed learned
something useful.
3. CORBA
In this programming assignment, you are going to implement the simple
distributed banking system (DBS) from Assignment 1 in CORBA. In addition to
the three operations (namely, Open, Deposit, Withdraw, and Balance) defined
in Assignment 1, the DBS has the following operation.
• Transfer(src_acnt, dest_acnt, amt): Causes the balance of account number
src_acnt to be decreased by amt and the balance of account number dest_acnt
to be increased by amt. Returns the new account balance of account number
src_acnt.
Note that in order to perform a Transfer operation, two BankServers need to
communicate between themselves. A BankClient sends a Transfer operation to
the BankServer of the branch having the src_acnt. That BankServer implements
this Transfer operation by (i) sending a message to the BankServer having
the dest_acnt to perform the appropriate deposit, if the src_acnt has enough
funds and (ii) withdrawing the src_acnt properly once the dest_acnt has been
deposited. Obviously, these two operations must be performed atomically.
Furthermore, implement all communication among BankServers using TCP
protocol.
In this assignment you are going to develop this application in CORBA using
Java IDL. Specifically, do the following.
• Write the Java IDL interface definition for the BankServer with the five
specified operations.
• Implement the BankServer.
• Design and implement a BankClient which invokes the server to test the
correct operation of the DBS running multiple BankServers and multiple
BankClients.
Before this requirement, there are a previous assignment of java RMI and the requirement is like this:
3. Java RMI
In the programming assignments, you are going to develop a simple
distributed banking system (DBS). Our bank comprises a set of branches. Each
branch manages a disjoint subset of the bank's accounts. Associated with
each account is a unique account number!seven digit integer (the first three
digits indicate the branch and the next four digits indicate the unique
account within that branch), a balance!floating point number, as well as
other information (like name, address and contact information of the account
holder) that will be of less concern to us. Customers may invoke the
following operations on accounts:
• Open(acnt): Opens a new account and returns the (randomly generated)
account number acnt.
• Deposit(acnt, amt): Cause the balance of account number acnt to be
increased by the specified amount amt. Returns the new account balance.
• Withdraw(acnt, amt): Cause the balance of account number acnt to be
decreased by specified amount amt. Returns the new (possibly negative)
account balance.
• Balance(acnt): Returns the balance of account number acnt.
This application basically has a BankServer implementing the above
operations and a BankClient invoking these operations as necessary. For
simplicity, assume that the account information of a branch (that is, all
the accounts and their balances) is stored in a table accessed by the
account number and stored in persistent storage.
In this assignment you are going to develop this application using Java RMI.
Specifically, do the following.
• Write the Java RMI interface definition for the BankServer with the
specified operations.
• Implement the BankServer.
• Design and implement a BankClient which invokes the server to test the
correct operation of the DBS.
First of all, a toy server has big difference from a real one. A thread-safe one has big difference from a toy server.
What I have learned in this assignment? It seems a lot, but it is hard to enumerate all of them.
1. using file lock:
In java, file lock is implemented as global, mandatory one which means that if you set up file lock in one file channel it will affect all other file channel in same process. It is obvious that java uses some mechanism to check file access because it is above OS.
And it is mandatory so that even the holder of file lock must have release it before accessing file. But in multi-thread environment, releasing file lock has potential risk of losing file lock to other threads. So, I have to use a semaphore or mutex to synchronize all threads. Then why don't we only use semaphore instead of using both of them? Because file lock is more appropriate for multi-thread. Imagine that file lock can lock specific region of file. It is quite tricky in handling possible exception which may cause problem in semaphore counting. see below:
a) available is semaphore and we have to keep it if we get file lock. And we have to release it if we don't lock because it is better to give others opportunity to get file lock. And it clarify the responsibility of releasing semaphore after this function: if fileLock==null, we don't have semaphore, otherwise, we get it and need to release it eventually.
b) before throw any exception, release semaphore so that our server is robust. That's why the first thing I do is to release semaphore because it is like "critical section" which is the shorter the better. And at most time people are making mistakes of forgetting releasing semaphore by too many "if then else".
c) using "tryLock" instead of "lock": You may run into deadlock if you use "blocking" lock. For example, server A transfer from account a1 to a2 and server B transfer from a2 to a1. They both locks local account a1 and a2 respectively and then try to lock the destination account a2, a1 respectively which are already locked. If we use "blocking" lock, we will deadlock for sure. That is why we use "tryLock". But be careful, this method will throw an OverlappingFileLockException which may change the sequence of your program. So, you would better be careful to use "try-catch" to wrap it, otherwise you may not be able to release your semaphore.
protected FileLock lockFile(int account, boolean toShare) { int counter=0; FileLock fileLock=null; try { while (true) { //allow account starting from 0 fileLock=null; available.acquire(); try { fileLock= branchFile.getChannel().tryLock(account*AccountSize, AccountSize, toShare); } catch(Exception er) { if (!(er instanceof OverlappingFileLockException)) { System.out.println("what?"); } } //critical section should be as short as possible //available.release();//must release to give other a chance to try if (fileLock==null) { available.release(); if (counter<BankUtility.MaxLockTrialNumber) { counter++; Thread.sleep(100); //notify(); //wait(); } else { //cannot acquire lock, now quit //return oldValue;//failed throw new BankException(" account is locked"); } } else { //don't release semaphore break; } } } catch(Exception er) { fileLock=null;//indicating failure //System.out.println("lock file error of "+er.getMessage()); } finally { return fileLock; } }
2. static vs. non-static
a) Something must be static, like semaphore, file pointer. Some may not be. What's more, if they are not static, you may gain performance from it. First of all, you need to understand the multi-thread model in java server. In each server, you can simply write as if it is a single-thread if you don't need synchronization because as soon as a client RPC request comes, java will create an instance of your server to handle it. Therefore the multi-thread is implemented beyond your server. However, you must be careful about your global variable if they are static which may not be thread-safe.
b) In each server, it needs to create a thread to "listen" TCP requests from other server. originally I thought it was better to declare it as static one since we are only listening at a fixed port and there are many instance of server who are waiting for reply by listening to this port. How can we do it? By post the packet to a global static table? Or by using a global static socket to listen? I have this puzzle purely because I wrote socket program before by using C/C++ which are purely socket API's. In java, it becomes a baby's job because they implement a "inputstream" and "outputstream". I guess they must send an extra return port number so that receiver knows how to return message back otherwise how can you differentiate multiple message from same port number? Actually I wrote like this and then realized that I didn't have to. And the idea is quite straightforward that I pass a reply port number along my packet so that listener will send back to my port number. However, you have to synchronize the access of this unused port number.
c) If it is static semaphore, then you must be careful to only create them once. So, in constructor, place a flag to avoid multiple creation.
d) Garbage collection only applies to memory management. For other resources like socket, file pointer etc. explicitly close them yourself. I made this mistakes by creating a temporary socket because I need "local host name". I happened to choose the port number of listening socket and then close it as a good habit. But weird thing happens when I rerun program and it seems java doesn't properly close the resources of socket. The listening port is occupied. So, I choose another port number for temporary socket.
3. Multiple file pointers
a) Originally I think I can only have one file pointer since I need file synchronization. If each server instance has its own file pointer, how can I synchronize them even my semaphore is already static? Later I realized that by using file lock, you can afford multiple file pointer because file lock is exactly another form of synchronization. You are actually testing for possible conflict when trying to lock. So, if you succeed in getting file lock, you can be rest assured to move your file pointer to where you want to. And by declaring each reading/writing file pointer as non-static, you can also be sure that you are the sole user of file pointer because within each server we maintain single-threaded model. But you need one global static file pointer for all instance to lock file lock. That is why in function "lockFile", I use a global static file pointer "branchFile" for locking and when writing to file I use a non-static file pointer "rpcFiler" and "threadFiler" to access file. And there is no synchronization necessary for them! Because there is no other threads competing for them.
b) You must keep in mind that RPC is very slow and before sending out remote TCP request, release semaphore but only keep the file lock so that other threads can have chances by acquiring semaphore. However, there is some attemption in acquiring file lock as "non-exclusive" type first and release it then acquire it with "exclusive" one. I hesitate for a while and decide to not to take this small advantage because it is exactly the "upgradeable file lock" and in java I haven't found such one.
4. Wait and notify is a confusing thing to me because I don't understand them at all when using them. It kept giving exceptions like "thread not own ...". Later I understand a little bit that it is only used for some synchronized operation. And what I need is a "Thread.sleep(1000)". Another function I spent a lot of time searching is: getlocalhostname which is the name of windows' version. It is in java like this:
InetAddress inetAddress;
ServerSocket serverSocket=new ServerSocket(9000);//don't use any port in your
program
inetAddress=serverSocket.getInetAddress();
inetAddress.getLocalHost().getHostName();
5. Mistaken in "finally". As in C++, we don't have key words of "finally", I accidentally regard it as if it will only be called when exception happens since it is follows "try-catch". However, no matter whether there is exception or not, it will be called.
6. Some code are trivial but there is some traps.
Socket socket=new Socket(hostName, portNumber);
java.io.DataInputStream inStream=new java.io.DataInputStream(socket.accept().getInputStream());
This line of code is actually blocking and I don't quite like this style of coding even though I tried to write it myself.
.
E.Further improvement
It is just an assignment and most students don't want to treat it seriously.
F.File listing
IDL file: Bank.idl
Package BankServer
1. BankServer.java
2. BankImpl.java
Package BankClient
1. BankClient.java
Package BankApp
1. Bank.java
2. BankException.java
3. BankUtility.java
4. BankOperations.java (generated by idlj)
5. _BankStub.java (generated by idlj)
6. BankHolder.java (generated by idlj)
7. BankHelper.java (generated by idlj)
8. BankPOA.java (generated by idlj)
file name: Bank.idl
module BankApp { interface Bank { long Open(); float Deposit(in long acnt, in float amt); float Withdraw(in long acnt, in float amt); float Balance(in long acnt); float Transfer(in long src, in long dest, in float amount); }; };
file name: BankServer.java
package BankServer;
import BankServer.BankImpl;
import BankApp.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.PortableServer.*;
import org.omg.PortableServer.POA;
import java.util.Properties;
import org.omg.CORBA.ORB.*;
import org.omg.CORBA.*;
import java.net.*;
public class BankServer
{
public static String branchHostNames[]={"chile", "uruguay", "argentina"};
public static int myBranchNo;
public static String myHostName;
public static NamingContextExt ncRef;
public static void main(String args[])
{
try
{
if (args.length<5)
{
System.out.println("usage: BankServer.BankServer [branch number]");
return ;
}
myBranchNo=Integer.parseInt(args[4]);
//inetSocketAddress=new InetSocketAddress(5000);
//System.out.println("host name is" +inetSocketAddress.getHostName());
// create and initialize the ORB
ORB orb = ORB.init(args, null);
// get reference to rootpoa& activate the POAManager
POA rootpoa= (POA)orb.resolve_initial_references("RootPOA");
rootpoa.the_POAManager().activate();
// create servant and register it with the ORB
BankImpl bankImpl= new BankImpl(myBranchNo);
bankImpl.setORB(orb);
// get object reference from the servant
org.omg.CORBA.Object ref =
rootpoa.servant_to_reference(bankImpl);
// and cast the reference to a CORBA reference
Bank href= BankHelper.narrow(ref);
// get the root naming context
// NameServiceinvokes the transient name service
org.omg.CORBA.Object objRef=
orb.resolve_initial_references("NameService");
//now create a pointer to namecontext
//NamingContext namingContext=objRef;
ncRef= NamingContextExtHelper.narrow(objRef);
// bind the Object Reference in Naming
//String name = "Branch" + args[4];
//String port="1234";
//NameComponent path[] = ncRef.to_name( name );
//ncRef.rebind(path, href);
//serverSocket=new ServerSocket(BankUtility.BasePortNumber);
/*//this is for test
branchHostNames=new String[BankUtility.MaxBranchNumber];
for (int i=0; i<BankApp.BankUtility.MaxBranchNumber; i++)
{
branchHostNames[i]="unknown";
}
*/
registerInfo(ncRef, href);
// Use NamingContextExt, which is part of the
// Interoperable Naming Service (INS) specification.
System.out.println("Branch"+myBranchNo+ " Server ready and waiting ...");
// wait for invocations from clients
orb.run();
}
catch(Exception er)
{
System.out.println("error");
System.out.println(er.getMessage());
}
}
public static void clearHostName(NamingContextExt ncRef, int myBranchNo)
{
BindingListHolder bl=new BindingListHolder();
BindingIteratorHolder bh=new BindingIteratorHolder();
ncRef.list(50, bl, bh);
Binding bindings[]=bl.value;
NameComponent nc[];//=n[0].binding_name;
//for (int i=0; i<n.length; i++)
//ncRef.unbind(test);
for (int i=0; i<bindings.length; i++)
{
//System.out.println("loop for " + i);
if (bindings[i].binding_type==BindingType.ncontext)
{
nc=bindings[i].binding_name;
for (int j=0; j<nc.length; j++)
{
if (nc[j].kind.compareTo("host"+Integer.toString(myBranchNo))==0)
{
try
{
System.out.println("try to unbind "+ "host"+Integer.toString(myBranchNo));
ncRef.unbind(nc);
//ncRef.resolve(nc[j]);
}
catch (Exception er)
{
System.out.println("unbind error in clear host name"+nc[j].id);
}
}
}
}
}
}
protected static void registerInfo(NamingContextExt ncRef, Bank bank)
{
InetAddress inetAddress;
ServerSocket serverSocket;
try
{
//clearHostName(ncRef, myBranchNo);
serverSocket=new ServerSocket(9000);
//System.out.println("why?");
inetAddress=serverSocket.getInetAddress();
//System.out.println("hostname="+ inetAddress.getLocalHost().getHostName());
//branchHostNames[myBranchNo-100]=inetAddress.getLocalHost().getHostName();
serverSocket.close();
NamingContextExt bankContext=ncRef;
//NameComponent branchNC[]=ncRef.to_name("Branch"+branchNo);
//NamingContextExt branchCtx=(NamingContextExt)ncRef.bind_new_context(branchNC);
NameComponent hostNode= new NameComponent(branchHostNames[myBranchNo-100], "host"+ myBranchNo);
NameComponent hostDir[]={hostNode};
bankContext.rebind_context(hostDir, bankContext);
NameComponent path[] = bankContext.to_name("Branch"+myBranchNo);
//ncRef.rebind(path, bank);
bankContext.rebind_context(hostDir, bankContext);
ncRef.rebind(path, bank);
}
catch (Exception er)
{
System.out.println("error in create");
System.out.println(er.getMessage());
}
}
}
file name: BankImpl.java
package BankServer; import BankApp.*; import org.omg.CosNaming.*; import org.omg.CORBA.*; import java.util.Properties; import java.rmi.*; import java.io.*; import java.nio.*; import java.util.*; import java.net.*; import java.util.concurrent.*; import java.nio.channels.*; import java.nio.channels.OverlappingFileLockException; class BankImpl extends BankPOA { private ORB orb; public BranchManager branchManager; //protected static int myBranchNumber; public BankImpl(int branchID) { try { branchManager=new BranchManager(branchID); //myBranchNumber=branchID; } catch(Exception er) { System.out.println(er.getMessage()); } } public void setORB(ORB orb_val) { orb = orb_val; } public int Open () //throws java.rmi.RemoteException { int result=-1; try { result= branchManager.Open(); } catch (Exception er) { System.out.println(er.getMessage()); } return result; } public float Deposit (int acnt, float amt) //throws java.rmi.RemoteException { float result=BankUtility.NegativeInfinite; try { result= branchManager.Deposit(acnt, amt); } catch (Exception er) { System.out.println(er.getMessage()); } finally { return result; } } public float Withdraw (int acnt, float amt) //throws java.rmi.RemoteException { float result=BankUtility.NegativeInfinite; try { result= branchManager.Withdraw(acnt, amt); } catch (Exception er) { System.out.println(er.getMessage()); } finally { return result; } } public float Balance (int acnt) //throws java.rmi.RemoteException { float result=BankUtility.NegativeInfinite; try { result= branchManager.Balance(acnt); } catch (Exception er) { System.out.println(er.getMessage()); } finally { return result; } } public float Transfer (int src, int dest, float amt) //throws java.rmi.RemoteException { float result=BankUtility.NegativeInfinite; try { result= branchManager.Transfer(src, dest, amt); } catch (Exception er) { System.out.println(er.getMessage()); } finally { return result; } } } //end class class BranchManager { //these are all static member which means only one copy is maintained public static final int AccountSize=4; protected static Random rand; protected static boolean flag=true; private static final int MAX_AVAILABLE = 1; //THIS IS ACTING AS ENUM IN PACKET //REQUEST IS SENT BY INITIATOR protected final static int PREPARED=1; //THIS CAN BE REPLIED FROM RECEIVER OR SENT AS SENDER TO CANCEL protected final static int REJECTED=2; private static Semaphore available; //these are non-static member, the constructor and finalize should treat them differently private int branchID; protected Listener listener; public ServerSocket serverSocket; public static RandomAccessFile branchFile;//, branchReader; protected RandomAccessFile rpcFiler, threadFiler; public BranchManager(int id) throws java.rmi.RemoteException { branchID=id; String fileName=Integer.toString(branchID)+".data"; try { //non-static member rpcFiler=new RandomAccessFile(fileName, "rw"); threadFiler=new RandomAccessFile(fileName, "rw"); //the following are all static global variables if (flag) { flag=false; Date date=new Date(); rand=new Random(date.getTime()); available=new Semaphore(MAX_AVAILABLE, true); branchFile=new RandomAccessFile(fileName, "rw"); //branchReader=new RandomAccessFile(fileName, "r"); if (branchFile.length()==0)//this is the new file { branchFile.setLength(AccountSize*10000); branchFile.seek(0);//starting for (int j=0; j<10000; j++) { //branchFiles[i].writeInt(0);//initialize to 0; branchFile.writeFloat(BankUtility.NegativeInfinite);// indicating unused } } } //listener might need use "serversocket", so place it here serverSocket=new ServerSocket(BankUtility.BasePortNumber); listener=new Listener(); listener.start(); } catch(Exception er) { System.out.println("BranchManager constructor error of "+er.getMessage()); } } protected void finalize()throws java.rmi.RemoteException { try { if (branchFile!=null) { branchFile.close(); branchFile=null; } rpcFiler.close(); threadFiler.close(); listener.join(); } catch (Exception er) { throw new BankException("finalize error of "+ er.getMessage()); } } //there is still need for file lock on opening new account //because you are trying to find invalid account //it is crazy to assume that user might use invalid account for transaction public int Open() throws java.rmi.RemoteException { int account=BankUtility.InvalidAccountNumber; boolean result=false; FileLock fileLock=null; try { while (!result) { //allow account starting from 0 account=rand.nextInt(10000); if ((fileLock=lockFile(account, false))!=null) { //System.out.println("get semaphore"); fileLock.release(); rpcFiler.seek(account*AccountSize); if (rpcFiler.readFloat()==BankUtility.NegativeInfinite) { rpcFiler.seek(account*AccountSize); rpcFiler.writeFloat(0); result=true; } //always release! and don't use "break" which is bad!!!! available.release(); } } } catch (Exception er) { //System.out.println(er.getMessage()); account=BankUtility.InvalidAccountNumber;//indicating unknown error throw new BankException("unknown open error of "+er.getMessage()); } finally { if (account!=BankUtility.InvalidAccountNumber) { account+=branchID*10000; } return account; } } //we first try to get file lock with possible several failure //then we get semaphore and r/w record //finally must test file lock and release it, we cannot do this for semaphore //so, whenever exception happens, it must maintain a correct counter for it. public float Deposit(int acnt, float amount) throws java.rmi.RemoteException { FileLock fileLock=null; int account=acnt%10000; int counter=0; float oldValue=BankUtility.NegativeInfinite; //entering critical section try { //account is possibly locked so we need to try several times to lock if ((fileLock=lockFile(account, false))!=null) { fileLock.release(); rpcFiler.seek(account*AccountSize); oldValue=rpcFiler.readFloat(); //account not opened if (oldValue!=BankUtility.NegativeInfinite) { rpcFiler.seek(account*AccountSize); oldValue+= amount; rpcFiler.writeFloat(oldValue); } available.release(); } } catch(Exception er) { oldValue=BankUtility.NegativeInfinite; throw new BankException("unknown deposit error: "+er.getMessage()); } finally { return oldValue; } } //assume amount is positive //the sequence of filelock and semaphore is critical in performance //we only need to lock and want to hold semaphore as short as possible public float Withdraw(int acnt, float amount) throws java.rmi.RemoteException { FileLock fileLock=null; int account=acnt%10000; int counter=0; float oldValue=BankUtility.NegativeInfinite; try { //account is possibly locked so we need to try several times to lock if ((fileLock=lockFile(account, false))!=null) { fileLock.release(); rpcFiler.seek(account*AccountSize); oldValue=rpcFiler.readFloat(); //account not opened if (oldValue!=BankUtility.NegativeInfinite && oldValue>=amount) { rpcFiler.seek(account*AccountSize); oldValue-= amount; rpcFiler.writeFloat(oldValue); } else { oldValue=BankUtility.NegativeInfinite; } available.release(); } } catch(Exception er) { oldValue=BankUtility.NegativeInfinite; throw new BankException("unknown withdraw error: "+er.getMessage()); } finally { return oldValue; } } //even reading may not need to be synchronized, but //file pointer has to be! So make it simple by synchronized! public float Balance(int acnt) throws java.rmi.RemoteException { float oldValue=BankUtility.NegativeInfinite; FileLock fileLock=null; int account=acnt%10000; int counter=0; try { //account is possibly locked so we need to try several times to lock if ((fileLock=lockFile(account, true))!=null) { fileLock.release(); rpcFiler.seek(account*AccountSize); oldValue=rpcFiler.readFloat(); //account not opened available.release(); } } catch(Exception er) { oldValue=BankUtility.NegativeInfinite; throw new BankException("unknown balance error: "+er.getMessage()); } finally { return oldValue; } } protected float localTransfer(int src, int dest, float amt) throws java.rmi.RemoteException { FileLock srcLock=null, destLock=null; float srcValue=BankUtility.NegativeInfinite, destValue=BankUtility.NegativeInfinite; int srcAccount=src%10000, destAccount=dest%10000; try { //notice the style, we have to do like this if ((srcLock=lockFile(srcAccount, false))!=null ) { available.release(); if ((destLock=lockFile(destAccount, false))!=null) { srcLock.release(); destLock.release(); rpcFiler.seek(srcAccount*AccountSize); srcValue=rpcFiler.readFloat(); rpcFiler.seek(destAccount*AccountSize); destValue=rpcFiler.readFloat(); if (srcValue!=BankUtility.NegativeInfinite && srcValue>=amt) { srcValue-=amt; destValue+=amt; //source rpcFiler.seek(srcAccount*AccountSize); rpcFiler.writeFloat(srcValue); //dest rpcFiler.seek(destAccount*AccountSize); rpcFiler.writeFloat(destValue); } else { srcValue=BankUtility.NegativeInfinite; } available.release(); } else { srcLock.release(); } } } catch(Exception er) { throw new BankException("unknown error of local transfer:"+ er.getMessage()); } finally { return srcValue; } } protected float remoteTransfer(int src, int dest, float amount) throws java.rmi.RemoteException { //first we send out our transfer request float result=BankUtility.NegativeInfinite; int account=src%10000; int reply, index=dest/10000-100; FileLock fileLock=null; try { //checkHostName(src);//possible has exception if ((fileLock=lockFile(account, false))!=null)//do nothing { //System.out.println("after remote transfer for file lock"); fileLock.release(); rpcFiler.seek(account*AccountSize); result=rpcFiler.readFloat(); if (result!=BankUtility.NegativeInfinite && result>=amount) { //here we lock again because rpc is slow, we give other one chances to lock //System.out.println("before re-lock in remote transfer for file lock"); try { fileLock=branchFile.getChannel().lock(account*AccountSize, AccountSize, false); } catch(Exception er) { System.out.println("This cannot be true, if it happens everything is wrong"); } available.release(); Socket socket=new Socket(BankServer.branchHostNames[index], BankUtility.BasePortNumber); java.io.DataOutputStream outStream=new java.io.DataOutputStream(socket.getOutputStream()); java.io.DataInputStream inStream=new java.io.DataInputStream(socket.getInputStream()); outStream.writeInt(dest);//account outStream.writeFloat(amount);//amount reply=inStream.readInt(); //must close socket, file resource, in java it is impossible to do this for you if ( reply == PREPARED) { //WE ARE SURE WE ARE THE SOLE USER OF RPC. AND WE ARE RPC USER NOW!!!!!, NOT THREAD USER available.acquire();//we need to prevent others accidentaly lock again fileLock.release(); rpcFiler.seek(account*AccountSize); //let's try to make sure, this is only for test result-=amount;//testing only rpcFiler.writeFloat(result); available.release(); } else { fileLock.release(); result=BankUtility.NegativeInfinite; //we didn't acquire semaphore if (reply!=REJECTED) { throw new BankException("unknown TCP error in remote transfer by recieving wrong packet"); } //we still need to release, but we don't have to release semaphore } } else { result=BankUtility.NegativeInfinite;//this costs me 5 hours!!!!!!! available.release(); } } } catch(Exception er) { result=BankUtility.NegativeInfinite; throw new BankException("unknown error of remote transfer:" + er.getMessage()); } finally { return result; } } //we use a dummy global filepointer branchFile for lock //and use another file pointer for writing protected FileLock lockFile(int account, boolean toShare) { int counter=0; FileLock fileLock=null; try { while (true) { //allow account starting from 0 fileLock=null; available.acquire(); try { fileLock= branchFile.getChannel().tryLock(account*AccountSize, AccountSize, toShare); } catch(Exception er) { if (!(er instanceof OverlappingFileLockException)) { System.out.println("what?"); } } //critical section should be as short as possible //available.release();//must release to give other a chance to try if (fileLock==null) { available.release(); if (counter<BankUtility.MaxLockTrialNumber) { counter++; Thread.sleep(100); //notify(); //wait(); } else { //cannot acquire lock, now quit //return oldValue;//failed throw new BankException(" account is locked"); } } else { //don't release semaphore break; } } } catch(Exception er) { fileLock=null;//indicating failure //System.out.println("lock file error of "+er.getMessage()); } finally { return fileLock; } } public float Transfer(int src, int dest, float amt) throws java.rmi.RemoteException { float result=BankUtility.NegativeInfinite; try { if (dest/10000==BankServer.myBranchNo) { //do the local transfer without using TCP result= localTransfer(src, dest, amt); } else { result= remoteTransfer(src, dest, amt); } } catch(Exception er) { result=BankUtility.NegativeInfinite; throw new BankException("unknown error of transfer:"+er.getMessage()); } finally { return result; } //now starting TP communication } class Listener extends Thread { public void run() { java.io.DataInputStream inStream; java.io.DataOutputStream outStream; FileLock fileLock=null; int result, account; float amount=-1, oldValue; while (true) { try { Socket socket=serverSocket.accept(); inStream=new java.io.DataInputStream(socket.getInputStream()); outStream=new java.io.DataOutputStream(socket.getOutputStream()); account=inStream.readInt()%10000;//the index of source branch amount=inStream.readFloat(); if ((fileLock=lockFile(account, false))!=null) { //System.out.println("after listener trying to lock file"); fileLock.release(); threadFiler.seek(account*AccountSize); oldValue=threadFiler.readFloat(); if (oldValue!=BankUtility.NegativeInfinite) { oldValue+=amount; threadFiler.seek(account*AccountSize); threadFiler.writeFloat(oldValue); result=PREPARED; } else { result=REJECTED; } available.release(); } else { result=REJECTED; } outStream.writeInt(result); } catch (Exception er) { System.out.println("listener error of "+er.getMessage()); } } } } }
file name: BankClient.java
package BankClient; import java.io.*; import java.util.*; import BankApp.BankException.*; import BankApp.*; import java.util.concurrent.*; import org.omg.CosNaming.NamingContextPackage.*; import org.omg.CosNaming.*; import org.omg.CORBA.ORB.*; import org.omg.CORBA.*; public class BankClient { //protected static String branchHostNames[]; //protected static Bank banks[]; protected static int RunningThreadNumber=10; protected static int counter=0; protected static TransferThread transferThread0[], transferThread1[], transferThread2[]; //protected static TransferThread doubleThread0[], doubleThread1[]; protected static RunningThread deposits[]; protected static RunningThread withdraws[]; protected static CyclicBarrier tripleBarrier=new CyclicBarrier(RunningThreadNumber*3); protected static CyclicBarrier doubleBarrier=new CyclicBarrier(RunningThreadNumber*2); protected static Bank h; protected static Random rand=new Random(); protected static final String menuStr ="Please input your choice:\nOpen(1), Deposit(2), Withdraw(3), Balance(4), Transfer(5), Exit(6)\n"; protected static BufferedReader bufInput; protected static NamingContextExt ncRef; protected static ORB orb; public static int Open()throws java.rmi.RemoteException { int account=(100+rand.nextInt(BankUtility.MaxBranchNumber))*10000; getInterface(account); return h.Open(); } public static float Deposit(int account, float amount)throws java.rmi.RemoteException { getInterface(account); return h.Deposit(account, amount); } public static float Withdraw(int account, float amount)throws java.rmi.RemoteException { getInterface(account); return h.Withdraw(account, amount); } public static float Balance(int account)throws java.rmi.RemoteException { getInterface(account); return h.Balance(account); } public static float Transfer(int source, int dest, float amount)throws java.rmi.RemoteException { getInterface(source); return h.Transfer(source, dest, amount); } protected static boolean checkAccount(int account) { int result=account/10000-100; return result>=0&&result<BankUtility.MaxBranchNumber; } protected static void getInterface(int account)throws java.rmi.RemoteException { //for testing int branchIndex=account/10000-100; if (branchIndex>=BankUtility.MaxBranchNumber||branchIndex<0) { throw new BankException("branch number invalid"); } try { h=BankHelper.narrow(ncRef.resolve_str("Branch"+(branchIndex+100))); } catch(Exception er) { throw new BankException(er.getMessage()); } if (h==null) { throw new BankException("cannot bind branch"+(branchIndex+100)); } } public static void main(String args[]) { try { orb = ORB.init(args, null); org.omg.CORBA.Object objRef= orb.resolve_initial_references("NameService"); ncRef= NamingContextExtHelper.narrow(objRef); bufInput = new BufferedReader(new InputStreamReader(System.in)); manualTester(); threadTester(); transferTester(); pairTransferTester(); autoTester(); } catch(Exception er) { System.out.println(er.getMessage()); } } //a helper class of thread to test mutual exclusion in server static class RunningThread extends Thread { protected float amount, newAmount; protected int account; //protected BankInterface bankInterface; public RunningThread(int acnt, float amt) { amount=amt; account=acnt; } public void run() { try { doubleBarrier.await(); if (amount>0) { System.out.println(getName()+ " reports to deposit "+Float.toString(amount) + " to account " + Integer.toString(account) +" and balance is " + Float.toString(Deposit(account, amount))); } else { System.out.println(getName()+ " reports to withdraw "+Float.toString(-amount) + " to account " + Integer.toString(account)+ " and balance is " + Float.toString(Withdraw(account, -amount))); } } catch (Exception er) { System.out.println(er.getMessage()); } } } protected static int getAccount(int index) { int result; while (true) { result=(int)BankUtility.NegativeInfinite; try { result=Open(); } catch(Exception er) { System.out.println("get account error of :"+er.getMessage()); } if (result/10000-100==index) { break; } } return result; } static class TransferThread extends Thread { protected float amount; protected int source, destination; //protected BankInterface bankInterface; public TransferThread(int src, int dest, float amt) { amount=amt; source=src; destination=dest; } public void run() { float newAmount, myAmount=amount; int mySource=source, myDest=destination; try { tripleBarrier.await(); do { newAmount=Transfer(mySource, myDest, myAmount); //System.out.println(getName()+" fail in transfer from "+source+" to "+destination); if (newAmount==BankUtility.NegativeInfinite) { Thread.sleep(1000); } else { break; } }while (true); System.out.println(getName()+" succeed in transfer from "+mySource+" to "+myDest+ " and balance of "+source+" should be " +newAmount); /* System.out.println(" and let's check " + source + " is "+Balance(source)+ " and " +destination+ " is "+Balance(destination)); */ counter++; } catch (Exception er) { System.out.println("transfer thread in run error of what ? "+ er.getMessage()); } } } static class PairTransferThread extends Thread { protected float amount, newAmount; protected int source, destination; //protected BankInterface bankInterface; public PairTransferThread(int src, int dest, float amt) { amount=amt; source=src; destination=dest; } public void run() { try { doubleBarrier.await(); do { newAmount=Transfer(source, destination, amount); //System.out.println(getName()+" fail in transfer from "+source+" to "+destination); if (newAmount==BankUtility.NegativeInfinite) { Thread.sleep(1000); } else { break; } }while (true); System.out.println(getName()+" succeed in transfer from "+source+" to "+destination+ " and balance of "+source+" should be " +newAmount); /* System.out.println(" and let's check " + source + " is "+Balance(source)+ " and " +destination+ " is "+Balance(destination)); */ counter++; } catch (Exception er) { System.out.println("transfer thread in run error of what ? "+ er.getMessage()); } } } protected static void pairTransferTester() { int account0,account1; final float InitialAmount=5000; float amount; try { account0=getAccount(1); account1=getAccount(2); //an initialize all counts to 5000 PairTransferThread t0[]=new PairTransferThread[RunningThreadNumber]; PairTransferThread t1[]=new PairTransferThread[RunningThreadNumber]; System.out.println("thread test begins and we deposit 5000.0 to each account " + " and we are going to run some number of thread to transfer 50 from one to the other," + "if server is not properly synchronized, the balance will not be the same probably"); Deposit(account0, InitialAmount); Deposit(account1, InitialAmount); System.out.println("account "+account0+" has balance of "+Balance(account0)); System.out.println("account "+account1+" has balance of "+Balance(account1)); for (int i=0; i<RunningThreadNumber; i++) { t0[i]=new PairTransferThread(account0,account1, 50); t0[i].start(); t1[i]=new PairTransferThread(account1,account0, 50); t1[i].start(); } while (counter<RunningThreadNumber*2) { Thread.sleep(2000); } for (int i=0; i<RunningThreadNumber; i++) { t0[i].join(); t1[i].join(); } System.out.println("thread test is over and let's check if the balance of account remains same as before"); System.out.println("account "+account0+" has balance of "+Balance(account0)); System.out.println("account "+account1+" has balance of "+Balance(account1)); } catch (Exception er) { System.out.println("transfer thread error of "+ er.getMessage()); } } protected static void transferTester() { final int MaxTransferAccount=3; int accounts[]=new int[MaxTransferAccount]; final float InitialAmount=5000; int index1, index2; float amount; try { for (int i=0; i<MaxTransferAccount; i++) { //accounts[i]=getAccount(rand.nextInt(BankUtility.MaxBranchNumber)); accounts[i]=getAccount(i); Deposit(accounts[i], InitialAmount); } //an initialize all counts to 5000 transferThread0=new TransferThread[RunningThreadNumber]; transferThread1=new TransferThread[RunningThreadNumber]; transferThread2=new TransferThread[RunningThreadNumber]; System.out.println("thread test begins and we deposit 5000.0 to each account " + " and we are going to run some number of thread to transfer 50 from one to the other," + "if server is not properly synchronized, the balance will not be the same probably"); for (int i=0; i<MaxTransferAccount; i++) { System.out.println("account "+accounts[i]+" balance is "+Balance(accounts[i])); } for (int i=0; i<RunningThreadNumber; i++) { transferThread0[i]=new TransferThread(accounts[0],accounts[1], 50); transferThread0[i].start(); transferThread1[i]=new TransferThread(accounts[1],accounts[2], 50); transferThread1[i].start(); transferThread2[i]=new TransferThread(accounts[2],accounts[0], 50); transferThread2[i].start(); } while (counter<RunningThreadNumber*3) { Thread.sleep(2000); } for (int i=0; i<RunningThreadNumber; i++) { transferThread0[i].join(); transferThread1[i].join(); transferThread2[i].join(); } System.out.println("thread test is over and let's check if the balance of account remains same as before"); for (int i=0; i<MaxTransferAccount; i++) { System.out.println("account "+accounts[i]+" balance is "+Balance(accounts[i])); } } catch (Exception er) { System.out.println("transfer thread error of "+ er.getMessage()); } } protected static void threadTester() { int account; float amount; try { account=Open();//open a new account; amount=Deposit(account, 3000); deposits=new RunningThread[RunningThreadNumber]; withdraws=new RunningThread[RunningThreadNumber]; System.out.println("thread test begins and we deposit 3000.0 to account " + Integer.toString(account) + " and we are going to run equal number of deposit and withdraw actions," + "if server is not properly synchronized, the balance will not be the same probably"); System.out.println("the balance of account "+ Integer.toString(account) + " is " + Float.toString(amount)); for (int i=0; i<RunningThreadNumber; i++) { deposits[i]=new RunningThread(account, 50); deposits[i].start(); withdraws[i]=new RunningThread(account, -50); withdraws[i].start(); } for (int i=0; i<RunningThreadNumber; i++) { deposits[i].join(); withdraws[i].join(); } System.out.println("thread test is over and let's check if the balance of account remains same as before"); System.out.println("the balance of "+Integer.toString(account) + " is " + Float.toString(Balance(account))); } catch (Exception er) { System.out.println(er.getMessage()); } } protected static void manualTester() { //now do the test int choice=0; int account, dest; float amount; do { //bufOutput.writeBytes(menuStr); System.out.println(menuStr); try { choice = Integer.parseInt(bufInput.readLine()); switch(choice) { case 1: //randomly choose a server //bufOutput.writeBytes("new opened account is" + Integer.toString(account)+"\n"); System.out.println("new opened account is " + Integer.toString(Open())); break; case 2: //bufOutput.writeBytes("input account no.\n"); System.out.println("input account no."); account=Integer.parseInt(bufInput.readLine()); if (!checkAccount(account)) { System.out.println("invalid account number!(1000000---1099999)"); break; } //bufOutput.writeBytes("input amount to deposit:\n"); System.out.println("input amount to deposit:"); amount=Float.parseFloat(bufInput.readLine()); System.out.println("new balance is "+Float.toString(Deposit(account, amount))); break; case 3: //bufOutput.writeBytes("input account no.\n"); System.out.println("input account no"); account=Integer.parseInt(bufInput.readLine()); //bufOutput.writeBytes("input amount to withdraw:\n"); if (!checkAccount(account)) { System.out.println("invalid account number!(1000000---1099999)"); break; } System.out.println("input amount to withdraw:"); amount=Float.parseFloat(bufInput.readLine()); System.out.println("new balance is "+ Float.toString(Withdraw(account, amount))); break; case 4: //bufOutput.writeBytes("input account no.\n"); System.out.println("input account no"); account=Integer.parseInt(bufInput.readLine()); if (!checkAccount(account)) { System.out.println("invalid account number!(1000000---1099999)"); break; } //get server //bufOutput.writeBytes("balance of account " + Integer.toString(account)+ // " is " + Float.toString(h.Balance(account))+ "\n"); System.out.println("balance of account " + Integer.toString(account)+ " is " + Float.toString(Balance(account))); break; case 5: //bufOutput.writeBytes("input account no.\n"); System.out.println("input source account no"); account=Integer.parseInt(bufInput.readLine()); if (!checkAccount(account)) { System.out.println("invalid account number!(1000000---1099999)"); break; } System.out.println("input destination account no"); dest=Integer.parseInt(bufInput.readLine()); if (!checkAccount(account)) { System.out.println("invalid account number!(1000000---1099999)"); break; } System.out.println("input amount to transfer"); amount=Float.parseFloat(bufInput.readLine()); System.out.println("transfer "+ Float.toString(amount) + " from "+ Integer.toString(account) + " to " +Integer.toString(dest) + " and balance of source account is " + Float.toString(Transfer(account, dest, amount))); break; case 6: //bufOutput.writeBytes("exiting test\n"); System.out.println("exiting test\n"); break; default: //bufOutput.writeBytes("please input choice between 1 to 5.\n"); System.out.println("please input choice between 1 to 5."); break; } } catch (Exception er) { System.out.println(er.getMessage()); } } while (choice!=6); } protected static void autoTester() { final int MaxAmount=1000; int choice, account, dest; float amount; for (int i=0; i<3000; ++i) { try { //purely for testing //choice=0; choice=rand.nextInt(5); switch (choice) { case 0: //open System.out.println("try to open a new account"); System.out.println("a new opened account is " +Integer.toString(Open())); break; case 1: //deposit account=(rand.nextInt(BankUtility.MaxBranchNumber)+BankUtility.BranchIDOffset)*10000 +rand.nextInt(10000); amount=rand.nextInt(MaxAmount)+rand.nextInt(100)/(float)100.0; System.out.println("try to deposit account "+Integer.toString(account)+" amount of " + Float.toString(amount)); System.out.println("account "+Integer.toString(account)+" is deposited with "+Float.toString(amount) + " and new balance is "+ Float.toString(Deposit(account, amount))); break; case 2: //withdraw account=(rand.nextInt(BankUtility.MaxBranchNumber)+BankUtility.BranchIDOffset)*10000 +rand.nextInt(10000); //i want to use float with two digits after point amount=rand.nextInt(MaxAmount)+rand.nextInt(100)/(float)100.0; System.out.println("try to withdraw account "+Integer.toString(account)+" amount of " + Float.toString(amount)); System.out.println("account "+Integer.toString(account)+" is withdraw with "+Float.toString(amount) +" and new balance is " + Float.toString(Withdraw(account, amount))); break; case 3: //balance account=(rand.nextInt(BankUtility.MaxBranchNumber)+BankUtility.BranchIDOffset)*10000 +rand.nextInt(10000); System.out.println("try to get balance of account "+Integer.toString(account)); amount=Balance(account); System.out.println("account "+Integer.toString(account)+" has balance of "+Float.toString(amount)); break; case 4: account=(rand.nextInt(BankUtility.MaxBranchNumber)+BankUtility.BranchIDOffset)*10000 +rand.nextInt(10000); dest=(rand.nextInt(BankUtility.MaxBranchNumber)+BankUtility.BranchIDOffset)*10000 +rand.nextInt(10000); //i want to use float with two digits after point amount=rand.nextInt(MaxAmount)+rand.nextInt(100)/(float)100.0; System.out.println("try to transfer from account "+Integer.toString(account)+" amount of " + Float.toString(amount)+ "to account "+dest+"result="+Transfer(account, dest,amount)); break; } } catch(Exception er) { System.out.println(er.getMessage()); } } } }
file name: Bank.java (the interface)
package BankApp; /** * BankApp/Bank.java . * Generated by the IDL-to-Java compiler (portable), version "3.2" * from ./bankidl/bank.idl * Thursday, February 9, 2006 6:27:29 PM EST */ public interface Bank extends BankOperations, org.omg.CORBA.Object, org.omg.CORBA.portable.IDLEntity { } // interface Bank
file name: BankException.java (my own exception, quite simple)
package BankApp; import java.rmi.*; public class BankException extends java.rmi.RemoteException { public BankException() { super(); } public BankException(String msg) { super(msg); } }
file name: BankUtility.java (mainly to define some common parameter between client and server)
package BankApp; /* import org.omg.CosNaming.NamingContextPackage.*; import org.omg.CosNaming.*; import org.omg.CORBA.ORB.*; import org.omg.CORBA.*; */ import org.omg.CosNaming.*; import org.omg.PortableServer.*; import org.omg.PortableServer.POA; import java.util.Properties; import org.omg.CORBA.ORB.*; import org.omg.CORBA.*; public class BankUtility { public static final int BasePortNumber=25000; public static final int MaxBranchNumber=3; public static final int MaxLockTrialNumber=10; public static final float NegativeInfinite= -10000; public static final int InvalidAccountNumber=-1; public static final int BranchIDOffset=100; public static void updateInterfaces(NamingContextExt ncRef, Bank banks[]) { for (int i=0; i<MaxBranchNumber; i++) { //even if we don't find some of them, we still need to iterate all of them try { banks[i]=BankHelper.narrow(ncRef.resolve_str("Branch"+Integer.toString(i+100))); } catch(Exception er) { //of course at runtime, we should not print these message to confuse user System.out.println(er.getMessage()); } } } public static void updateHostNames(NamingContextExt ncRef, String branchHostNames[]) { BindingListHolder bl=new BindingListHolder(); BindingIteratorHolder bh=new BindingIteratorHolder(); ncRef.list(50, bl, bh); Binding bindings[]=bl.value; NameComponent nc[];//=n[0].binding_name; //for (int i=0; i<n.length; i++) for (int i=0; i<bindings.length; i++) { //System.out.println("loop for " + i); if (bindings[i].binding_type==BindingType.ncontext) { nc=bindings[i].binding_name; for (int j=0; j<nc.length; j++) { for (int k=0; k<MaxBranchNumber; k++) { if (nc[j].kind.compareTo("host"+Integer.toString(k+100))==0) { branchHostNames[k]=nc[j].id; } } } } } } }
file name: BankOperations.java (generated by idlj)
package BankApp; /** * BankApp/BankOperations.java . * Generated by the IDL-to-Java compiler (portable), version "3.2" * from ./bankidl/bank.idl * Thursday, February 9, 2006 6:27:29 PM EST */ public interface BankOperations { int Open (); float Deposit (int acnt, float amt); float Withdraw (int acnt, float amt); float Balance (int acnt); float Transfer (int src, int dest, float amount); } // interface BankOperations
file name: BankHelper.java (generated by idlj)
package BankApp; /** * BankApp/BankHelper.java . * Generated by the IDL-to-Java compiler (portable), version "3.2" * from ./bankidl/bank.idl * Thursday, February 9, 2006 6:27:29 PM EST */ abstract public class BankHelper { private static String _id = "IDL:BankApp/Bank:1.0"; public static void insert (org.omg.CORBA.Any a, BankApp.Bank that) { org.omg.CORBA.portable.OutputStream out = a.create_output_stream (); a.type (type ()); write (out, that); a.read_value (out.create_input_stream (), type ()); } public static BankApp.Bank extract (org.omg.CORBA.Any a) { return read (a.create_input_stream ()); } private static org.omg.CORBA.TypeCode __typeCode = null; synchronized public static org.omg.CORBA.TypeCode type () { if (__typeCode == null) { __typeCode = org.omg.CORBA.ORB.init ().create_interface_tc (BankApp.BankHelper.id (), "Bank"); } return __typeCode; } public static String id () { return _id; } public static BankApp.Bank read (org.omg.CORBA.portable.InputStream istream) { return narrow (istream.read_Object (_BankStub.class)); } public static void write (org.omg.CORBA.portable.OutputStream ostream, BankApp.Bank value) { ostream.write_Object ((org.omg.CORBA.Object) value); } public static BankApp.Bank narrow (org.omg.CORBA.Object obj) { if (obj == null) return null; else if (obj instanceof BankApp.Bank) return (BankApp.Bank)obj; else if (!obj._is_a (id ())) throw new org.omg.CORBA.BAD_PARAM (); else { org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate (); BankApp._BankStub stub = new BankApp._BankStub (); stub._set_delegate(delegate); return stub; } } public static BankApp.Bank unchecked_narrow (org.omg.CORBA.Object obj) { if (obj == null) return null; else if (obj instanceof BankApp.Bank) return (BankApp.Bank)obj; else { org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate (); BankApp._BankStub stub = new BankApp._BankStub (); stub._set_delegate(delegate); return stub; } } }
file name: BankHolder.java (generated by idlj)
package BankApp; /** * BankApp/BankHolder.java . * Generated by the IDL-to-Java compiler (portable), version "3.2" * from ./bankidl/bank.idl * Thursday, February 9, 2006 6:27:29 PM EST */ public final class BankHolder implements org.omg.CORBA.portable.Streamable { public BankApp.Bank value = null; public BankHolder () { } public BankHolder (BankApp.Bank initialValue) { value = initialValue; } public void _read (org.omg.CORBA.portable.InputStream i) { value = BankApp.BankHelper.read (i); } public void _write (org.omg.CORBA.portable.OutputStream o) { BankApp.BankHelper.write (o, value); } public org.omg.CORBA.TypeCode _type () { return BankApp.BankHelper.type (); } }
file name: _BankStub.java (generated by idlj)
package BankApp; /** * BankApp/_BankStub.java . * Generated by the IDL-to-Java compiler (portable), version "3.2" * from ./bankidl/bank.idl * Thursday, February 9, 2006 6:27:29 PM EST */ public class _BankStub extends org.omg.CORBA.portable.ObjectImpl implements BankApp.Bank { public int Open () { org.omg.CORBA.portable.InputStream $in = null; try { org.omg.CORBA.portable.OutputStream $out = _request ("Open", true); $in = _invoke ($out); int $result = $in.read_long (); return $result; } catch (org.omg.CORBA.portable.ApplicationException $ex) { $in = $ex.getInputStream (); String _id = $ex.getId (); throw new org.omg.CORBA.MARSHAL (_id); } catch (org.omg.CORBA.portable.RemarshalException $rm) { return Open ( ); } finally { _releaseReply ($in); } } // Open public float Deposit (int acnt, float amt) { org.omg.CORBA.portable.InputStream $in = null; try { org.omg.CORBA.portable.OutputStream $out = _request ("Deposit", true); $out.write_long (acnt); $out.write_float (amt); $in = _invoke ($out); float $result = $in.read_float (); return $result; } catch (org.omg.CORBA.portable.ApplicationException $ex) { $in = $ex.getInputStream (); String _id = $ex.getId (); throw new org.omg.CORBA.MARSHAL (_id); } catch (org.omg.CORBA.portable.RemarshalException $rm) { return Deposit (acnt, amt ); } finally { _releaseReply ($in); } } // Deposit public float Withdraw (int acnt, float amt) { org.omg.CORBA.portable.InputStream $in = null; try { org.omg.CORBA.portable.OutputStream $out = _request ("Withdraw", true); $out.write_long (acnt); $out.write_float (amt); $in = _invoke ($out); float $result = $in.read_float (); return $result; } catch (org.omg.CORBA.portable.ApplicationException $ex) { $in = $ex.getInputStream (); String _id = $ex.getId (); throw new org.omg.CORBA.MARSHAL (_id); } catch (org.omg.CORBA.portable.RemarshalException $rm) { return Withdraw (acnt, amt ); } finally { _releaseReply ($in); } } // Withdraw public float Balance (int acnt) { org.omg.CORBA.portable.InputStream $in = null; try { org.omg.CORBA.portable.OutputStream $out = _request ("Balance", true); $out.write_long (acnt); $in = _invoke ($out); float $result = $in.read_float (); return $result; } catch (org.omg.CORBA.portable.ApplicationException $ex) { $in = $ex.getInputStream (); String _id = $ex.getId (); throw new org.omg.CORBA.MARSHAL (_id); } catch (org.omg.CORBA.portable.RemarshalException $rm) { return Balance (acnt ); } finally { _releaseReply ($in); } } // Balance public float Transfer (int src, int dest, float amount) { org.omg.CORBA.portable.InputStream $in = null; try { org.omg.CORBA.portable.OutputStream $out = _request ("Transfer", true); $out.write_long (src); $out.write_long (dest); $out.write_float (amount); $in = _invoke ($out); float $result = $in.read_float (); return $result; } catch (org.omg.CORBA.portable.ApplicationException $ex) { $in = $ex.getInputStream (); String _id = $ex.getId (); throw new org.omg.CORBA.MARSHAL (_id); } catch (org.omg.CORBA.portable.RemarshalException $rm) { return Transfer (src, dest, amount ); } finally { _releaseReply ($in); } } // Transfer // Type-specific CORBA::Object operations private static String[] __ids = { "IDL:BankApp/Bank:1.0"}; public String[] _ids () { return (String[])__ids.clone (); } private void readObject (java.io.ObjectInputStream s) throws java.io.IOException { String str = s.readUTF (); String[] args = null; java.util.Properties props = null; org.omg.CORBA.Object obj = org.omg.CORBA.ORB.init (args, props).string_to_object (str); org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl) obj)._get_delegate (); _set_delegate (delegate); } private void writeObject (java.io.ObjectOutputStream s) throws java.io.IOException { String[] args = null; java.util.Properties props = null; String str = org.omg.CORBA.ORB.init (args, props).object_to_string (this); s.writeUTF (str); } } // class _BankStub
file name: BankPOA.java (generated by idlj)
package BankApp; /** * BankApp/BankPOA.java . * Generated by the IDL-to-Java compiler (portable), version "3.2" * from ./bankidl/bank.idl * Thursday, February 9, 2006 6:11:55 PM EST */ public abstract class BankPOA extends org.omg.PortableServer.Servant implements BankApp.BankOperations, org.omg.CORBA.portable.InvokeHandler { // Constructors private static java.util.Hashtable _methods = new java.util.Hashtable (); static { _methods.put ("Open", new java.lang.Integer (0)); _methods.put ("Deposit", new java.lang.Integer (1)); _methods.put ("Withdraw", new java.lang.Integer (2)); _methods.put ("Balance", new java.lang.Integer (3)); _methods.put ("Transfer", new java.lang.Integer (4)); } public org.omg.CORBA.portable.OutputStream _invoke (String $method, org.omg.CORBA.portable.InputStream in, org.omg.CORBA.portable.ResponseHandler $rh) { org.omg.CORBA.portable.OutputStream out = null; java.lang.Integer __method = (java.lang.Integer)_methods.get ($method); if (__method == null) throw new org.omg.CORBA.BAD_OPERATION (0, org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE); switch (__method.intValue ()) { case 0: // BankApp/Bank/Open { int $result = (int)0; $result = this.Open (); out = $rh.createReply(); out.write_long ($result); break; } case 1: // BankApp/Bank/Deposit { int acnt = in.read_long (); float amt = in.read_float (); float $result = (float)0; $result = this.Deposit (acnt, amt); out = $rh.createReply(); out.write_float ($result); break; } case 2: // BankApp/Bank/Withdraw { int acnt = in.read_long (); float amt = in.read_float (); float $result = (float)0; $result = this.Withdraw (acnt, amt); out = $rh.createReply(); out.write_float ($result); break; } case 3: // BankApp/Bank/Balance { int acnt = in.read_long (); float $result = (float)0; $result = this.Balance (acnt); out = $rh.createReply(); out.write_float ($result); break; } case 4: // BankApp/Bank/Transfer { int src = in.read_long (); int dest = in.read_long (); float amount = in.read_float (); float $result = (float)0; $result = this.Transfer (src, dest, amount); out = $rh.createReply(); out.write_float ($result); break; } default: throw new org.omg.CORBA.BAD_OPERATION (0, org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE); } return out; } // _invoke // Type-specific CORBA::Object operations private static String[] __ids = { "IDL:BankApp/Bank:1.0"}; public String[] _all_interfaces (org.omg.PortableServer.POA poa, byte[] objectId) { return (String[])__ids.clone (); } public Bank _this() { return BankHelper.narrow( super._this_object()); } public Bank _this(org.omg.CORBA.ORB orb) { return BankHelper.narrow( super._this_object(orb)); } } // class BankPOA
The result is like following:
How to compile?
1. first compile "idl" file by "idlj -fall Bank.idl".
2. Second compile server and client by placing server and client file into folders generated by "idlj".
javac ./BankServer/BankServer.java
javac ./BankClient/BankClient.java
How to run?
How to run orb:
start orbd -ORBInitialPort 2345
(And I presume this name server is running on a host name "argentina.encs.concordia.ca", see below explanation in server.)
How to run server?
Originally I plan to let all server to register its host name in "name server". However, the naming context is very complicated in CORBA that there is difference between "transient and persistence". I use the later one and all name context cannot be properly unbund. so, I give it up after a couple of days trial by hardcoding all host name in "BankServer". See below in "BankServer".
public static String branchHostNames[]={"chile", "uruguay", "argentina"};
So, the host name of server "Branch100", "Branch101", "Branch102" must have these three name as host name.
to run server "branch100" you have to run this server at a host name of "chile.encs.concordia.ca":
java BankServer.BankServer -ORBInitialHost argentina.encs.concordia.ca -ORBInitialPort 2345 100
to run server "branch101" you have to run this server at a host name of "uruguay.encs.concordia.ca":
java BankServer.BankServer -ORBInitialHost argentina.encs.concordia.ca -ORBInitialPort 2345 101
to run server "branch102" you have to run this server at a host name of "argentina.encs.concordia.ca"
java BankServer.BankServer -ORBInitialHost
argentina.encs.concordia.ca -ORBInitialPort 2345 102
How to run client?
java BankClient.BankClient -ORBInitialHost argentina.encs.concordia.ca -ORBInitialPort
2345