CORBA

Primary-Backup (CORBA)

    A. First Edition

    This is a huge teamwork project and actually it is really worth the name "project". Here is the link for downloading and

    please note that it is not done by my own. So, I will only publish those parts done by myself and it is only about half of

    total project. If you want to download the full documents, you have to send me an email because it is too huge to be put

    a link.

    B.The problem

Project Group B (for teams of 4)
Availability Through Primary-Backup Replication
Project 3: Highly Available CORBA DBS using Primary-Backup Replication
Project 4: Highly Available Web Services DBS using Primary-Backup Replication
In the distributed banking system built for the assignments, bank accounts (and the funds they store) at crashed branches are unavailable until the faulty host has been repaired and restarted. For this group of projects, extend your DBS implementation from Assignment 2 or 3 by adding a ¡°warm standby¡±. The ¡°warm standby¡± is a branch server that is running in the background (normally on a different machine), receiving operations from the primary server to update its state and hence ready to jump in if the primary server fails. Thus, when the primary server receives a request from a client which will change its state, it sends the request to the backup server, performs the request, receives the response from the backup server and then sends the reply back to the client. Since the primary and backup servers are usually on a local area network within a branch, they communicate using the unreliable UDP protocol. However, the communication between them should be reliable and FIFO. Specifically do the following.
• (Team) Assuming that processor failures are benign (i.e. crash failures) and not Byzantine, design your ¡°warm standby¡± replication system.
• (Student 1) Design and implement the primary server which receives requests from clients, sends the request to the backup server, performs the request, and sends the response back to the client only after the request has been completed correctly both in the primary and the backup servers. When the primary notices that the backup does not respond within a reasonable time, it assumes and informs the branch monitor that the backup has failed so that a new backup server can be created and initialized.
• (Student 2) Design and implement the backup server which receives requests from the primary, performs the request and sends the reply back to the primary. If the backup server does not receive any request from the primary for a reasonable time, it sends arequest to the primary to check if the latter is working. If the primary server does not reply in a reasonable time, the backup server assumes that the primary has failed and takes over by configuring itself as the primary so that it can receive and handle all client requests from that point onwards; and also informs the branch monitor of the switch over so that the latter can create and initialize another backup server.
• (Student 3) Design and implement a reliable FIFO communication subsystem over the unreliable UDP layer for the communication between a primary and its backup servers.
• (Student 4) Design and implement a branch monitor module which maintains the application highly available. This branch monitor initializes the primary and backup servers at the beginning, creates and initializes a backup server when the primary fails (and the original backup server takes over as the primary), and creates and initializes a backup server when the original backup server fails.
• (Team) Integrate all the modules properly, deploy your application on a local area network, and test the correct operation of your application using properly designed test runs. You may simulate a process failure by killing that process while the application is running.

C.The idea of program
¡¡
To learn more about this project, you may need to read this.

Design Choices Reliable Communication Module

a) Protocol:

This module implements a reliable, FIFO communication channel based on UDP socket. In order to ease programming work, we choose to use stop-and-wait protocol because our design analysis shows that all communications are using single packet which is very small compared with size of UDP packet. Therefore stop-and-wait protocol is enough for this purpose.

b) class and package:

owner package | class | methods | Super/Comments
BankServer | UDPSocket | doSendPacket |
doRecvPacket
UDPClientSocket | sendPacket | UDPSocket
UDPServerSocket | recvPacket | UDPSocket

c) stop-and-wait:

i) Initialize: Sender and receiver starts sequence number from 0.

ii) Sender: Sender sends out a message to receiver and expects receiver returns acknowledgement with same sequence number within a time out period. If not, sender repeats sending and listening for a certain number of trials. If failed, sender throws out an exception to application indicating receiver is down.

iii) Receiver: Receiver acknowledges whatever message it receives, but only delivers new message by comparing the sequence number in packet with its checking sequence number. After a new message is delivered, receiver increases checking sequence number by one so that it won¡¯t deliver repeated message.

iv) Connection: We adopt a concept of connection to allow one receiver to receive messages from multiple sources and one sender to send messages to multiple destinations. For each combination of host plus port number it makes a unique connection in communication. By assigning sequence number for each connection each sender can choose correct sequence number and each receiver can recognize its expected sequence number.

d) Design of class

i) class UDPSocket:

methods: doSendPacket(), doRecvPacket()

It composes two DatagramSockets. One is used for sending, the other is used for receiving. In order to ease programmer's job, I use "DataInputStream" and "DataOutputStream" as input and output stream-filter so that all type of data can use read/write method of stream. It implements basic packet receiving and sending method so that derived class can use these basic methods to fulfill transportation control logical.

ii) class UDPClientSocket

methods: sendPacket()

It inherits from UDPSocket and can be used as a client socket. It uses UDPSocket basic sending and receiving packet method to implement a sending-acknowledgement model. It sends out packet with its local port number written inside packet, so that receiver can reply acknowledgement to it. When UDPClientSocket sends out packet, it turns on "timeout" and waiting for reply. If acknowledgement doesn't reach within timeout time, it tries to send packet again. After a certain number of such failure, it throws timeout exception to indicate the receiver is down.

iii) class UDPServerSocket

methods: recvPacket()

It also inherits from UDPSocket and can be used as a server socket. Internally it has a listening thread monitoring incoming packets and a buffer to hold received packets. It acknowledges every packet it receives with the sequence number inside the received packet. Whenever it receives a packet from a new connection which means a new combination of host plus port number, it initializes the checking sequence number for this new connection to the sequence number in the received packet and delivers the packet in buffer. After it delivers the received new packet in buffer, it increases the checking sequence number for that connection by one so that it won¡¯t deliver repeated packet.

¡¡

General strategy to deal with fault and failure

Our project is an exploration of solution to deal failure in service by using a primary-backup system. A backup server is a warm standby which constantly monitors running of primary and instantly take over whenever it detects failure of primary. In this system, there are several key technology and assumptions.

1. Reliable, FIFO communication channel:

Failure detection is very difficult in network environment. Without accurate detection of failure there is no way to deal with failure. We implement a reliable, FIFO communication channel which can be used to detect the failure and guarantee delivery of necessary messages.

2. Naming service:

When failure is detected, new service must be re-supplied without awareness of client side. This requires a reliable centre to supply necessary updating information for requests from users. The concept of user here does not only necessarily mean the real user of application, but also the new service provider. For example, when a primary server is down, application may need to turn to name service for registry information of new service. However, during transfer operation source primary server may detect failure of destination primary server. Still server must use name service to find new service information from registry in name service. Besides name service is also served as information centre for deployment configuration which makes our system easy for configure when migrating to new environment.

3. Monitor:

A fault-tolerate system which can deal with failure of some subsystem does require some subsystem to be reliable. A reliable monitor system is such a subsystem which is guarantee to work even other subsystem is down. However, our monitor system is a unique design because it relies on the system level, namely CORBA system, which is foundation of whole project. In CORBA architecture a persistent server is guarantee to work against failure and our monitor system is built on the basis of persistent server which automatically reload any failed monitor. What's more, in order to increase reliability of monitor system, we also apply the concept of group monitor system in which more than one monitors work together, watch each other and form as one single reliable monitor. In this group monitor, any failure of a particular monitor will be detected by other members of group and be restarted while all other working monitors still function properly. This design is an add-on insurance for detection failure apart from mutual detection between primary and backup. That is to say, even the requirement of project only asks the primary and backup servers watch each other for possible failure, our monitor system also watches both primary and backup server constantly. This design gives a possible upgrading in future when we don't have to limit ourselves with "single failure".

4. Logging system and recovery from failure

In a reliable system, recovery is essential for robustness of system. Our system uses log to store all information for recovery when system failure happens. Generally there are two scheme for logging strategy, redo logging or undo logging. We decide to apply redo log to our system because we observe that each operation in system is a single compete transaction which can be used as check point. Therefore system writes down whatever updates in system states whenever update is completed. By doing so, system is rest assured that all recorded updates are indeed done to system. During state transfer when recovery, primary server only needs to transfer all cached log records to recovered backup to synchronize states between each other. For example, the contents of log record include following information, operation ID, operation type, account number, new value etc. The recover server only needs to check if operation ID of log record. If no such ID is found, server simply update the account to new value.

Design of Database

a) In order to speed up searching and simplify synchronization, I store data of one branch in one file. i.e. there are ten branches in my design and there are ten database files for them.

b) Usually we store each record linearly in database file. However, unless we implement an index file there is no way for fast access to records in searching. Therefore, I used a RandomAccessFile object to speed up searching. This comes from the observation of a simple fact that there are 0 to 9999 account number in each branch. Therefore I store the balance of each account in file like a big array. Initially the balance for each account is initialized to -1 to indicate unopened account. When opening a new account, my file pointer uses "seek" method to move to offset of accountNumber*SizeOfAccount from beginning of file. Then write a new value of 0 to the balance of that account. So, by wasting a little space, my database file can achieve highest searching performance. Of course there is some overhead to initialize all accounts when database starts its first run but it will only do it once.

c) Since file opening operation is very expensive, my database class allocate each branch file with one RandomAccessFile object to handle file operations.

Design of Deadlock-Free Transfer model

1. The Algorithms of Transfer

a) In server side, when a ¡°Transfer¡± is requested, it first analysis if the source and destination branch are located in same host. If yes, it uses a local transfer function which is much faster than remote transfer. If not, it calls remote transfer function.

b) Local Transfer:

i) Try to lock both source account and destination account. If failed, return failure to client.

ii) Check if both account are valid account and source account has balance bigger than or equal to requested amount, if no, return failure to client.

iii) Minus amount from balance of source and add amount to balance of destination. Return success to client.

c) Remote Transfer

i) First lock source account by file lock. If failed, return failure to client. If succeed, check if the account is a valid account and the balance is bigger than or equal to amount. If no, return failure to client. If yes, send out a remote request to destination server. If replies from remote server is success, it minus amount from source account and return success to client. Otherwise return failure to client.

ii) If remote server successfully receives packet, it tries to lock the destination account and checks if destination account is valid. If no, reply failure to local server. If yes, add amount into account and return success to local server.

2. The Mutual Exclusion and Prevention of Deadlock

a) The mutual exclusion is achieved by using file lock which prevents from two simultaneous accesses. For example, whenever a file access is going to proceed, it must first acquire file lock to the part of file. (Using account as offset from beginning of file and call "tryLock(start_offset, size, toShare)") It permits several times failure during acquiring file lock.

However, java file lock is mandatory. So even the owner of file lock has to release file lock before file access. Therefore, it is possible that another thread may get file lock after owner releases file lock. So, I have to use a mutex to synchronize file locking. That is, any thread needs acquire mutex before trying to acquiring file lock.

b) Using Non-blocking Locking to Prevent Deadlock

Locking file lock can be either blocking or non-blocking. Blocking method may cause deadlock. For example, two transfers are invoked such that thread A wants to transfer from account a1 to a2 while thread B wants to transfer from a2 to a1. Both will first lock source account a1 and a2 respectively. Then send request to destination server to try to lock a2 and a1 respectively. If blocking method are used, there will be a dead lock. So, by using non-blocking ¡°file-lock¡± method my server won¡¯t be dead locked. This algorithms is suitable for more than two participants. In general, it is completely thread-safe and deadlock-free.

The Design of Fault-Tolerance

The design of fault-tolerance system actually involves two big aspects. One is a seamless takeover by a warm backup when failure happens at primary server so that client will in fact be transparent to this takeover happened behind scene. The other aspect is how the failed primary server later joins the primary-backup server and synchronizes its data state so that the system has new backup to tolerate future failure in primary server. The following is an analysis of design of seamless takeover and recovery from failure.

1. The Design of Seamless Takeover

The seamless takeover actually is achieved by an observation that the primary and backup server are working in a lock step mode such that the MAXIMUM DIFFERENCE states of data between them is within the last client's request. And what's more, the detection of failure must also be within this last client's request.

a) Detection of Failure Is within Last Client's Request

First the backup server is able to detect failure of primary server within last client's request. This comes from the observation of two assumption in requirement. One is that the client will repeatedly send its operation request after noticing failure of primary server until backup server take over and replies RPC request. This request-locking model can also be found during transfer operation. i.e. When source primary server discovers failure in remote primary server during transfer operation, it will wait until new primary takes over and replies transfer request. The other one is that the lock step working mode between primary and backup server. That is to say, whatever request primary server receives from client, it will only reply to client after the request is sent to backup server. These assumptions ensures that whenever primary server crashes the MAXIMUM DIFFERENCE between their data states and log contents are within the last operation request. This observation ensures that there can be a seamless takeover for backup server after detecting failure of primary server.

b) The Analysis of State of Data between Primary and Backup Server

First please note the same observation of assumption in requirement in a). These assumption ensures that the MAXIMUM DIFFERENCE of data states between failed primary server and jumped-in backup server at the moment of failure is within the client's last request. Let's enumerate all possible failure timing point of primary server during transfer operation to explain. And we choose transfer operation because it is most complicated operation and includes all scenario of all other operations like withdraw, deposit and open. And please note the sequence of all involved events are like this 1) update in remote 2) update in local 3) request is sent to backup 4) log is written in local 5) reply to client.

i) before 1)

Since the data is not yet updated in primary server, the states of primary and backup server are exactly same. So are the contents of their log contents. Even failure happens at this moment, there is no need for state transfer during recovery. And this is reflected in log because there is no log records yet for this transfer operation.

ii) after 1) before 2)

This situation is actually the same as case i) if we don't take remote primary server into consideration. If we assume there is no double failure among primary and backup server pair, then when backup server jumping in as new primary server, it will continue current transfer by receiving request from client which is still waiting for reply after noticing failure of previous primary.

