CORBA

             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.
B.The problem

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.

 

C.The idea of program
 

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.

7. Testing: As I mentioned at beginning there is a big difference between a simple toy version and a robust thread-safe one. I try to create a couple of testing methods which uses multiple threads to transfer in a circle. And in order to make realistic, I used a barrier to block all threads before all of them are created.
D.The major functions
 
.
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
 

 

 
 
				back.gif (341 bytes)       up.gif (335 bytes)         next.gif (337 bytes)