iii) after 2) before 3)

The data state between primary server and backup server is different because request is not sent to backup server yet. However, please note that there is no log contents both in primary and backup server to reflect this difference. If failure happens at this moment and later backup server upgrades to be primary server, it will continue to receive requests from client which repeats request after previous primary failure. So, primary server and backup server only have difference between this transfer operation before primary server is done.

iv) after 3) before 4)

Although the data state is the same in primary and backup server, yet the contents of logs between them are different. However, the difference is still within this transaction.

v) after 4) before 5)

The state of data and contents of log between primary and backup are exactly same.

2. The design of logging system and recovery algorithm

The design of logging system and recovery algorithm are closely connected and they also depends on the transaction model for transfer operation. First let me explain our transaction model for transfer operation. The transfer operation requires that the local primary server first check local data to ensure transfer operation is possible then updates its data state only after remote primary server updates its data state successfully. And The design of logging system involves decisions of when and what to be written into log.

a. When to write into log:

Generally speaking, there are two logging schemes to choose. One way is to write down into log BEFORE data update is done to system. The other way is to write down into log only AFTER data update is done to system. Even though both scheme can achieve goal of recovery in theory, still we choose the second one because it has less requirements, less complexity of recovery algorithm. Please observe that the later scheme actually requires a certain degree of system deterministic which means whatever is recorded in log can be done exactly before failure when log contents are replayed. The remote transfer operation increases complexity of analysis, especially considering multi-thread environment. What's more, during recovery the backup server must resolve if the data update has been done to system by comparing between data and contents in log. Therefore we choose to apply the log scheme of writing down into log only after data update is completed.

b. What to write into log:

The contents of log is greatly depended on the decision of when log is written. Since we choose to write down into log after data update is complete, the choice of contents of log is very clear. They are just the current description of state of system. In other words, the contents of log includes such information like operation ID, account number, the new value of balance and some other optional information like operation type etc..

c. The Design of Recovery Algorithm

The design of recovery algorithms are also greatly depended on the decision of logging scheme. After we choose to write down into log after data update is completed, the recovery algorithm is very simple. When backup server request to join with primary server either for the first time or after it crashed as previous primary, the current primary server simply needs to transfer all log records to backup server up to last check point or the last record before it crashed. The backup server only needs to update its data to whatever is in log record without any analysis between actual data and contents in log because all information in log record are those data update are actually completed in primary server. Therefore after the process of log transfer, the data state of backup server becomes same as primary server.

    D.The major functions

Class BankImpl: BankImpl is nothing but inherited from CORBA POA class and these above methods simply call BranchManager¡¯s corresponding methods.
Methods | Functionality
Open() | simply call BranchManager¡¯s corresponding methods.
Deposit() | simply call BranchManager¡¯s corresponding methods.
Withdraw() | simply call BranchManager¡¯s corresponding methods.
Balance() | simply call BranchManager¡¯s corresponding methods.
Transfer() | simply call BranchManager¡¯s corresponding methods.

BankServer. BranchManager:

Class BranchManager: implements primary server and it is used by BankImpl class.
Methods | Functionality

updateInformation()

This method is to update all server configure information of all active primary server and current backup server from Naming Service of CORBA. The information may include host name, UDP port number, branch index etc. It is called by initialization of BranchManager and also when UDP socket and TCP socket fail to connect with remote host.

startNewBackup(); | It first changes server mode from NORMAL to SINGLE and notify monitor to start up a new backup. It is called whenever primary server detects failure of backup server.
Open(); | It actually random searches in database for a new account. A new account is simply an account with balance initialized to be constant NegativeInfinite. After found a new account, it initialize balance of this account to 0. Before return, it sends request to backup if server mode is in NORMAL. After that it writes down operation result into log. Then it returns the new account.
Deposit() | It checks if account is a valid account by checking if balance is -1. If yes, returns constant NegativeInfinite. If no, after adding amount to balance of the account, if server mode is in NORMAL, it sends request to backup server and also writes result into log. Then it returns new balance.
Withdraw () | Similar to ¡°Deposit¡± except that it deducts amount from balance and returns the new balance.
Balance() | Returns balance.

localTransfer()

A fast transfer operation when both source and destination accounts locates in same host.

It first tries to lock both accounts and checks if both accounts are valid accounts and balance of source account is equal to or bigger than amt. If no, returns failure. If yes, deducts amt from source account and adds amt to destination account and returns new balance of source account.

remoteTransfer()

It first tries to lock source account and check if account is valid account and has balance equal to or bigger than amount. If no, returns failure. If yes, it sends transfer request to destination branch by TCP connection. As remote branch may be down, it needs to catch connection failure exception and call function ¡°updateInformation¡± to update remote host information and gives another try until success. If the remote branch replies success, it deducts amount from source account and returns new balance of source account. Otherwise returns failure. Please note that the remote transfer may block if remote host is down, therefore it releases semaphore after it successfully checks balance of source account so that other threads can try other operations. However, before releasing semaphore it needs to lock again source account after checking balance so that source account won¡¯t be touched by other operation. When remote branch replies success, it needs acquire semaphore again to update source account. By doing this, it won¡¯t block other operation during this function call.

lockFile()

This is called whenever an operation needs to access an account. It first try to acquire semaphore before call ¡°tryLock¡±. The function ¡°tryLock¡± is not a blocking function and returns exception if account is actually locked by others. In this case, it release semaphore and returns null to indicate failure of file locking. User doesn¡¯t have to release semaphore. If ¡°tryLock¡± succeeds, it won¡¯t release semaphore because user will definitely need to hold semaphore. This is because ¡°file lock¡± in java is mandatory which means that even the holder of file lock cannot access file unless it releases file lock. But if file lock is released, it is possible that other threads may accidentally again lock file. Therefore whoever needs accessing file must use semaphore to protect after it releases file lock. In the case that the return of this function is not null, the caller of this function acquires file lock and is responsible to release semaphore.

Transfer() | It first checks if source and destination account is located in same branch. If yes, it calls localTransfer which is much faster than remoteTransfer. In case of success of either of them, it returns new balance of source account.

backupPacket()

It writes log record into local log only after it successfully sends it to backup server if server mode is NORMAL. The sequence is important because during sending packet to backup it may setup check point if it notices failure of backup server. This check point setting must occur before new log record is written into primary server¡¯s log.

BackupListener()

The listening thread of primary server which is responsible for transferring cached log records to backup server when received backup server¡¯s ¡°join¡± request. It first setup server mode flag to be ¡°BACKUPJOIN¡± so that new requests will hold on until transferring process finishes. For those operations which already starts, they can continue their operation except that they cannot write down result into logs. This is achieved by using semaphore protection.

Class PrimaryListener: The TCP socket listening thread of primary server which is responsible for accepting remote transfer request.
Methods | Functionality
run() | It checks if destination account is valid account. If yes, then adds amount to balance of the account and sends log record to backup server when server mode is NORMAL before writing down into local log and returns success to sender. If not, it returns failure to sender.

Class BackupListener: The listening thread of primary server which is responsible for transferring cached log records to backup server when received backup server¡¯s ¡°join¡± request.
Methods | Functionality
run() | It first setup server mode flag to be ¡°BACKUPJOIN¡± so that new requests will hold on until transferring process finishes. For those operations which already starts, they can continue their operation except that they cannot write down result into logs. This is achieved by using semaphore protection.

BankServer.UDPSocket

Class UDPSocket: It composes two DatagramSockets. One is used for sending, the other is used for receiving. In order to ease programmer's job, I use "DataInputStream" and "DataOutputStream" as input and output stream-filter so that all type of data can use read/write method of stream. It implements basic packet receiving and sending method so that derived class can use these basic methods to fulfill transportation control logical.
Methods | Functionality
doSendPacket(), | It simply sends one packet.
doRecvPacket() | It simply receives one packet.

BankServer.UDPClientSocket

Class UDPClientSocket: It inherits from UDPSocket and can be used as a client socket. It uses UDPSocket basic sending and receiving packet method to implement a sending-acknowledgement model. It sends out packet with its local port number written inside packet, so that receiver can reply acknowledgement to it. When UDPClientSocket sends out packet, it turns on "timeout" and waiting for reply. If acknowledgement doesn't reach within timeout time, it tries to send packet again. After a certain number of such failure, it throws timeout exception to indicate the receiver is down.
Methods | Functionality
sendPacket(), | send one packet and wait for acknowledgement.

BankServer.UDPServerSocket

Class UDPServerSocket: It also inherits from UDPSocket and can be used as a server socket. Internally it has a listening thread monitoring incoming packets and a buffer to hold received packets. It acknowledges every packet it receives with the sequence number inside the received packet. Whenever it receives a packet from a new connection which means a new combination of host plus port number, it initializes the checking sequence number for this new connection to the sequence number in the received packet and delivers the packet in buffer. After it delivers the received new packet in buffer, it increases the checking sequence number for that connection by one so that it won¡¯t deliver repeated packet.
Methods | Functionality
recvPacket(), | receives one packet and acknowledges it.

BankApp.LogManager

Class LogManager:

This is a very simple manager class of logs. Our log is essentially a random access file with fixed size of record and each record is just same packet of UDP socket communication. At the beginning of file, there is two long number which tells the number of total records and the position of check point which is just the position of new records. In order to ease reading from and writing to stream of different data type, a stream filter class ¡°DataInputStream¡± and ¡°DataOutputStream¡± is used.

Methods | Functionality
setCheckPoint(); | It simply updates check point number to the number of current record.
findRecord() | It searches log for a request ID until end of log. If matching record is found, it returns the record. Otherwise it throws out exception to indicate end of file reached.
readPacket() | It reads in next record until end of file.
writePacket() | It appends record at end of file.

BankApp. PacketWrapper

Class PacketWrapper: A class wrapping all reading from and writing to stream of different data types. It takes advantage that both reading from and writing to socket and file are the same as operation over stream.
Methods | Functionality
readPacket (); | stream r/w of log record from either socket or file
writePacket () | stream r/w of log record from either socket or file

BankApp. Packet

Class Packet: This is a simple structure which will be both used in socket communication and log record in logs. The field tag is used as reply port number in UDP socket communication.
Methods | Functionality

BankClient.BankClient

Class BankClient: A testing client which has a series of testing cases.
Methods | Functionality
getInterface (); | Acquire CORBA object reference from account number. This must be called whenever RPC is called so that client can receive most updated object reference.
getOpID() | First get a global unique ID which is guarantee to be unique among all servers and then combine it with a local sequence number so that the combination is guaranteed to be unique among all primary servers.

    ¡¡

    E.Further improvement

    There is a known bug that when you shut down backup server, sometimes there is a mistake in its data. Probably it is somewhere

    one log record is not properly handled when backup joins with primary server again.

    F.File listing

    ¡¡

    (Perhaps this is an exception that I don't publish all source files here. One reason is that there are just

    too many. The other one is that some of them is not mine. However, here is the link for download. )

    IDL file: Bank.idl

    ¡¡

    Package BankServer

    1. BankImpl.java

    2. UDPSocket.java

    3. UDPClientSocket.java

    4. UDPServerSocket.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)

    9. Packet.java

    10.PacketWrapper.java

    11.LogManager.java

    Package ServerConfig

    (This package are all done by H.M. and you need to download from above link.)

    ¡¡

    file name: Bank.idl

    module BankApp
    {
    	interface Bank
    	{
    		long  Open(in string opID);

    		float Deposit(in string opID, in long acnt, in float amt);

    		float Withdraw(in string opID, in long acnt, in float amt);

    		float Balance(in string opID, in long acnt);

    		float Transfer(in string opID, in long src, in long dest, in float amount);

    	};
    };

    ¡¡

    file name: BankImpl.java

    package BankServer;

    import ServerConfig.*;
    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;

    public class BankImpl extends BankPOA {

    	public static ServerConfig localConfig = null; // The config of this server

    	public boolean running = true; // False to notify outter wrapper to quit

    	public boolean startFailed = true; // True to notify outter wrapper to quit

    	public static UDPServerSocket UDPServer = null; // The socket to listen request

    	public static ServerSocket transferServer = null; // The socket to accept transfer

    	private ORB orb = null;

    	//public String branchHostNames[];// ={"chile", "uruguay", "argentina"};
    	//public static String branchHostNames[];//={"lynen", "lwoff"};

    	public static int myBranchNo = -1;

    	public static String myHostName = null;

    	public BranchManager branchManager = null;

    	public BankImpl(ServerConfig sc) throws Exception {
    		localConfig = sc;
    		myBranchNo = Integer.parseInt(sc.branchID);
    		branchManager = new BranchManager(sc);
    		UDPServer = branchManager.backupServer;
    		transferServer = branchManager.serverSocket;
    	}

    	public void finalize() throws Exception {
    		branchManager.finalize();
    	}

    	public void setORB(ORB orb_val) {
    		orb = orb_val;
    		branchManager.orb = orb;
    	}

    	public int Open(String opID) //throws java.rmi.RemoteException
    	{

    		// Debug
    		BankUtility.showDebugInfo(1, "Open operation, opID=" + opID);

    		int result = -1;
    		try {
    			result = branchManager.Open(opID);
    		}
    		catch (Exception er) {
    			System.out.println(er.getMessage());
    		}

    		// Debug
    		BankUtility.showDebugInfo(1, "Open operation returned with " + result);

    		return result;
    	}

    	public float Deposit(String opID, int acnt, float amt) //throws java.rmi.RemoteException
    	{

    		// Debug
    		BankUtility.showDebugInfo(1, "Deposit operation, opID=" + opID + ", acnt=" + acnt + ", amt=" + amt);

    		float result = BankUtility.NegativeInfinite;
    		try {
    			result = branchManager.Deposit(opID, acnt, amt);
    		}
    		catch (Exception er) {
    			System.out.println(er.getMessage());
    		}
    		finally {

    			// Debug
    			BankUtility.showDebugInfo(1, "Deposit operation returned with " + result);

    			return result;
    		}
    	}

    	public float Withdraw(String opID, int acnt, float amt) //throws java.rmi.RemoteException
    	{

    		// Debug
    		BankUtility.showDebugInfo(1, "Withdraw operation, opID=" + opID + ", acnt=" + acnt + ", amt=" + amt);

    		float result = BankUtility.NegativeInfinite;
    		try {
    			result = branchManager.Withdraw(opID, acnt, amt);
    		}
    		catch (Exception er) {
    			System.out.println(er.getMessage());
    		}
    		finally {

    			// Debug
    			BankUtility.showDebugInfo(1, "Withdraw operation returned with " + result);

    			return result;
    		}
    	}

    	public float Balance(String opID, int acnt) //throws java.rmi.RemoteException
    	{

    		// Debug
    		BankUtility.showDebugInfo(1, "Balance operation, opID=" + opID + ", acnt=" + acnt);

    		float result = BankUtility.NegativeInfinite;
    		try {
    			result = branchManager.Balance(opID, acnt);
    		}
    		catch (Exception er) {
    			System.out.println(er.getMessage());
    		}
    		finally {

    			// Debug
    			BankUtility.showDebugInfo(1, "Balance operation returned with " + result);

    			return result;
    		}
    	}

    	public float Transfer(String opID, int src, int dest, float amt) //throws java.rmi.RemoteException
    	{

    		// Debug
    		BankUtility.showDebugInfo(1, "Transfer operation, opID=" + opID + ", src=" + src + ", dest=" + dest + ", amt=" + amt);

    		float result = BankUtility.NegativeInfinite;
    		try {
    			result = branchManager.Transfer(opID, src, dest, amt);
    		}
    		catch (Exception er) {
    			System.out.println(er.getMessage());
    		}
    		finally {

    			// Debug
    			BankUtility.showDebugInfo(1, "Transfer operation returned with " + result);

    			return result;
    		}
    	}

    } //end class

    class BranchManager extends java.lang.Object {
    	public static ORB orb = null;

    	//these are all static member which means only one copy is maintained
    	public static final int AccountSize = BankUtility.ACCOUNT_SIZE;

    	protected static Random rand = null;

    	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;

    	///////////////////////////////////////////////////////////////////////////////
    	protected static Semaphore available = null;

    	//these are non-static member, the constructor and finalize should treat them differently
    	protected int branchID = -1;

    	//////////////////////////////////////////////////
    	//to huang ming: this is the TCP socket
    	public ServerSocket serverSocket = null;

    	public static RandomAccessFile branchFile = null;//, branchReader;

    	protected RandomAccessFile rpcFiler = null, threadFiler = null;

    	//////////////////////////////////////////////////////
    	//mar. 26

    	//two threads for listening
    	protected static PrimaryListener primaryListener = null;

    	protected static BackupListener backupListener = null;

    	public static int serverMode = -1;

    	protected static String backupHostName = null; //must be initialized

    	protected static String myHostName = null;

    	protected static int myBranchIndex = -1;

    	public static String branchHostNames[] = null;

    	public static int branchPortNumbers[] = null;

    	protected static LogManager log = null;

    	//protected static UDPClientSocket backupClient, primaryClients[];
    	//protected static UDPServerSocket backupServer, primaryServer;

    	///////////////////////////////////////////////////////
    	//to huangming:
    	//this is the socket you want
    	public static UDPClientSocket backupClient = null;

    	public static UDPServerSocket backupServer = null;

    	//////////////////////////////////////////////////
    	//the TCP port number for transferring
    	protected static int primaryTransferPortNumber = -1;

    	///////////////////////////////////////////////
    	//this is the portnumber in backup for transfering log
    	protected static int backupUDPPortNumber = -1;

    	///////////////////////////////////////////////////////////////////////////
    	//this is the portnumber for listening to request from backup
    	protected static int localUDPPortNumber = -1;

    	//////////////////////////////////////////////////////////////////////////////

    	//protected static int transientCounter=0;

    	protected static PacketWrapper packetWrapper = new PacketWrapper();

    	protected static Packet packet = new Packet();

    	protected static Semaphore modeSemaphore = null;

    	protected static String myDataFileName = null, myLogFileName = null;

    	protected boolean threadStop = false;

    	///////////////////////////////////////////////////////////////////////////////////////////

    	protected ServerConfig localConfig = null;

    	public BranchManager(ServerConfig sc) throws Exception {
    		localConfig = sc;
    		branchID = Integer.parseInt(sc.branchID);

    		//String fileName=Integer.toString(branchID)+".data";

    		initialize(); //will call update and initialize all data

    		backupServer = new UDPServerSocket(BranchManager.localUDPPortNumber);
    		serverSocket = new ServerSocket(BranchManager.primaryTransferPortNumber);

    		try {
    			//non-static member
    			rpcFiler = new RandomAccessFile(myDataFileName, "rw");
    			threadFiler = new RandomAccessFile(myDataFileName, "rw");

    			//backupSocket=new UDPClientSocket();
    			//the following are all static global variables
    			if (flag) {
    				flag = false;
    				//log=new LogManager(Integer.toString(branchID));
    				log = new LogManager(myLogFileName);

    				Date date = new Date();
    				rand = new Random(date.getTime());
    				available = new Semaphore(MAX_AVAILABLE, true);

    				branchFile = new RandomAccessFile(myDataFileName, "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

    				//this method should be implemented to initialize all group information
    				//!!!!!!!!!!this must be called before listening thread because
    				//portnumber need to be acquired

    				////////////////////////
    				/*
    				 primaryClients=new UDPClientSocket[BankUtility.MaxBranchNumber];
    				 for (int i=0; i<BankUtility.MaxBranchNumber; i++)
    				 {
    				 primaryClients[i]=new UDPClientSocket();
    				 }
    				 */
    				modeSemaphore = new Semaphore(MAX_AVAILABLE, true);
    				primaryListener = new PrimaryListener();
    				backupListener = new BackupListener();

    				primaryListener.start();
    				backupListener.start();
    				///////////////////////////////////////////////////////////
    			}
    		}
    		catch (Exception er) {
    			System.out.println("BranchManager constructor error of " + er.getMessage());
    			throw (er);
    		}
    	}

    	////////////////////////////////////////////////////////////////////////////////////
    	/////!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    	//this method should be called every time there is a timeout
    	//////////////!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    	public synchronized void updateInformation() {
    		ServerConfig sc = localConfig;
    		String serverList[] = new String[0];
    		try {
    			serverList = ServerConfig.listServers(sc.rootContext);
    		}
    		catch (Exception ex) {
    		}

    		for (int i = 0; i < BankUtility.MaxBranchNumber; i++) {
    			branchHostNames[i] = null;
    			branchPortNumbers[i] = -1;
    		}
    		// Load those servers one by one
    		for (int i = 0; i < serverList.length; i++) {
    			// Retrieve current branchID and serverID
    			String branchID = serverList[i].substring(6, 9);
    			String serverID = serverList[i].substring(16, serverList[i].length());

    			// Update the server list
    			try {
    				// Get the server configure
    				ServerConfig server = ServerConfig.getServerConfig(sc.rootContext, branchID, true);
    				branchHostNames[Integer.parseInt(branchID)] = server.localhostIP;
    				branchPortNumbers[Integer.parseInt(branchID)] = Integer.parseInt(server.transferPort);
    			}
    			catch (Exception e) {
    			}
    		}

    		try {
    			this.primaryTransferPortNumber = Integer.parseInt(sc.transferPort);
    			this.localUDPPortNumber = Integer.parseInt(sc.UDPPort);
    			myDataFileName = sc.dataFileName;
    			myLogFileName = sc.logFileName;

    			String otherServerID = localConfig.serverID.compareTo("1")==0?"2":"1";
    			ServerConfig backupConfig = ServerConfig.getServerConfig(sc.rootContext, sc.branchID, otherServerID);
    			this.backupUDPPortNumber = Integer.parseInt(backupConfig.backupUDPPort);
    			this.backupHostName = backupConfig.localhostIP;

    		}
    		catch (Exception e) {

    		}

    		/*
    		 String theBranchID;
    		 for (int i=0; i<BankUtility.MaxBranchNumber; i++)
    		 {
    		 try
    		 {
    		 theBranchID="branch"+100+i;
    		 ServerConfig result=sc.getServerConfig(sc.rootContext, theBranchID, true);

    		 branchHostNames[i]=InetAddress.getAllByName(result.localhostIP).getHostName();
    		 branchPortNumbers[i]=Integer.parseInt(result.transferPort);
    		 if (myBranchIndex==i)
    		 {
    		 primaryPortNumber=result.Integer.parseInt(result.transferPort);
    		 backupPortNumber=result.UDPPort;
    		 myDataFileName=result.dataFileName;
    		 myLogFileName=result.logFileName;
    		 result=sc.getServerConfig(sc.rootContext, theBranchID, false);
    		 backupTransferPort=result.UDPPort;
    		 backupHostName=InetAddress.getAllByName(result.localhostIP).getHostName();
    		 }

    		 }
    		 catch (Exception er)
    		 {
    		 System.out.println("branch "+ branchID +" is not accessible");
    		 }
    		 }
    		 */
    	}

    	///////////////////////////////////////////////////////////////////////////////

    	protected void initialize() {
    		try {
    			branchHostNames = new String[BankUtility.MaxBranchNumber];
    			branchPortNumbers = new int[BankUtility.MaxBranchNumber];

    			myBranchIndex = Integer.parseInt(localConfig.branchID);

    			myHostName = localConfig.localhostIP;

    			//ALWAYS STARTS WITH THIS MODE
    			serverMode = BankUtility.SINGLE;
    			// Debug
    			BankUtility.showDebugInfo(1, "Server running in SINGLE mode.");

    			//now call get server config
    			updateInformation();

    		}
    		catch (Exception er) {
    			System.out.println(er.getMessage());
    		}

    	}

    	///////////////////////////////////////////////////////////////////////////////////////

    	protected void finalize() throws java.rmi.RemoteException {
    		try {
    			if (serverSocket != null)
    				serverSocket.close();//maybe it is better to stop thread then close socket.
    			if (backupServer != null)
    				backupServer.close();//maybe it is better to stop thread then close socket.
    			if (branchFile != null) {
    				branchFile.close();
    				branchFile = null;
    			}
    			threadStop = true;
    			if (rpcFiler != null)
    				rpcFiler.close();
    			if (threadFiler != null)
    				threadFiler.close();

    			////////////////////////////////////////////
    			primaryListener.join();
    			backupListener.join();
    			/////////////////////////////////////////////
    		}
    		catch (Exception er) {
    			throw new BankException("finalize error of " + er.getMessage());
    		}
    	}

    	//////////////////////////////////////////////////////////////////////
    	//this will be called by every method
    	/*
    	void modeCheck() {
    		//////////////////////////////////////////////////
    		try {
    			while (serverMode == BankUtility.BACKUPJOIN) {
    				Thread.sleep(BankUtility.WaitingLength);
    			}
    		}
    		catch (Exception er) {
    			System.out.println(er.getMessage());
    		}
    	}
    	*/

    	Packet secureCheckPacket(String opID)
    	{
    		boolean done=false;
    		Packet packet=null;
    		while (!done)
    		{
    			try
    			{
    				modeSemaphore.acquire();
    				if (serverMode == BankUtility.BACKUPJOIN)
    				{
    					//modeSemaphore.release();
    					Thread.sleep(BankUtility.WaitingLength);
    				}
    				else
    				{
    					packet=log.findRecord(opID);
    					//modeSemaphore.release();
    					done=true;
    				}
    			}
    			catch (Exception er)
    			{
    				System.out.println(er.getMessage());
    			}
    			modeSemaphore.release();
    		}
    		return packet;
    	}

    	void modeCheck()
    	{
    		boolean done=false;

    		while (!done)
    		{
    			try
    			{
    				modeSemaphore.acquire();
    				if (serverMode == BankUtility.BACKUPJOIN)
    				{
    					modeSemaphore.release();
    					Thread.sleep(BankUtility.WaitingLength);
    				}
    				else
    				{
    					modeSemaphore.release();
    					done=true;
    				}
    			}
    			catch (Exception er)
    			{
    				System.out.println(er.getMessage());
    			}
    		}
    	}

    	synchronized void startNewBackup() {
    		try {
    			// kh2, should not set the new check point, check point should be set only when the process totally succeeded
    			// log.setCheckPoint();//set up a check point

    			//change to server mode
    			//////////////////////////////////////////////////////////////////////////////////////
    			// to do
    			/////////////////////////////////////////////////////////////////////////////////
    			//notify monitor
    			modeSemaphore.acquire();
    			serverMode = BankUtility.SINGLE;
    			// Debug
    			BankUtility.showDebugInfo(1, "Can not contact with backup server, server changed to SINGLE mode.");
    			modeSemaphore.release();
    			//to do
    			//call monitor to start a new backup and then

    			// Notify monitor to load backup server
    			ServerConfig.notifyMonitor(localConfig.rootContext, localConfig.branchID, false);

    			updateInformation();//if necessary
    		}
    		catch (Exception er) {
    			System.out.println(er.getMessage());
    		}
    	}

    	void backupPacket(Packet packet) {
    		//this is necessary if server is between state transfer
    		try {
    			modeCheck();

    			modeSemaphore.acquire();
    			//we only send backup log when it is in normal state

    			if (serverMode == BankUtility.NORMAL)
    			{

    				try {
    					// Debug
    					//BankUtility.showDebugInfo(0, backupHostName + ":" + this.backupUDPPortNumber, packet);

    					if(backupClient==null){
    						this.backupClient = new UDPClientSocket();
    					}
    						backupClient.sendPacket(backupHostName, this.backupUDPPortNumber, packet);
    				}
    				catch (Exception er) {
    					if (er instanceof SocketException)
    					{
    						// Sending log packet failed, set the serverMode and relaunch a backup server
    						modeSemaphore.release();
    						startNewBackup();//should change mode, notify monitor that backup is down etc.
    						modeSemaphore.acquire();
    					}
    					else {
    						System.out.println("unknown error of backup packet" + er.getMessage());
    					}
    				}
    			}
    			log.writePacket(packet);
    			if (serverMode == BankUtility.NORMAL)
    			{
    				log.setCheckPoint();
    			}

    		}
    		catch (Exception er) {
    			System.out.println(er.getMessage());
    		}
    		finally{
    			modeSemaphore.release();
    		}

    		//we must notify if we are ready for state transfer
    		//THE REASON i DEFINE AN EXTRA STATE IS FOR SYNCHRONIZATION BETWEEN DIFFERENT THREAD
    		//BECAUSE it is possible that when backup send join request while some of our transaction is still
    		//going on

    	}

    	////////////////////////////////////////////////////////////////////////

    	//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 synchronized int Open(String opID) throws java.rmi.RemoteException
    	{
    		/////////////////////////////////////////////////////////////////////////
    		//we must wait for backup
    		Packet packet=null;
    		int account = BankUtility.InvalidAccountNumber;
    		boolean result = false;
    		FileLock fileLock = null;

    		////////////////////////////////////////////////////////////
    		//because log access must be protected, i changed a little bit
    		//modeCheck();
    		packet=secureCheckPacket(opID);
    		if (packet!=null)
    		{
    			return packet.source;
    		}

    		/////////////////////////////////////////////////////////////////////////

    		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);
    					// Check if this account is opened
    					if (rpcFiler.readFloat() == BankUtility.NegativeInfinite) {

    						// Write to local file
    						rpcFiler.seek(account * AccountSize);
    						rpcFiler.writeFloat(0);
    						result = true;

    						// Write the log in local file and backup it on remote backup server
    						packet = new Packet();
    						packet.opID = opID;
    						packet.opType = Packet.OPEN;
    						packet.source = Integer.parseInt(localConfig.branchID)*10000+account;
    						packet.dest = -1;//ignored
    						packet.oldValue = BankUtility.NegativeInfinite;
    						packet.newValue = 0;//is initialized to 0
    						packet.tag = -1;//should be ignored
    						packet.status=BankUtility.SUCCESS;//Indicate the succeeded of writting log

    						backupPacket(packet);

    						// Operation succeeded, set checkpoint
    						//if(serverMode==BankUtility.NORMAL) log.setCheckPoint();

    						account += Integer.parseInt(localConfig.branchID)*10000;
    					}
    					//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("Error in open operation " + er.getMessage());
    		}
    		finally {
    			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 synchronized float Deposit(String opID, int acnt, float amount) throws java.rmi.RemoteException
    	{
    		/////////////////////////////////////////////////////////////////////
    		/*
    		modeCheck();
    		if (serverMode == BankUtility.SINGLE) {
    			packet = log.findRecord(opID);
    			if (packet != null) {
    				//should be the source account
    				return packet.newValue;
    			}
    		}
    		*/

    		/////////////////////////////////////////
    		Packet packet=secureCheckPacket(opID);
    		if (packet!=null)
    		{
    			return packet.newValue;
    		}

    		////////////////////////////////////////////////////////////////////

    		FileLock fileLock = null;
    		int account = acnt % 10000;
    		int counter = 0;

    		float balance = 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);
    				balance = rpcFiler.readFloat();
    				// Check if account is opened
    				if (balance != BankUtility.NegativeInfinite) {
    					balance += amount;

    					// Write to local file
    					rpcFiler.seek(account * AccountSize);
    					rpcFiler.writeFloat(balance);

    					// Write the log in local file and backup it on remote backup server
    					packet = new Packet();
    					packet.opID = opID;
    					//packet.hostIndex=hostIndex;
    					packet.opType = Packet.DEPOSIT;
    					packet.source = Integer.parseInt(localConfig.branchID)*10000+account;
    					packet.dest = -1;//ignored
    					packet.oldValue = balance - amount;
    					packet.newValue = balance;//is initialized to 0
    					packet.tag = -1;//should be ignored
    					packet.status=BankUtility.SUCCESS;//Indicate the succeeded of writting log
    					backupPacket(packet);

    					// Operation succeeded, set checkpoint
    					//if(serverMode==BankUtility.NORMAL) log.setCheckPoint();
    				}
    				available.release();
    			}

    		}
    		catch (Exception er) {
    			balance = BankUtility.NegativeInfinite;
    			throw new BankException("unknown deposit error: " + er.getMessage());
    		}
    		finally
    		{
    			return balance;
    		}
    	}

    	//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 synchronized float Withdraw(String opID, int acnt, float amount) throws java.rmi.RemoteException {
    		/////////////////////////////////////////////////////////////////////
    		/*
    		modeCheck();
    		if (serverMode == BankUtility.SINGLE) {
    			packet = log.findRecord(opID);
    			if (packet != null) {
    				//should be the source account
    				return packet.newValue;
    			}
    		}
    		*/
    		/////////////////////////////////////////
    		Packet packet=secureCheckPacket(opID);
    		if (packet!=null)
    		{
    			return packet.newValue;
    		}

    		////////////////////////////////////////////////////////////////////
    		FileLock fileLock = null;
    		int account = acnt % 10000;
    		int counter = 0;
    		float balance = 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);
    				balance = rpcFiler.readFloat();
    				// Check if account is opened
    				if (balance != BankUtility.NegativeInfinite && balance >= amount) {
    					balance -= amount;

    					// Write to local log file
    					rpcFiler.seek(account * AccountSize);
    					rpcFiler.writeFloat(balance);

    					// Write the log in local log file and backup it on remote backup server
    					packet = new Packet();
    					packet.opID = opID;
    					//packet.hostIndex=hostIndex;
    					packet.opType = Packet.WITHDRAW;
    					packet.source = Integer.parseInt(localConfig.branchID)*10000+account;
    					packet.dest = -1;//ignored
    					packet.oldValue = balance + amount;
    					packet.newValue = balance;//is initialized to 0
    					packet.tag = -1;//should be ignored
    					packet.status=BankUtility.SUCCESS;//Indicate the succeeded of writting log
    					backupPacket(packet);

    					// Operation succeeded, set checkpoint
    					//if(serverMode==BankUtility.NORMAL) log.setCheckPoint();
    				}
    				else {
    					balance = BankUtility.NegativeInfinite;
    				}
    				available.release();
    			}

    		}
    		catch (Exception er) {
    			balance = BankUtility.NegativeInfinite;
    			throw new BankException("unknown withdraw error: " + er.getMessage());
    		}
    		finally {
    			return balance;
    		}
    	}

    	//even reading may not need to be synchronized, but
    	//file pointer has to be! So make it simple by synchronized!
    	public synchronized float Balance(String opID, int acnt) throws java.rmi.RemoteException {
    		/////////////////////////////////////////////////////////////////////
    		//there is no record to check but we still need to hold "file-reading"
    		//modeCheck();

    		//we don't need hold this, because the log file will not be touched by this method

    		////////////////////////////////////////////////////////////////////

    		float balance = 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);
    				balance = rpcFiler.readFloat();
    				//account not opened
    				available.release();
    			}
    		}
    		catch (Exception er) {
    			balance = BankUtility.NegativeInfinite;
    			throw new BankException("unknown balance error: " + er.getMessage());
    		}
    		finally {
    			return balance;
    		}
    	}

    	//
    	protected synchronized float localTransfer(String opID, 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 && destValue!=BankUtility.NegativeInfinite) {
    						srcValue -= amt;
    						destValue += amt;

    						// Write to local data file
    						rpcFiler.seek(srcAccount * AccountSize);
    						rpcFiler.writeFloat(srcValue);
    						rpcFiler.seek(destAccount * AccountSize);
    						rpcFiler.writeFloat(destValue);

    						// Write the log in local log file and backup it on remote backup server
    						Packet packet = new Packet();
    						packet.opID = opID;
    						packet.opType = BankUtility.TRANSFER;
    						packet.source = src;
    						packet.dest = dest;
    						packet.oldValue = srcValue + amt;
    						packet.newValue = srcValue;//is initialized to 0
    						packet.destOldValue = destValue - amt;
    						packet.destNewValue = destValue;//is initialized to 0
    						packet.tag = -1;//should be ignored
    						packet.status=BankUtility.SUCCESS;//should be ignored
    						backupPacket(packet);

    						// Operation succeeded, set checkpoint
    						//if(serverMode==BankUtility.NORMAL) log.setCheckPoint();
    					}
    					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 synchronized float remoteTransfer(String opID, int src, int dest, float amount) throws java.rmi.RemoteException {
    		//first we send out our transfer request
    		float srcValue = BankUtility.NegativeInfinite;
    		int account = src % 10000;
    		//int reply, index=dest/10000-100;
    		int reply, index = dest / 10000;
    		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);
    				srcValue = rpcFiler.readFloat();
    				if (srcValue != BankUtility.NegativeInfinite && srcValue >= 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

    					 */

    					boolean done;

    					srcValue -= amount;

    					// Setup the packet for transfer operation
    					Packet transferPacket = new Packet();
    					transferPacket.opID=opID;
    					transferPacket.opType=BankUtility.TRANSFER;
    					transferPacket.source=src;
    					transferPacket.dest=dest;
    					transferPacket.oldValue=amount;//this is no good;
    					transferPacket.newValue=-1;//should be ignored

    /*
    					done = true;
    					try {
    						updateInformation();
    						if (branchHostNames[index] == null)
    							throw new Exception();

    						// Debug
    						BankUtility.showDebugInfo(1, "Using TCP to connect to " + branchHostNames[index] + ":" + branchPortNumbers[index]);

    						Socket socket = new Socket(branchHostNames[index], branchPortNumbers[index]);

    						packetWrapper.writePacket(socket, transferPacket);
    						packet = packetWrapper.readPacket(socket);

    					}
    					catch (Exception er) {
    						done = false;
    						packet.status = BankUtility.TRANSFER_REJECTED;
    					}
    */

    					Socket socket=null;
    					done = false;
    					while (!done)
    					{
    						try
    						{
    							updateInformation();
    							if (branchHostNames[index] == null)
    								throw new Exception();

    							// Debug
    							BankUtility.showDebugInfo(0, "Using TCP to connect to " + branchHostNames[index] + ":" + branchPortNumbers[index]);

    							socket = new Socket(branchHostNames[index], branchPortNumbers[index]);

    							PacketWrapper.writePacket(socket, transferPacket);
    							packet = PacketWrapper.readPacket(socket);
    							done=true;

    						}
    						catch (Exception er)
    						{
    							done = false;
    							packet.status = BankUtility.TRANSFER_REJECTED;
    							//System.out.println("remote transfer connect remote primary failed");
    						}
    					}

    					reply = packet.status;

    					//////////////////////////////////////////////////////////////////////////////////////////

    					if (reply == BankUtility.TRANSFER_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();

    						// Write local data file
    						rpcFiler.seek(account * AccountSize);
    						rpcFiler.writeFloat(srcValue);
    						available.release();

    						// Write the log in local log file and backup it on remote backup server
    						Packet packet = new Packet();
    						packet.opID = opID;
    						packet.opType = BankUtility.TRANSFER;
    						packet.source = src;
    						packet.dest = dest;
    						packet.oldValue = srcValue + amount;
    						packet.newValue = srcValue;//is initialized to 0
    						packet.tag = -1;//should be ignored
    						packet.status=BankUtility.SUCCESS;//should be ignored
    						backupPacket(packet);

    						// Operation succeeded, set checkpoint
    						//if(serverMode==BankUtility.NORMAL) log.setCheckPoint();
    					}
    					else {
    						fileLock.release();
    						srcValue = BankUtility.NegativeInfinite;
    						//we didn't acquire semaphore
    						if (reply != BankUtility.TRANSFER_REJECTED) {
    							throw new BankException("Communication with remote branch server failed.");
    						}
    						//we still need to release, but we don't have to release semaphore

    					}
    				}
    				else {
    					srcValue = BankUtility.NegativeInfinite;//this costs me 5 hours!!!!!!!
    					available.release();
    				}
    			}
    		}
    		catch (Exception er) {

    			srcValue = BankUtility.NegativeInfinite;
    			throw new BankException("Error of remote transfer:" + er.getMessage());
    		}
    		finally {
    			return srcValue;
    		}
    	}

    	//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(rand.nextInt(BankUtility.SleepingLength));
    						Thread.sleep(rand.nextInt(50));
    						Thread.yield();
    						//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 synchronized float Transfer(String opID, int src, int dest, float amt) throws java.rmi.RemoteException {
    		/////////////////////////////////////////////////////////////////////
    		/*
    		modeCheck();
    		if (serverMode == BankUtility.SINGLE) {
    			packet = log.findRecord(opID);
    			if (packet != null) {
    				//should be the source account
    				return packet.newValue;
    			}
    		}
    		*/
    		/////////////////////////////////////////
    		Packet packet=secureCheckPacket(opID);
    		if (packet!=null)
    		{
    			return packet.newValue;
    		}

    		////////////////////////////////////////////////////////////////////

    		float result = BankUtility.NegativeInfinite;
    		try {
    			if (dest / 10000 == this.branchID) {
    				//do the local transfer without using TCP
    				result = localTransfer(opID, src, dest, amt);
    			}
    			else {
    				result = remoteTransfer(opID, src, dest, amt);
    			}
    		}
    		catch (Exception er) {
    			result = BankUtility.NegativeInfinite;
    			throw new BankException("unknown error of transfer:" + er.getMessage());
    		}
    		finally {
    			return result;
    		}
    	}

    	class BackupListener extends Thread {
    		public void run() {
    			UDPClientSocket sender = new UDPClientSocket();
    			int selfChecking = 0;
    			while (!threadStop) {
    				try {
    					Packet result = backupServer.recvPacket();

    					if (result.status == BankUtility.REQUEST_JOIN) {

    						ServerConfig backupConfig = null;
    						try {
    							String otherServerID = localConfig.serverID.compareTo("1")==0?"2":"1";
    							backupConfig = ServerConfig.getServerConfig(localConfig.rootContext, localConfig.branchID,otherServerID);
    						}
    						catch (Exception e) {
    						}
    						backupUDPPortNumber = Integer.parseInt(backupConfig.backupUDPPort);
    						backupHostName = backupConfig.localhostIP;

    //						if (serverMode == BankUtility.SINGLE) {
    							Packet packet;
    							try {
    								// UDPClientSocket sender=new UDPClientSocket();
    								modeSemaphore.acquire();
    								serverMode = BankUtility.BACKUPJOIN;
    								// Debug
    								BankUtility.showDebugInfo(0, "Received join request from backup server, server changed to BACKUPJOIN mode.");
    								log.gotoCheckPoint();
    								boolean done = false;
    								do {
    									// Read next log
    									try {
    										packet = log.readPacket();
    									}
    									catch (Exception e) {
    										// Reach end of log file, join process finished
    										done = true;
    										continue;
    									}

    									// No packet need to send
    									if(packet == null){
    										done=true;
    										continue;
    									}

    									// Send to backup server
    									try{
    										//BankUtility.showDebugInfo(2, "Sending packet "+packet.opID);
    										sender.sendPacket(backupHostName, backupUDPPortNumber, packet);
    									}catch(Exception e){
    										// Sending to backup server failed, resume to single status
    										done = true;
    										serverMode = BankUtility.SINGLE;

    										// Debug
    										BankUtility.showDebugInfo(0, "Backup server failed during join process, server changed to SINGLE mode.");

    										throw new Exception("Backup server failed during join process.");
    									}

    									// Operation succeeded, move forward the check point
    									log.setCheckPoint(log.getCurrentPos());
    								} while (!done);

    								// Debug
    								BankUtility.showDebugInfo(0, "Backup server join process finished, server changed to NORMAL mode.");

    								serverMode = BankUtility.NORMAL;

    							}
    							catch (Exception er) {
    								// Error while joining process
    								// System.out.println("error in backup listener " + er.getMessage());
    							}
    							finally{
    								modeSemaphore.release();
    							}
    //						}
    					}
    				}
    				catch (Exception e) {
    					// System.out.println("UDP listener error of "+e.getMessage());
    				}

    				/* Depreciated
    				 if (result.status==BankUtility.BACKUP_READY)
    				 {
    				 if (serverMode==BankUtility.BACKUPJOIN)
    				 {
    				 try
    				 {
    				 modeSemaphore.acquire();
    				 serverMode=BankUtility.NORMAL;
    				 modeSemaphore.release();
    				 }
    				 catch (Exception er)
    				 {
    				 System.out.println("error in backup listener when try to rewrite mode"+er.getMessage());
    				 }
    				 }
    				 }
    				 */
    			} // End of while

    			// Clean up
    			if (backupServer != null)
    				backupServer.close();
    			if (sender != null)
    				sender.close();
    		}
    	}

    	//this
    	class PrimaryListener extends Thread {
    		public void run() {
    			FileLock fileLock = null;
    			int result, account;
    			int selfChecking = 0;
    			float amount = -1, oldValue;
    			while (!threadStop) {
    				try {
    					serverSocket.setSoTimeout(BankUtility.WaitingLength);
    					Socket socket = serverSocket.accept();

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    					packet = packetWrapper.readPacket(socket);
    					amount = packet.oldValue;//this is just a convention
    					account = packet.dest % 10000;//the

    					// Debug
    					BankUtility.showDebugInfo(1, "Received transfer request, opID=" + packet.opID + ", from " + packet.source + " to " + packet.dest + " amount is " + amount);

    					//if (packet.status == BankUtility.TRANSFER_CHECK) {

    					// Check if this transfer operation has been processed
    						//modeSemaphore.acquire();
    						//Packet oldPacket = log.findRecord(packet.opID);
    					Packet oldPacket = secureCheckPacket(packet.opID);
    					if (oldPacket != null) {
    						packet.status = BankUtility.TRANSFER_PREPARED;
    						packetWrapper.writePacket(socket, packet);
    						//modeSemaphore.release();
    						continue;
    					}
    					//}

    					////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    					/*
    					 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;

    							// Write to local data file
    							threadFiler.seek(account * AccountSize);
    							threadFiler.writeFloat(oldValue);
    							result = BankUtility.TRANSFER_PREPARED;

    							// Write the log in local log file and backup it on remote backup server
    							packet.destOldValue = oldValue - amount;
    							packet.destNewValue = oldValue;
    							packet.status = BankUtility.SUCCESS;
    							//modeSemaphore.release();
    							backupPacket(packet);
    							//modeSemaphore.acquire();

    							// Operation succeeded, set checkpoint
    							//if(serverMode==BankUtility.NORMAL) log.setCheckPoint();
    						}
    						else {
    							result = BankUtility.TRANSFER_REJECTED;
    						}
    						available.release();
    					}
    					else {
    						result = BankUtility.TRANSFER_REJECTED;
    					}

    					///////////////////////////////////////////////////////////////////////////////////////////////////
    					packet.status = result;
    					packetWrapper.writePacket(socket, packet);

    					///////////////////////////////////////////////////////////////////////////////////////////////////////
    					/*
    					 outStream.writeInt(result);
    					 */
    					///////////////////////////////////////////////////////////////////////////////////////////////////////
    				}
    				catch (Exception er) {
    					//System.out.println("listener error of "+er.getMessage());
    				}
    				//modeSemaphore.release();
    			}
    		}
    	}
    }

    file name: UDPSocket.java

    /******************************************************************
    1. I upload a new version of UDP in folder "apr2".
    2. It now can accept arbitary number of connection (<=256). It take advantages of "DatagramSocket" which uses a fixed local
    	port number when sending out packet. So, hostname + portnumber becomes a unique connection ID for receiver to maintain the sequence number.
    3. I also change the stop-and-wait algorithms a little bit. Now the sender increment sequence number when sending out is successful.
    The receiver does like this:
    a) if the packet is from a new connection, record the sender's sequence number and add this connection into table.
    b) if the packet is from an old connection, compare the sequence number with previous one. If it is bigger than previous, update sequence number,
    	acknowledge message. If it is smaller than or equal to previous message, ignore.

    4. My design is based on an assumption of usage in our "primary-primary" transfer operation such that to use a fixed "sending" socket for
    	each remote primary server. By doing so, the socket's local port number will remain unchanged.  i.e. we have primary servers A,B,C. Then server
    	A will use two client socket b,c to connect with server B,C for transfer request so that the port number of b,c will be a fixed as long as
    	connection is not closed.

    *****************************************************************/

    package BankServer;

    import java.net.*;
    import java.io.*;
    import BankApp.*;

    //import java.io.ByteArrayInputStream;
    //import java.io.DataInputStream;

    //this socket should be used in such a way that it can either be a client socket which send
    //out message or as server socket, but it cannot be both.
    public class UDPSocket
    {

    	public int replyPortNumber;
    	public String replyHostName;

    	protected long seqNumber[]=new long[BankUtility.MaxUDPConnectionNumber];
    	protected String hostNames[]=new String[BankUtility.MaxUDPConnectionNumber];
    	protected int portNumbers[]=new int[BankUtility.MaxUDPConnectionNumber];
    	protected int connectionCount=0;

    	protected int myPortNumber;
    	protected DataInputStream dataIn;
    	protected DataOutputStream dataOut;
    	protected ByteArrayInputStream byteIn;
    	protected ByteArrayOutputStream byteOut;
    	protected DatagramPacket recvPacket, sendPacket;
    	protected DatagramSocket recvSocket, sendSocket;
    	protected byte buffer[];

    	public void close()
    	{
    		if (recvSocket != null)
    			if (!recvSocket.isClosed())
    				recvSocket.close();
    		if (sendSocket != null)
    			if (!sendSocket.isClosed())
    				sendSocket.close();
    	}

    	public int getIndexByName(String theHostName, int portNumber)
    	{
    		for(int i=0; i<connectionCount; i++)
    		{
    			if (hostNames[i].compareTo(theHostName)==0&&portNumber==portNumbers[i])
    			{
    				return i;
    			}
    		}
    		return -1;//in case we have error,
    	}

    	public UDPSocket()
    	{
    		//BankUtility.SocketPacketHeadSize
    		buffer=new byte[BankUtility.PacketSize];
    		for (int i=0; i<BankUtility.PacketSize; i++)
    		{
    			buffer[i]=2;
    		}
    		byteIn=new ByteArrayInputStream(buffer);
    		dataIn=new DataInputStream(byteIn);
    		byteOut=new ByteArrayOutputStream(BankUtility.PacketSize);
    		dataOut=new DataOutputStream(byteOut);
    		recvPacket=new DatagramPacket(buffer, BankUtility.PacketSize);
    		for (int i=0; i<BankUtility.MaxUDPConnectionNumber; i++)
    		{
    			seqNumber[i]=-1;
    		}
    	}

    //////////////////////////////////////////////////////////////////////////////////////////////////
    	//host recover must call this method at both client and server socket
    	//this is the host name index and must be called whenever the corresponding host is down
    	public void resetSeqNumber(int index)
    	{
    		seqNumber[index]=0;
    	}
    /////////////////////////////////////////////////////////////////////////////////////////////////////

    	//attention: here is a potential risk because I didn't initialize socket in base class constructor
    	//because I want to leave this at derived class
    	protected Packet doRecvPacket() throws IOException, SocketException, Exception
    	{
    		Packet packet=new Packet();
    		//byteIn.reset();
    		//recvSocket.disconnect();

    		//recvPacket.setData(buffer);
    		//buffer=new byte[BankUtility.PacketSize];
    		//byteIn=new ByteArrayInputStream(buffer);
    		//dataIn=new DataInputStream(byteIn);
    		//recvPacket=new DatagramPacket(buffer, BankUtility.PacketSize);
    		//Thread.sleep(2000);
    		//System.out.println("do recvPacket begins");

    		recvSocket.receive(recvPacket);
    		//byteIn=new ByteArrayInputStream();
    		//String str=new String(recvPacket.getData());
    		//System.out.println("now I read data directly "+str);

    		// test
    		// System.out.println("\nrecvPacket recieves "+recvPacket.getLength() +" bytes of data\n");

    		//String str=new String(recvPacket.getData());
    		//System.out.println("preview received data in doRecvPacket"+str);

    		byte temp[]=new byte[BankUtility.OpIDLength];

    		//System.out.println("before read fully to temp");

    		dataIn.readFully(temp, 0,  BankUtility.OpIDLength);//BankUtility.OpIDLength
    		//System.out.println("after read fully to temp");
    		packet.opID=new String(temp);
    		packet.seqNumber=dataIn.readLong();
    		packet.opType=dataIn.readInt();
    		packet.source=dataIn.readInt();
    		packet.dest=dataIn.readInt();
    		packet.oldValue=dataIn.readFloat();
    		packet.newValue=dataIn.readFloat();
    		packet.destOldValue=dataIn.readFloat();
    		packet.destNewValue=dataIn.readFloat();
    		packet.tag=dataIn.readInt();
    		packet.status=dataIn.readInt();

    		//System.out.println("before doRecvPacket return ");
    		//packet.display();
    		byteIn.reset();
    		//recvSocket.close();

    		// debug
    		BankUtility.showDebugInfo(2,"Received UDP packet from " + recvPacket.getAddress().getHostAddress() + ":" + recvPacket.getPort());
    		BankUtility.showDebugInfo(3,"",packet);

    		return packet;
    	}

    	//attention: here is a potential risk because I didn't initialize socket in base class constructor
    	//because I want to leave this at derived class
    	public void doSendPacket(String hostName, int portNumber, Packet packet) throws IOException
    	{
    		InetAddress iNet=InetAddress.getByName(hostName);
    		if(packet.opID == null){
    			packet.opID = BankUtility.defaultOpID;
    		}else if(packet.opID.length()<BankUtility.OpIDLength){
    			packet.opID = packet.opID + BankUtility.defaultOpID.substring(0,BankUtility.OpIDLength-packet.opID.length());
    		}else if(packet.opID.length()>BankUtility.OpIDLength){
    			packet.opID = packet.opID.substring(0,BankUtility.OpIDLength);
    		}

    		dataOut.writeBytes(packet.opID);
    		dataOut.writeLong(packet.seqNumber);
    		dataOut.writeInt(packet.opType);
    		dataOut.writeInt(packet.source);
    		dataOut.writeInt(packet.dest);
    		dataOut.writeFloat(packet.oldValue);
    		dataOut.writeFloat(packet.newValue);
    		dataOut.writeFloat(packet.destOldValue);
    		dataOut.writeFloat(packet.destNewValue);
    		dataOut.writeInt(packet.tag);
    		dataOut.writeInt(packet.status);
    		//datagrampacket is newed
    		sendPacket=new DatagramPacket(byteOut.toByteArray(), BankUtility.PacketSize, iNet, portNumber);//
    		//String str=new String(sendPacket.getData());
    		//System.out.println("before send out packet, I read data directly "+str);
    		sendSocket.send(sendPacket);
    		// debug
    		BankUtility.showDebugInfo(2,"Send UDP packet to " + iNet.getHostAddress() + ":" + portNumber);
    		BankUtility.showDebugInfo(3,"",packet);

    		byteOut.reset();
    	}
    }

    ¡¡

    file name: UDPClientSocket.java

    package BankServer;

    import java.net.*;
    import java.io.*;
    import BankApp.*;
    import BankApp.Packet;
    import BankApp.BankUtility.*;

    //import java.io.ByteArrayInputStream;
    //import java.io.DataInputStream;

    public class UDPClientSocket extends UDPSocket
    {
    	protected int replyPortNumber;//27000

    	public UDPClientSocket()
    	{
    		replyPortNumber=BankUtility.BaseReplyPortNumber;
    		try
    		{
    			sendSocket=new DatagramSocket();
    		}
    		catch (Exception er)
    		{
    			System.out.println(er.getMessage());
    		}
    				//don't initialize recv socket because it is dynamic
    	}

    	//must always timeout, otherwise we cannot retry other host name when primary fails
    	//the sender who create packet is responsible to write down the host index in packet
    	public void sendPacket(String hostName, int portNumber, Packet packet)	throws Exception
    	{
    		Packet result;
    		int counter=0;
    		int index;

    		index= getIndexByName(hostName, portNumber);
    		if (index==-1)
    		{
    			index=connectionCount;
    			connectionCount++;
    			hostNames[index]=hostName;
    			portNumbers[index]=portNumber;
    			seqNumber[index]=0;
    		}

    		//System.out.println("client call and index="+index);

    		packet.seqNumber=seqNumber[index];

    		/* Depreciated, corrected by khtwo, use the code followed
    		boolean done;
    		do
    		{
    			done=true;
    			replyPortNumber++;
    			if (replyPortNumber==BankUtility.MaxReplyPortNumber)
    			{
    				replyPortNumber=BankUtility.BaseReplyPortNumber;
    			}
    			try
    			{
    				recvSocket=new DatagramSocket(replyPortNumber);
    			}
    			catch (Exception er)
    			{
    				done=false;
    			}
    		}
    		while (!done);
    		*/

    		// Use the send socket directly
    		replyPortNumber = sendSocket.getLocalPort();
    		recvSocket = sendSocket;

    		packet.tag=replyPortNumber;//must indicate the reply port number

    		// debug
    		BankUtility.showDebugInfo(0,"<sender>Send packet to " + hostName + ":" + portNumber + " Seq=" + seqNumber[index]);

    		int tried = 0;
    		do
    		{
    			try
    			{
    				recvSocket.setSoTimeout(BankUtility.TimeOutMiliSeconds);

    				tried++;
    				doSendPacket(hostName, portNumber, packet);

    				do
    				{
    					//must setup the receive port number
    					//every time when it is not what I expect, I reset the timeout
    					result=doRecvPacket();

    				}
    				while (result.status!=BankUtility.ACKNOWLEDGEMENT||result.seqNumber!=seqNumber[index]);

    				// debug
    				BankUtility.showDebugInfo(0,"<sender>Received reply from "+recvPacket.getAddress().getHostAddress()+":"+recvPacket.getPort()+ " Seq=" + result.seqNumber + " waited=" + tried*BankUtility.TimeOutMiliSeconds + "ms");

    				seqNumber[index]++;

    				return;
    			}
    			catch (Exception er)
    			{
    				// Error in communication, add the error counter
    				counter++;
    			}
    			finally
    			{
    				// Error correction, should not close here
    				//recvSocket.close();//better to clear this port number resource for re-usage
    			}

    		}while (counter<BankUtility.MaxTrialNumber);

    		// debug
    		BankUtility.showDebugInfo(0,"<sender>Packet lost.");

    		seqNumber[index]++;

    		if (counter >= BankUtility.MaxTrialNumber) {
    			// recvSocket.close();
    			throw new ConnectException();
    		}
    	}
    }

    file name: UDPServerSocket.java

    package BankServer;

    import java.net.*;
    import java.io.*;
    import BankApp.*;

    //import java.io.ByteArrayInputStream;
    //import java.io.DataInputStream;

    public class UDPServerSocket extends UDPSocket {
    	public Packet[] receivedBuffer = new Packet[BankUtility.UDPReceiveBufferSize];

    	public String socketLock = "";

    	public int beginPos = 0;

    	public int endPos = 0;

    	public UDPServerListener listener = null;

    	public UDPServerSocket(int portNumber) throws Exception {
    		myPortNumber = portNumber;
    		recvSocket = new DatagramSocket(myPortNumber);
    		sendSocket = new DatagramSocket();// no port is needed
    		recvSocket.setSoTimeout(BankUtility.WaitingLength);

    		// Start the listener which receive packets and feed the buffer
    		listener = new UDPServerListener(this);
    		listener.start();
    	}

    	/*
    	 * Clean up the socket
    	 */
    	public void close() {
    		// Debug
    		if(recvSocket!=null){
    			if(!recvSocket.isClosed()){
    				BankUtility.showDebugInfo(1,"UDPServerListener stopped on port: " + recvSocket.getLocalPort());
    			}
    		}

    		// Close the sockets
    		super.close();
    		// Shutdown the listener
    		if (listener != null) {
    			listener.isListening = false;
    			try {
    				listener.join();
    			}
    			catch (Exception e) {
    			}
    		}
    	}

    	/**
    	 * Retrieve packet from received buffer
    	 *
    	 * @return The packet if one exists, or null if no packets received
    	 * @throws Exception
    	 */
    	public Packet recvPacket() throws Exception {
    		Packet result = null;
    		// Check if there is packets in the buffer
    		synchronized (this.receivedBuffer) {
    			if (beginPos != endPos) {
    				// There are packets
    				result = receivedBuffer[beginPos++];
    				if (beginPos > BankUtility.UDPReceiveBufferSize)
    					beginPos = 0;
    				// Return the packet
    				return result;
    			}
    		}
    		Thread.yield();

    		synchronized (this.socketLock) {
    			synchronized (this.receivedBuffer) {
    				if (beginPos != endPos) {
    					// There are packets, get one
    					result = receivedBuffer[beginPos++];
    					if (beginPos > BankUtility.UDPReceiveBufferSize)
    						beginPos = 0;
    				}
    				else {
    					// Still no packet, throw exception
    					throw new ConnectException();
    				}
    			}
    		}
    		return result;
    	}

    	// (if we are going to monitor incoming message, there is no timeout,)
    	// if we are acknowledge receipt, we must have this timeout, otherwise
    	// system maybe deadlock
    	public Packet recvPacketInner() throws Exception {
    		boolean isTestPacket = true;
    		Packet result = null;

    		// Don't return the received packet if the packet is a test packet
    		while (isTestPacket) {
    			result = null;
    			int index = 0;
    			do {
    				// recvSocket=new DatagramSocket(myPortNumber);
    				recvSocket.setSoTimeout(BankUtility.WaitingLength);
    				result = doRecvPacket();

    				String theHostName = recvPacket.getAddress().getHostAddress();
    				int thePortNumber = recvPacket.getPort();

    				// debug
    				BankUtility.showDebugInfo(1, "Received one packet from " + theHostName + ":" + thePortNumber + " Seq=" + result.seqNumber);

    				Packet ack = new Packet();

    				ack.opID = result.opID;

    				ack.seqNumber = result.seqNumber;
    				// ///////////////////////////////
    				// BankUtility.ACKNOWLEDGEMENT;
    				replyPortNumber = result.tag;

    				// replyHostName = recvPacket.getAddress().getHostName();
    				replyHostName = recvPacket.getAddress().getHostAddress();

    				ack.status = BankUtility.ACKNOWLEDGEMENT;
    				// make sure bankimplement has this

    				if (result.status != BankUtility.ACKNOWLEDGEMENT) {
    					// debug
    					BankUtility.showDebugInfo(1, "Reply to " + recvPacket.getAddress().getHostAddress() + ":" + result.tag + " Seq="+ack.seqNumber);

    					doSendPacket(recvPacket.getAddress().getHostAddress(), result.tag, ack);
    				}

    				index = getIndexByName(theHostName, thePortNumber);

    				if (index == -1) {
    					// this is a new connection
    					BankUtility.showDebugInfo(1, "New connection:" + connectionCount + " on port "
    							+ recvSocket.getLocalPort());
    					index = connectionCount;
    					connectionCount++;
    					hostNames[index] = theHostName;
    					portNumbers[index] = thePortNumber;
    					seqNumber[index] = result.seqNumber;
    				}
    				// System.out.println("server call gethost by adddress and index="+ index);

    			}// while (result.seqNumber < seqNumber[index]);
    			while (result.seqNumber < seqNumber[index]);

    			// seqNumber[index] = result.seqNumber;
    			seqNumber[index] = result.seqNumber + 1;

    			// System.out.println("server acknowledge OK");

    			// Check if the packet is a test packet, if it's not, return the packet
    			if (result.status != Packet.TEST)
    				isTestPacket = false;
    		} // End of while

    		return result;
    	}

    } // end class

    /**
     * The UDP Server Listener which cache the UDP packet received
     *
     * @author Min Huang (khtwo)
     */
    class UDPServerListener extends Thread {
    	public boolean isListening = true;

    	public UDPServerSocket theSocket = null;

    	public Packet currentPacket = null;

    	public UDPServerListener(UDPServerSocket socket) {
    		theSocket = socket;
    	}

    	/*
    	 * Receive packets and put them in the buffer
    	 */
    	public void run() {
    		BankUtility.showDebugInfo(1,"UDPServerListener start on port: " + theSocket.recvSocket.getLocalPort());
    		while (isListening) {
    			try {
    				synchronized (theSocket.socketLock) {
    					// Receive a packet and put it into the buffer
    					currentPacket = theSocket.recvPacketInner();
    				}
    				Thread.yield();

    				// Check if the buffer is full
    				synchronized (theSocket.receivedBuffer) {
    					if (theSocket.endPos == theSocket.beginPos - 1
    							|| (theSocket.beginPos == 0 && theSocket.endPos == BankUtility.UDPReceiveBufferSize)) {
    						// Buffer full, ignor the newest packet
    						continue;
    					}
    					theSocket.receivedBuffer[theSocket.endPos++] = currentPacket;
    					// Rewind the buffer when overflow
    					if (theSocket.endPos > BankUtility.UDPReceiveBufferSize) {
    						theSocket.endPos = 0;
    					}
    				}
    			}
    			catch (Exception e) {
    				// Time out, do nothing
    				Thread.yield();
    			}
    		}
    	}
    }

    file name: BankClient.java

    package BankClient;

    import ServerConfig.*;

    import java.io.*;
    import java.net.*;
    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
    {
    	public static ServerConfig currentConfig = null;
    	//protected static String branchHostNames[];
    	//protected static Bank banks[];
    	protected static String hostName;
    	protected static int sequenceNo=10000;

    	//actually this is a fake processID, since i cannot find the method, I have to use time to represent.
    	protected static String processID = "";//I have to make it fixed length
    	protected static String uniqueID = ""; // The uniqueID for this execution

    	protected static int RunningThreadNumber=5;
    	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
    		="\nPlease input your choice:\nOpen(1), Deposit(2), Withdraw(3), Balance(4), Transfer(5), Exit(6)";

    	protected static BufferedReader bufInput;

    	protected static NamingContextExt ncRef;

    	protected static ORB orb;

    	public static int Open()
    	{
    		int result=BankUtility.InvalidAccountNumber;
    		boolean done=false;
    		String branchList[] = null;
    		String opID=getOpID();
    		while (!done)
    		{
    			try
    			{
    				try
    				{
    					branchList = ServerConfig.listServers(currentConfig.rootContext);
    				}
    				catch(Exception e)
    				{
    					System.out.println("Central Naming Service access failed.");
    					return BankUtility.InvalidAccountNumber;
    				}

    				int branchID = 0;
    				try
    				{
    					branchID = Integer.parseInt(branchList[rand.nextInt(branchList.length)].substring(6,9));
    				}
    				catch(Exception e){}
    				int account=(branchID)*10000;
    				Bank h=getInterface(account);
    				result= h.Open(opID);
    				done=true;
    			}
    			catch (Exception er)
    			{
    				done=false;
    			}
    		}
    		return result;
    	}

    	public static float Deposit(int account, float amount)
    	{
    		float result=BankUtility.NegativeInfinite;
    		boolean done=false;
    		String opID=getOpID();
    		while (!done)
    		{
    			try
    			{
    				Bank h=getInterface(account);
    				result= h.Deposit(opID, account, amount);
    				done=true;
    			}
    			catch (Exception er)
    			{
    				done=false;
    			}
    		}
    		return result;
    	}

    	public static float Withdraw(int account, float amount)
    	{
    		float result=BankUtility.NegativeInfinite;
    		boolean done=false;
    		String opID=getOpID();
    		while (!done)
    		{
    			try
    			{
    				Bank h=getInterface(account);

    				result= h.Withdraw(opID, account, amount);
    				done=true;
    			}
    			catch (Exception er)
    			{
    				done=false;
    			}
    		}
    		return result;
    	}

    	public static float Balance(int account)
    	{
    		float result=BankUtility.NegativeInfinite;
    		boolean done=false;
    		String opID=getOpID();
    		while (!done)
    		{
    			try
    			{
    				Bank h=getInterface(account);
    				result= h.Balance(opID, account);
    				done=true;
    			}
    			catch (Exception er)
    			{
    				done=false;
    			}
    		}
    		return result;
    	}

    	public static float Transfer(int source, int dest, float amount)
    	{
    		float result=BankUtility.NegativeInfinite;
    		boolean done=false;
    		String opID=getOpID();
    		while (!done)
    		{
    			try
    			{
    				Bank h=getInterface(source);
    				result= h.Transfer(opID, source, dest, amount);
    				done=true;
    			}
    			catch (Exception er)
    			{
    				done=false;
    			}
    		}
    		return result;
    	}

    	protected static boolean checkAccount(int account)
    	{
    		int result=account/10000;
    		return result>=0&&result<BankUtility.MaxBranchNumber;
    	}

    	//since the method which is calling this method is already synchronized, we don't have to use semaphore any more
    	protected synchronized static Bank getInterface(int account)throws java.rmi.RemoteException
    	{
    		//for testing
    		Bank h;
    		int branchIndex=account/10000;

    		if (branchIndex>=BankUtility.MaxBranchNumber-1||branchIndex<0)
    		{
    			throw new BankException("branch number invalid");
    		}
    		try
    		{
    			String zeros = "000";
    			String branchID = String.format("%1$3d",branchIndex).trim();
    			if(branchID.length()<3){
    				branchID = zeros.substring(0,3-branchID.length())+branchID;
    			}

    			NamingContext ctx2 = NamingContextExtHelper.narrow(currentConfig.rootContext.resolve_str(ServerConfig.branchDirectory));
    			ctx2 = NamingContextExtHelper.narrow(((NamingContextExt)ctx2).resolve_str(ServerConfig.branchTag+branchID));
    			h=BankHelper.narrow(((NamingContextExt)ctx2).resolve_str(ServerConfig.PrimaryTag));
    		}
    		catch(Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    		if (h==null)
    		{
    			throw new BankException("cannot bind branch"+(branchIndex+100));
    		}
    		return h;
    	}

    	//a synchronized method
    	protected synchronized static String getOpID()
    	{
    		// Try to get a unique ID from central naming service

    		try{
    			if(BankClient.uniqueID.length()<10){
    				BankClient.uniqueID = ServerConfig.getUniqueID(currentConfig.rootContext);
    			}
    			sequenceNo++;
    			if (sequenceNo==100000)
    			{
    				// Local sequence number run out
    				// Get another unique ID from central naming service
    				BankClient.uniqueID = ServerConfig.getUniqueID(currentConfig.rootContext);
    				sequenceNo=10000;
    			}
    		}catch(Exception e){
    			// Error while retrieve unique ID
    			System.out.println("Error while retrieve unique ID");
    			uniqueID = String.format("%1$" + BankUtility.OpIDPrefixLength + "d",((long)-1));
    		}
    		//return processID+sequenceNo;
    		return uniqueID + sequenceNo;
    	}

    	public static void main(String args[])
    	{
    		boolean running = false;
    		// Read central naming service configure
    		currentConfig = new ServerConfig();
    		try{
    			currentConfig.readNSFromFile();
    		}catch(Exception e){
    			System.out.println("Reading naming service configure file failed. Quit.");
    			System.exit(1);
    		}

    		try
    		{

    			//hostName=BankUtility.getHostName();

    			hostName=InetAddress.getLocalHost().getHostName();

    			long pid=System.currentTimeMillis();
    			//char[] array=new char[BankUtility.OpIDPrefixLength];
    			String temp=hostName+pid+"                                ";
    			processID=String.copyValueOf(temp.toCharArray(), 0, BankUtility.OpIDPrefixLength);

    			// Retrieve the central naming service
    			Properties props = new Properties();
    			props.put("org.omg.CORBA.ORBInitialPort", ServerConfig.centralNCInitialPort);
    			props.put("org.omg.CORBA.ORBInitialHost", ServerConfig.centralNCInitialHost);
    			String empty[] = new String[0];
    			orb = ORB.init(empty, props);
    			currentConfig.setServerConfig(orb);

    			ncRef= currentConfig.rootContext;

    			bufInput = new BufferedReader(new InputStreamReader(System.in));

    			// test
    			BankUtility.showDebugInfo(1,"The unique operation id prefix is " + BankClient.getOpID().substring(0,BankUtility.OpIDPrefixLength));

    			manualTester();
    			oneWayTester();
    			threadTester();
    			transferTester();
    			pairTransferTester();
    			//autoTester();
    		}
    		catch(Exception er)
    		{
    			System.out.println(er.getMessage());
    		}
    	}

    	static void oneWayTester()
    	{
    		int account1, account2;
    		int RunningNumber=10;
    		float InitialAmount=50000;
    		account1=getAccount(1);
    		account2=getAccount(2);

    		System.out.println("one way test begins and we deposit 5000.0 to each account "
    			+ " and we are going to run some number transfer of 50 each from account "+ account1+ " to account " + account2+
    			" later we will transfer same number of transfer back from account "+ account2 +" to account "+account1+
    			" if server is not properly synchronized and fault tolerant, the balance will not be the same probably ");
    		Deposit(account1, InitialAmount);
    		Deposit(account2, InitialAmount);
    		System.out.println("account "+account1+" has balance of "+Balance(account1));
    		System.out.println("account "+account2+" has balance of "+Balance(account2));
    		int counter=0;
    		while(counter<RunningNumber)
    		{
    			if (Transfer(account1, account2, 50)!=BankUtility.NegativeInfinite)
    			{
    				counter++;
    				System.out.println("transfer from account "+account1 +" to account "+ account2+" succeed");
    			}
    		}
    		counter=0;

    		while(counter<RunningNumber)
    		{
    			if (Transfer(account2, account1, 50)!=BankUtility.NegativeInfinite)
    			{
    				counter++;
    				System.out.println("transfer from account "+account2 +" to account "+ account1+" succeed");
    			}
    		}

    		System.out.println("now let's check if two accounts have same balance");
    		System.out.println("account "+ account1+" has balance of "+Balance(account1));
    		System.out.println("account "+ account2+" has balance of "+Balance(account2));
    	}

    	//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==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)
    					{
    						System.out.println("Failed");
    						Thread.sleep(1000);
    					}
    					else
    					{
    						System.out.println("Succeeded");
    						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+1);
    				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());
    				float result = 0;
    				boolean done = false;
    				int retry = 0;
    				switch(choice)
    				{
    					case 1:
    						//randomly choose a server
    						//bufOutput.writeBytes("new opened account is" + Integer.toString(account)+"\n");
    						int newAcnt = 0;

    						// Retry if the remote object is shut down
    						retry = 0;
    						done = false;
    						while(!done && retry<10){
    							try{
    								newAcnt = Open();
    								done = true;
    							}catch(Exception e){
    								if(e.getMessage().indexOf("cannot bind branch")!=-1){
    									// Can not find branch server
    									System.out.println("The branch server is not start up.");
    									done=false;
    									break;
    								}
    								retry++;
    							}
    						}
    						if(done==false){
    							continue;
    						}
    						System.out.println("new opened account is " + newAcnt);
    						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!");
    							break;
    						}
    						//bufOutput.writeBytes("input amount to deposit:\n");
    						System.out.println("input amount to deposit:");
    						amount=Float.parseFloat(bufInput.readLine());

    						// Retry if the remote object is shut down
    						retry = 0;
    						done = false;
    						while(!done && retry<10){
    							try{
    								result = Deposit(account, amount);
    								done = true;
    							}catch(Exception e){
    								if(e.getMessage().indexOf("cannot bind branch")!=-1){
    									// Can not find branch server
    									System.out.println("The branch server is not start up.");
    									done=false;
    									break;
    								}
    								retry++;
    							}
    						}
    						if(done==false){
    							continue;
    						}
    						if(result==BankUtility.NegativeInfinite){
    							System.out.println("Operation failed, the account does not exist.");
    						}else{
    							System.out.println("new balance is "+result);
    						}
    						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!");
    							break;
    						}
    						System.out.println("input amount to withdraw:");
    						amount=Float.parseFloat(bufInput.readLine());

    						// Retry if the remote object is shut down
    						retry = 0;
    						done = false;
    						while(!done && retry<10){
    							try{
    								result = Withdraw(account, amount);
    								done = true;
    							}catch(Exception e){
    								if(e.getMessage().indexOf("cannot bind branch")!=-1){
    									// Can not find branch server
    									System.out.println("The branch server is not start up.");
    									done=false;
    									break;
    								}
    								retry++;
    							}
    						}
    						if(done==false){
    							continue;
    						}
    						if(result==BankUtility.NegativeInfinite){
    							System.out.println("Operation failed, the account does not exist or not enough money.");
    						}else{
    							System.out.println("new balance is "+result);
    						}

    						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!");
    							break;
    						}
    						//get server

    						//bufOutput.writeBytes("balance of account " + Integer.toString(account)+
    						//	" is " + Float.toString(h.Balance(account))+ "\n");

    						// Retry if the remote object is shut down
    						retry = 0;
    						done = false;
    						while(!done && retry<10){
    							try{
    								result = Balance(account);
    								done = true;
    							}catch(Exception e){
    								if(e.getMessage().indexOf("cannot bind branch")!=-1){
    									// Can not find branch server
    									System.out.println("The branch server is not start up.");
    									done=false;
    									break;
    								}
    								retry++;
    							}
    						}
    						if(done==false){
    							continue;
    						}
    						if(result==BankUtility.NegativeInfinite){
    							System.out.println("Account does not exist.");
    						}else{
    							System.out.println("balance of account " + Integer.toString(account)+
    									" is " + result);
    						}
    						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!");
    							break;
    						}
    						System.out.println("input destination account no");
    						dest=Integer.parseInt(bufInput.readLine());
    						if (!checkAccount(account))
    						{
    							System.out.println("invalid account number!");
    							break;
    						}
    						System.out.println("input amount to transfer");
    						amount=Float.parseFloat(bufInput.readLine());

    						// Retry if the remote object is shut down
    						retry = 0;
    						done = false;
    						while(!done && retry<10){
    							try{
    								result = Transfer(account, dest, amount);
    								done = true;
    							}catch(Exception e){
    								if(e.getMessage().indexOf("cannot bind branch")!=-1){
    									// Can not find branch server
    									System.out.println("The branch server is not start up.");
    									done=false;
    									break;
    								}
    								retry++;
    							}
    						}
    						if(done==false){
    							continue;
    						}
    						if(result==BankUtility.NegativeInfinite){
    							System.out.println("Operation failed, the account does not exist or not enough money.");
    						}else{
    							System.out.println("transfer "+ Float.toString(amount) + " from "+ Integer.toString(account)
    									+ " to " +Integer.toString(dest) + " and balance of source account is " +
    									result);
    						}
    						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()
    	{
    		int branchNum = 3;
    		final int MaxAmount=1000;

    		int choice, account, dest;
    		float amount;

    		for (int i=0; i<100; ++i)
    		{
    			if(i%20==1) System.out.println("20 tests done");
    			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(branchNum)+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(branchNum)+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(branchNum)+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(branchNum)+BankUtility.BranchIDOffset)*10000
    						+rand.nextInt(10000);
    					dest=(rand.nextInt(branchNum)+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: BankUtility.java (mainly to define some common parameter between client and server)

    package BankApp;

    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
    {
    	// Debug
    	public static final int DEBUG_LEVEL = 0;	// 0: little debug,		1: Reliable Message Level,
    												// 2: Packet Level,  	3: Show Packet detail,

    	//CONSTANT FOR OPTYPE
    	public static final int OPEN=0;
    	public static final int DEPOSIT=1;
    	public static final int WITHDRAW=2;
    	public static final int BALANCE=3;
    	public static final int TRANSFER=4;

    	//CONSTANT FOR OP_STATUS
    	public static final int START=0;
    	public static final int SUCCESS=1;
    	public static final int FAILED=2;
    	//replay only happens when backup becomes primary and try to replay message to finish unfinished transfer
    	public static final int REPLAY=3;

    	//public static final int BasePortNumber=25000;
    	public static final int AMaxBranchNumber=2;
    	public static final int MaxBranchNumber=1000;
    	public static final int MaxLockTrialNumber=100;

    	public static final int MaxTransientRecord=10;

    	/////////////////////////////////////////////
    	//UDP
    	public static final int TimeOutMiliSeconds=1;
    	public static final int MaxTrialNumber=1000;
    	public static final int MaxUDPConnectionNumber=2000;

    	public static final int SleepingLength=1000;
    	public static final int WaitingLength=1;
    	public static final int WaitingTimes=1500;

    	public static final float NegativeInfinite= -10000;
    	public static final int InvalidAccountNumber=-1;
    	public static final int BranchIDOffset=1;
    	public static final int OpIDPrefixLength=21;
    	public static final int OpIDSuffixLength=5;
    	public static final int OpIDLength=OpIDPrefixLength+OpIDSuffixLength;
    	public static String defaultOpID = String.format("%1$" + BankUtility.OpIDLength + "d",0);

    	// public static final int PacketSize=OpIDLength+5*4+2*4+1*8;
    	public static final int PacketSize=OpIDLength+5*4+2*4+2*4+1*8;

    	public static final int BaseReplyPortNumber=27000;
    	public static final int MaxReplyPortNumber=27300;

    	public static final int ACKNOWLEDGEMENT=10;//used as status in packet
    	public static final int REPLY=11;//used as status in packet
    	public static final int UDPReceiveBufferSize = 10000;

    	//these are four states of primary server
    	public static final int NORMAL=0;
    	public static final int TRANSIENT=1;
    	public static final int SINGLE=2;
    	public static final int BACKUPJOIN=3;
    	public static final int STATETRANSFER=4;

    	//THIS IS MESSAGE SENT TO MONITOR
    	public static final int LOAD_BACKUP=4;

    	//MESSAGE SENT TO OTHER PRIMARY
    	public static final int TRANSFER_CHECK=5;
    	public static final int TRANSFER_NORMAL=6;
    	public static final int TRANSFER_REJECTED=7;
    	public static final int TRANSFER_PREPARED=8;

    	//MESSAGE RECEIVED FROM BACKUP

    	public static final int BACKUP_READY=9;
    	public static final int REQUEST_JOIN=10010;

    	// Bank Account related
    	public static final int ACCOUNT_SIZE = 4;

    	/**
    	 * Print debug information
    	 * @param debug_level The debug level of this information
    	 * @param debugInfo The text message
    	 */
    	public static void showDebugInfo(int debug_level, String debugInfo){
    		if(debug_level<=BankUtility.DEBUG_LEVEL){
    			System.out.println(debugInfo);
    		}
    	}

    	/**
    	 * Print debug information
    	 * @param debug_level The debug level of this information
    	 * @param debugInfo The text message
    	 * @param packet The packet information
    	 */
    	public static void showDebugInfo(int debug_level, String debugInfo, Packet packet){
    		if(debug_level<=BankUtility.DEBUG_LEVEL){
    			if(debugInfo.length()>0) System.out.println(debugInfo);
    			if(packet!=null) packet.display();
    		}
    	}
    }

    file name: Packet.java

    package BankApp;

    //the packet is using the longest possible fields
    public class Packet
    {
    	//CONSTANT FOR OPTYPE
    	public static final int OPEN=0;
    	public static final int DEPOSIT=1;
    	public static final int WITHDRAW=2;
    	public static final int BALANCE=3;
    	public static final int TRANSFER=4;

    	//CONSTANT FOR OP_STATUS
    	public static final int START=0;
    	public static final int SUCCESS=1;
    	public static final int FAILED=2;
    	//replay only happens when backup becomes primary and try to replay message to finish unfinished transfer
    	public static final int REPLAY=3;
    	public static final int TEST=101;		// It's a test packet
    	public static final int TEST_REPLY=102;		// It's a test reply packet
    	public static final int RELOAD=103;		// It's a packet which request the monitor to relaunch a server

    	public String opID;
    	public long seqNumber=0; //this is used in socket communication
    	public int opType=0;
    	public int source=0;
    	public int dest=0;
    	public float oldValue=0;
    	public float newValue=0;
    	public float destOldValue=0;
    	public float destNewValue=0;
    	public int tag=0;//maybe used as return address port number in socket communication
    	public int status=0;

    	public void display()
    	{
    		System.out.print("opID="+opID+", ");
    		System.out.print("seqNumber="+seqNumber+", ");
    		System.out.print("opType="+opType+", ");
    		System.out.print("source="+source+", ");
    		System.out.print("dest="+dest+", ");
    		System.out.print("oldValue="+oldValue+", ");
    		System.out.print("newValue="+newValue+", ");
    		System.out.print("destOldValue="+destOldValue+", ");
    		System.out.print("destNewValue="+destNewValue+", ");
    		System.out.print("tag="+tag+", ");
    		System.out.println("status="+status+"\n");
    	}

    };

    file name: PacketWrapper.java

    package BankApp;

    //import java.net.Socket;
    //import java.net.ServerSocket;
    //import java.lang.Object;
    //import java.io.DataInputStream;
    //import java.io.RandomAccessFile;
    import java.io.*;
    import java.net.*;

    //import java.nio.*;
    //import java.util.*;

    public class PacketWrapper
    {
    	protected static byte[] buffer=new byte[BankUtility.OpIDLength];
    	//protected ServerSocket socket;
    	//protected RandomAccessFile file;

    	public static Packet readPacket(Socket socket)
    	{
    		Packet packet=new Packet();
    		try
    		{

    			//java.io.DataInputStream  inStream=new java.io.DataInputStream(socket.getInputStream());
    			DataInputStream  inStream=new DataInputStream(socket.getInputStream());

    			inStream.readFully(buffer, 0, BankUtility.OpIDLength);
    			//inStream.readFully(buffer);
    			packet.opID= new String(buffer,0,BankUtility.OpIDLength);
    			packet.seqNumber=inStream.readLong();
    			packet.opType=inStream.readInt();
    			packet.source=inStream.readInt();
    			packet.dest=inStream.readInt();
    			packet.oldValue=inStream.readFloat();
    			packet.newValue=inStream.readFloat();
    			packet.destOldValue=inStream.readFloat();
    			packet.destNewValue=inStream.readFloat();
    			packet.tag=inStream.readInt();
    			packet.status=inStream.readInt();
    		}
    		catch(Exception er)
    		{
    			System.out.println(er.getMessage());
    		}
    		return packet;
    	}

    	public static void writePacket(Socket socket, Packet packet)
    	{
    		try
    		{
    			java.io.DataOutputStream  outStream=new java.io.DataOutputStream(socket.getOutputStream());
    			outStream.writeBytes(packet.opID);
    			outStream.writeLong(packet.seqNumber);
    			outStream.writeInt(packet.opType);
    			outStream.writeInt(packet.source);
    			outStream.writeInt(packet.dest);
    			outStream.writeFloat(packet.oldValue);
    			outStream.writeFloat(packet.newValue);
    			outStream.writeFloat(packet.destOldValue);
    			outStream.writeFloat(packet.destNewValue);
    			outStream.writeInt(packet.tag);
    			outStream.writeInt(packet.status);
    		}
    		catch (Exception er)
    		{
    			System.out.println(er.getMessage());
    		}
    	}

    	public static Packet readPacket(RandomAccessFile file)
    	{
    		Packet packet=new Packet();
    		try
    		{
    			file.readFully(buffer, 0, BankUtility.OpIDLength);
    			//file.readFully(buffer);
    			packet.opID= new String(buffer,0,BankUtility.OpIDLength);
    			packet.seqNumber=file.readLong();
    			packet.opType=file.readInt();
    			packet.source=file.readInt();
    			packet.dest=file.readInt();
    			packet.oldValue=file.readFloat();
    			packet.newValue=file.readFloat();
    			packet.destOldValue=file.readFloat();
    			packet.destNewValue=file.readFloat();
    			packet.tag=file.readInt();
    			packet.status=file.readInt();
    		}
    		catch (Exception er)
    		{
    			// System.out.println(er.getMessage());
    		}
    		return packet;
    	}

    	public static void writePacket(RandomAccessFile file, Packet packet)
    	{
    		try
    		{
    			file.writeBytes(packet.opID);
    			file.writeLong(packet.seqNumber);
    			file.writeInt(packet.opType);
    			file.writeInt(packet.source);
    			file.writeInt(packet.dest);
    			file.writeFloat(packet.oldValue);
    			file.writeFloat(packet.newValue);
    			file.writeFloat(packet.destOldValue);
    			file.writeFloat(packet.destNewValue);
    			file.writeInt(packet.tag);
    			file.writeInt(packet.status);
    		}
    		catch (Exception er)
    		{
    			System.out.println(er.getMessage());
    		}

    	}
    }

    ¡¡

    file name: LogManager.java

    package BankApp;

    import java.io.*;
    import java.nio.*;
    import java.util.*;
    import java.net.*;

    //let's use a redo logging algo, that is every operation is treated as a transaction
    //the first thing to do is to write into log such that a transaction begins
    //at the end when update is done to disk, then write into log that a transaction committed or roll-backed
    public class LogManager
    {
    	protected static final long LogStartOffset=8*2;
    	protected long packetNumber = 0;
    	protected long checkPointer = 0;//the number of log should be started to check
    	protected long currentPos = 0;
    	protected long checkPos = 0;
    	protected RandomAccessFile log;
    	protected PacketWrapper wrapper;

    	public LogManager(String logFileName)
    	{
    		try
    		{
    			//log=new RandomAccessFile(branchID+".log", "rw");
    			log=new RandomAccessFile(logFileName, "rw");
    			if (log.length()==0)
    			{
    				log.seek(0);
    				packetNumber=0;
    				checkPointer=0;
    				log.writeLong(packetNumber);
    				log.writeLong(checkPointer);
    			}
    			else
    			{
    				log.seek(0);
    				packetNumber=log.readLong();
    				checkPointer=log.readLong();
    			}
    			wrapper=new PacketWrapper();
    			currentPos=packetNumber;
    			checkPos=checkPointer;
    		}
    		catch (Exception er)
    		{
    			System.out.println(er.getMessage());
    		}
    	}

    	//////////////////////////////////////////////////
    	//do we need reset current position?
    	public synchronized void setCheckPoint()
    	{
    		try
    		{
    			checkPointer=packetNumber;
    			log.seek(0);
    			log.writeLong(packetNumber);
    			log.writeLong(checkPointer);
    		}
    		catch (Exception er)
    		{
    			System.out.println(er.getMessage());
    		}
    	}

    	/**
    	 * Set the new check point, normally do this after sending out a log to backup server
    	 * @param newCheckPoint
    	 */
    	public synchronized void setCheckPoint(long newCheckPoint)
    	{
    		try
    		{
    			// Check if the new check point is in the range
    			if(newCheckPoint<=packetNumber){
    				checkPointer=newCheckPoint;
    				log.seek(0);
    				log.writeLong(packetNumber);
    				log.writeLong(checkPointer);
    			}
    		}
    		catch (Exception er)
    		{
    			System.out.println(er.getMessage());
    		}
    	}

    	/////////////////////////////////////////////////////

    	public synchronized Packet findRecord(String opID)
    	{
    		Packet result=null;
    		try
    		{
    			// log.seek(LogStartOffset+checkPointer*BankUtility.PacketSize);
    			// Check whole records
    			log.seek(LogStartOffset);
    			do
    			{
    				result=wrapper.readPacket(log);
    			}
    			while (result==null?false:result.opID.compareTo(opID)!=0);
    		}
    		catch(Exception er)
    		{
    			result=null;//not find
    		}
    		return result;
    	}

    	/**
    	 * Get current position
    	 * @return
    	 */
    	public synchronized long getCurrentPos(){
    		return this.currentPos;
    	}

    	/**
    	 * Set current position
    	 * @return
    	 */
    	public synchronized void setCurrentPos(long newPos){
    		this.currentPos = newPos;
    	}

    	/**
    	 * Find the absolute position of a packet
    	 * @param packet The packet to be search
    	 * @return Return the index of the packet or -1 if not found
    	 */
    	public synchronized int findRecord(Packet packet)
    	{
    		int index = 0;
    		Packet result;
    		try
    		{
    			log.seek(LogStartOffset);
    			do
    			{
    				result=wrapper.readPacket(log);
    				index++;
    			}
    			while (result.opID.compareTo(packet.opID)!=0);
    		}
    		catch(Exception er)
    		{
    			index=-1;//not find
    		}
    		return index;
    	}

    	public synchronized Packet readPacket() throws java.rmi.RemoteException
    	{
    		Packet result=null;
    		try
    		{
    			// Debug
    			BankUtility.showDebugInfo(1,"Log: current reading position:" +currentPos + ", packetNumber:" + packetNumber + " ");

    			if (currentPos<packetNumber)
    			{
    				log.seek(currentPos*BankUtility.PacketSize+LogStartOffset);
    				result= wrapper.readPacket(log);
    				currentPos++;
    			}
    			else
    			{
    				throw new BankException("read invalid log");
    			}
    		}
    		catch (Exception er)
    		{
    			result = null;
    			// System.out.println(er.getMessage());
    		}
    		return result;
    	}
    	public synchronized void writePacket(Packet packet)
    	{
    		try
    		{
    			// Check if the packet has been recorded
    			if(findRecord(packet.opID)==null){
    				log.seek(packetNumber*BankUtility.PacketSize+LogStartOffset);
    				wrapper.writePacket(log, packet);
    				packetNumber++;
    				currentPos=packetNumber;
    				log.seek(0);
    				log.writeLong(packetNumber);
    			}
    		}
    		catch (Exception er)
    		{
    			System.out.println(er.getMessage());
    		}
    	}

    	//negative means going back to number of packet
    	public synchronized void skipPacket(int number) throws java.rmi.RemoteException
    	{
    		long temp=currentPos+number;
    		try
    		{
    			if (temp>=0&& temp<=packetNumber)
    			{
    				currentPos+=number;
    				log.seek(currentPos*BankUtility.PacketSize+LogStartOffset);
    			}
    			else
    			{
    				throw new BankException("moving to invalid log position");
    			}
    		}
    		catch (Exception er)
    		{
    			System.out.println(er.getMessage());
    		}
    	}
    	public synchronized void skipToBeginning()
    	{
    		try
    		{
    			currentPos=0;
    			log.seek(LogStartOffset);
    		}
    		catch (Exception er)
    		{
    			System.out.println(er.getMessage());
    		}
    	}

    	public synchronized void gotoCheckPoint()
    	{
    		try
    		{
    			// Debug
    			BankUtility.showDebugInfo(1, "Current checkpointer = " + checkPointer + ", packetNumber = " + packetNumber);

    			// Set the position in file
    			log.seek(checkPointer*BankUtility.PacketSize+LogStartOffset);
    			// Set the current position
    			this.currentPos = checkPointer;
    		}
    		catch (Exception er)
    		{
    			System.out.println(er.getMessage());
    		}
    	}
    }
    //Mar 21 ends

    ¡¡

    If you want to run this project, you need to deploy it. And deploy a distributed system is always troublesome and you have

    to download full source code and those batch files.

    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".

    complile.bat

    ¡¡

    How to run?

    The quickest way is to run this:

    run.bat

    But before that, modify "centralNS.cfg" to change IP address of "centralNCInitialHost" to the IP address of the machine

    where you want "orbd" to run. However, this quick test-drive will load all server at your local host. If you are capable

    to understand code, surely you are able to figure out what to do to deploy them.

¡¡

How to run client?

BankClient.bat

¡¡

    ¡¡

    ¡¡