The Distributed Banking System is a client-server program which is implemented using RMI (Remote Method Invocation). The server manages all users’ account information.
1. DISTRIBUTED BANKING SYSTEM USING RMI
A PROJECT REPORT
Submitted by
NARALA NAVEEN REDDY(18BCE1036)
SANGAM V V S BHARADWAJ(18BCE1248)
BACHELOR OF ENGINEERING
in
COMPUTER SCIENCE ENGINEERING
VIT UNIVERSITY: CHENNAI 600127
2. BONAFIDE CERTIFICATE
Certified that this project report “DISTRIBUTED BANKING SYSTEM USING
RMI” is the bonafidework of NARALA NAVEEN REDDY(18BCE1036),
SANGAM V V S BHARADWAJ(18BCE1248) who carried out the project
work under my supervision.
SIGNATURE
Prof:GAYATHRI R
School of Computer Science and Engineering
VIT University
Chennai – 600127
3. CONTENTS
1. ABSTRACT.
2. LITERATURE REVIEW.
3. RESEARCH GAP IN EXISTING METHODS.
4. PROPOSED ARCHITECTURE.
5. MODULES EXPLANATION.
6. SCREENSHOTS.
7. CODE.
8. CONCLUSION.
1. ABSTRACT:
The Distributed Banking System is a client-server program which is
implemented using RMI (Remote Method Invocation). The servermanages
all users’ account information. A customer can invoke the following
operations at an ATM:
● Void deposit(int acnt, int amt): This operation increases thebalance
of user account acnt by amt and returns nothing.
● Withdraw(int acnt, int amt): this operation decreases the balanceof
user account acnt by amt and returns nothing.
● Float inquiry(int acnt): this operation returns the balance ofuser
account acnt.
4. 2. LITERATURE REVIEW:
● https://ieeexplore.ieee.org/document/4296931
(Implementation and Performance Evaluation of Socket and RMI
based Java Message Passing Systems.)
● https://ieeexplore.ieee.org/document/1592774
(Requirements for and Evaluation of RMI Protocols forScientific
Computing.)
● https://ieeexplore.ieee.org/document/8742380
(Payment system using blockchain technology.)
● https://ieeexplore.ieee.org/document/8020070
(Data processing in distributed bankingsystems.)
3. RESEARCH GAP IN EXISTING METHODS:
Before we started the project, we have checked if there were any previous
versions or models of our project that have been implemented already. We
have found out that there were some previous implementations of our
project, and that they have used the concept of RMI in distributed banking.
In the previous projects, there was no time limit for the client to login, he
could login at any time after he received his pin. This posed many security
problems, as many third party’s can access that information in this huge
time gap. So, to eliminate this problem, we have added a new concept
called session time. In our project, the user is given a specific time to login
after he receives the pin, if he doesn't login in that time frame, he must wait
till a new pin is generated, and login with that pin. With this new addition,
we plan to eliminate the above problem.
6. 5. MODULES EXPLANATION:
In this project, we have 5 major modules, they are RMI, Client side, Server
side, Interfaces and the Exceptions. Given Below is a detailed explanation
of all five modules.
● RMI:
RMI is used to build distributed applications that provide remote
communication between Java programs provided in the package
java.rmi. In an RMI application, we write two programs, a server
program (resides on the server) and a client program (resides onthe
client). Inside the server program, a remote object is created and
reference to that object is made available for the client (using the
registry). The client program requests the remote objects on the
server and tries to invoke its methods. Following are the goals of
RMI:
● To minimize the complexity of the application.
● To preserve type safety.
● Distributed garbage collection.
● Minimize the difference between working with local andremote
objects.
● Client Side:
This side of the coding part, contains all the codes related to the
client side of the project. As stated above, the client can invoke
different operations which are then executed on the server side, the
values are returned to the client. The main code in the client side is
the ATM, which is the UI for the client to run any operation. Theclient
must login to his account with a pin, we have made sure that the pin
generated must be used within a certain timeframe, or it will not
work. A new pin is generated for every 30 seconds.
7. ● Server Side:
This is the part of the project where the code related to the server
side is stored. This side contains codes related to the operations
called by the client. This side contains code relating to the account of
the user i.e, his username and password. It also contains the Bank
code, which contains the various cases of exceptions possible, and
on which condition they must be called. There is the transaction code
which takes care of the transactions initiated by the user. Thesession
code takes care of the initiation and the ending of each session.The
session starts when the user is logged in. The statement code takes
care of the statements made by the user.
● Exceptions:
The exceptions are used to make sure that the program ends when a
invalid input is given by the user. There are many exception codes in
our code. The InvalidAccountException is used to make sure theuser
does not enter invalid account details. The StatementException is
used to make sure the user does not enter an invalid statement.
There are many more statements available.
● Interfaces:
These are used to provide the UI for both the client and server
consoles. There are only 2 codes, one is the Server UI, and theother
is the Client UI.
These are the 5 main modules in our project, we will take a closer look at
the codes available in them in the upcoming section.
10. 7. CODE:
1. Client:
● ATM:
package client;
import exceptions.*;
import interfaces.BankInterface;
import server.Account;
import server.Statement;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.text.SimpleDateFormat;
import java.util.Date;
//Client program, which connects to the bank using RMI andclass
methods of the remote bank object
public class ATM {
static int serverAddress, serverPort, account;
static String operation, username, password;
static long sessionID, id=0;
static double amount;
static BankInterface bank;
static Date startDate, endDate;
public static void main (String args[]) {
try {
//Parse the command line arguments into the progam
getCommandLineArguments(args);
//Set up the rmi registry and get the remote bank object from it
String name = "Bank";
Registry registry = LocateRegistry.getRegistry(serverPort);
bank = (BankInterface) registry.lookup(name);
11. System.out.println("n ---------------nClient Connected" +
"n n");
} catch (InvalidArgumentException ie){
ie.printStackTrace();
System.out.println(ie);
} catch (Exception e){
e.printStackTrace();
System.out.println(e);
}
double balance;
//Switch based on the operation
switch (operation){
case "login":
try {
//Login with username and password
id = bank.login(username, password);
Account acc = bank.accountDetails(id);
//Print account details
System.out.println("------------------------- nAccount
Details:n n" +
"Account Number: " +
acc.getAccountNumber() +
"nSessionID: " + id +
"nUsername: " + acc.getUserName() +
"nBalance: " + acc.getBalance() +
"n n");
System.out.println("Session active for 5 minutes");
System.out.println("Use SessionID " + id + " for all other
operations");
//Catch exceptions that can be thrown from the server
} catch (RemoteException e) {
e.printStackTrace();
} catch (InvalidLoginException e) {
e.printStackTrace();
12. } catch (InvalidSessionException e) {
e.printStackTrace();
}
break;
case "deposit":
try {
//Make bank deposit and get updated balance
balance = bank.deposit(account, amount, sessionID);
System.out.println("Successfully deposited E" + amount
+ " into account " + account);
System.out.println("New balance: E" + balance);
//Catch exceptions that can be thrown from the server
} catch (RemoteException e) {
e.printStackTrace();
} catch (InvalidSessionException e) {
System.out.println(e.getMessage());
}
break;
case "withdraw":
try {
//Make bank withdrawal and get updated balance
balance = bank.withdraw(account, amount, sessionID);
System.out.println("Successfully withdrew E" + amount +
" from account " + account +
"nRemaining Balance: E" + balance);
//Catch exceptions that can be thrown from the server
} catch (RemoteException e) {
e.printStackTrace();
} catch (InvalidSessionException e) {
System.out.println(e.getMessage());
} catch (InsufficientFundsException e) {
System.out.println(e.getMessage());
}
13. break;
case "inquiry":
try {
//Get account details from bank
Account acc = bank.inquiry(account,sessionID);
System.out.println("------------------------- nAccount
Details:n n" +
"Account Number: " + acc.getAccountNumber() +
"nUsername: " + acc.getUserName() +
"nBalance: E" + acc.getBalance() +
"n n");
//Catch exceptions that can be thrown from the server
} catch (RemoteException e) {
e.printStackTrace();
} catch (InvalidSessionException e) {
System.out.println(e.getMessage());
}
break;
case "statement":
Statement s = null;
try {
//Get statement for required dates
s = (Statement) bank.getStatement(account, startDate,
endDate, sessionID);
//format statement for printing to the window
SimpleDateFormat dateFormat = new
SimpleDateFormat("dd/MM/yyyy");
System.out.print("
---n");
System.out.println("Statement for Account " + account +
" between " +
14. dateFormat.format(startDate) + " and " +
dateFormat.format(endDate));
System.out.print("
---n");
System.out.println("DatetttTransaction
TypetAmountttBalance");
System.out.print("
---n");
for(Object t : s.getTransations()) {
System.out.println(t);
}
System.out.print("
---n");
//Catch exceptions that can be thrown from the server
} catch (RemoteException e) {
e.printStackTrace();
} catch (InvalidSessionException e) {
System.out.println(e.getMessage());
} catch (StatementException e) {
System.out.println(e.getMessage());
}
break;
default:
//Catch all case for operation that isn't one of the above
System.out.println("Operation not supported");
break;
}
}
15. public static void getCommandLineArguments(String args[])throws
InvalidArgumentException{
//Makes sure server, port and operation are entered as
arguments
if(args.length < 4) {
throw new InvalidArgumentException();
}
//Parses arguments from command line
//arguments are in different places based on operation, soswitch
needed here
serverPort = Integer.parseInt(args[1]);
operation = args[2];
switch (operation){
case "login":
username = args[3];
password = args[4];
break;
case "withdraw":
case "deposit":
amount = Double.parseDouble(args[4]);
account = Integer.parseInt(args[3]);
sessionID = Long.parseLong(args[5]);
break;
case "inquiry":
account = Integer.parseInt(args[3]);
sessionID = Long.parseLong(args[4]);
break;
case "statement":
account = Integer.parseInt(args[3]);
startDate = new Date(args[4]);
endDate = new Date(args[5]);
sessionID = Long.parseLong(args[6]);
break;
}
16. }
}
2. Exceptions:
● InsufficientFundsException:
package exceptions;
public class InsufficientFundsException extends Exception{
public InsufficientFundsException(){
super("Insufficient Funds");
}
}
● InvalidAccountException:
package exceptions;
public class InvalidAccountException extends Exception {
public InvalidAccountException(int acnum){
super("Account with account number: " + acnum + " does
not exist");
}
}
● InvalidArgumentException:
package exceptions;
public class InvalidArgumentException extends Exception{
public InvalidArgumentException() {
super("Invalid command line arguments entered");
}
}
17. ● InvalidLoginException:
package exceptions;
public class InvalidLoginException extends Exception {
public InvalidLoginException() {
super("Your Login Details are Invalid");
}
}
● InvalidSessionException:
package exceptions;
public class InvalidSessionException extends Exception {
public InvalidSessionException() {
super("Your session has timed out after 5 minutes of
inactivity. Please Log In again");
}
}
● StatementException:
package exceptions;
public class StatementException extends Exception {
public StatementException(String msg){
super(msg);
}
}
18. 3. Interfaces:
● BankInterface:
package interfaces;
import exceptions.*;
import server.Account;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.Date;
public interface BankInterface extends Remote {
long login(String username, String password) throws
RemoteException, InvalidLoginException;
double deposit(int accountnum, double amount, long
sessionID) throws RemoteException, InvalidSessionException;
double withdraw(int accountnum, double amount, long
sessionID) throws RemoteException, InvalidSessionException,
InsufficientFundsException;
Account inquiry(int accountnum, long sessionID) throws
RemoteException, InvalidSessionException;
StatementInterface getStatement(int accountnum, Date from,
Date to, long sessionID) throws RemoteException,
InvalidSessionException, StatementException;
Account accountDetails(long sessionID) throws
RemoteException, InvalidSessionException;
}
● StatementInterface:
package interfaces;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
public interface StatementInterface extends Serializable {
19. public int getAccountnum(); // returns account number
associated with this statement
public Date getStartDate(); // returns start Dateof
StatementInterface
public Date getEndDate(); // returns end Dateof
StatementInterface
public String getAccoutName(); // returns name of account
holder
public List getTransations(); // returns list of Transaction
objects that encapsulate details about each transaction
}
4. Server:
● Account:
package server;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
//Account class, which holds user details, transactions and
balance
public class Account implements Serializable {
//Instance variables for each account object
private double balance;
private String username, password;
private int accountNumber;
private List<Transaction> transactions;
//static variable to control account numbers
private static int nextAcNum = 88769912;
public Account (String uName, String pass) {
this.transactions = new ArrayList<>();
20. this.username = uName;
this.password = pass;
this.accountNumber = nextAcNum;
this.balance = 0;
//increment account number, so next one will be updated
nextAcNum++;
}
//add new transactions to the account
public void addTransaction(Transaction t) {
this.transactions.add(t);
}
//getters and setters
public String getUserName() {
return username;
}
public void setUserName(String userName) {
this.username = userName;
}
public double getBalance() {
return this.balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
22. //Bank Class which implements all the methods defined in the
BankInterface
public class Bank extends UnicastRemoteObject implements
BankInterface {
private List<Account> accounts; // users accounts
private List<Session> sessions, deadSessions;
public Bank() throws RemoteException
{
super();
//set up ArrayLists and create test accounts
accounts = new ArrayList<>();
sessions = new ArrayList<>();
deadSessions = new ArrayList<>();
accounts.add(new Account("user1", "pass1"));
accounts.add(new Account("user2", "pass2"));
accounts.add(new Account("user3", "pass3"));
}
public static void main(String args[]) throws Exception {
try {
//Set up securitymanager for server, and specify path to
the policy file
System.setSecurityManager(new SecurityManager());
System.out.println("n -------------------nSecurity Manager
Set");
//Add bank to the RMI registry so it can be located by
the client
String name = "Bank";
BankInterface bank = new Bank();
Registry registry =
LocateRegistry.getRegistry(Integer.parseInt(args[0]));
23. registry.rebind(name, bank);
System.out.println("Bank Server Bound");
System.out.println("Server Staredn------------------- n");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public long login(String username, String password) throws
RemoteException, InvalidLoginException {
//Loop through the accounts to find the correct one for
given username and password
for(Account acc : accounts) {
if(username.equals(acc.getUserName()) &&
password.equals(acc.getPassword())){
System.out.println(">> Account " +
acc.getAccountNumber() + " logged in");
//Create a new session on Successfull login, and
return ID to the client
Session s = new Session(acc);
sessions.add(s);
return s.sessionId;
}
}
//Throw exception if login details are not valid
throw new InvalidLoginException();
}
@Override
public double deposit(int accountnum, double amount, long
sessionID) throws RemoteException, InvalidSessionException {
//Check if user session is active, based on sessionID
passed by client
if(checkSessionActive(sessionID)) {
24. Account account;
try {
//Get the correct account
account = getAccount(accountnum);
account.setBalance(account.getBalance() + amount);
//Create transaction object for this transaction and
add to the account
Transaction t = new Transaction(account, "Deposit");
t.setAmount(amount);
account.addTransaction(t);
System.out.println(">> E" + amount + " deposited to
account " + accountnum + "n");
//return balance to client
return account.getBalance();
} catch (InvalidAccountException e) {
e.printStackTrace();
}
}
return 0;
}
@Override
public double withdraw(int accountnum, double amount, long
sessionID) throws RemoteException,
InsufficientFundsException, InvalidSessionException {
//Check if user session is active, based on sessionID
passed by client
if(checkSessionActive(sessionID)) {
try {
//Get correct user account based on accountnum
Account account = getAccount(accountnum);
//Check if withdrawal can be made, based on account
balance
25. if (account.getBalance() > 0 && account.getBalance()
- amount >= 0) {
account.setBalance(account.getBalance() -
amount);
//create new Transaction and add to account
Transaction t = new Transaction(account,
"Withdrawal");
t.setAmount(amount);
account.addTransaction(t);
System.out.println(">> E" + amount + " withdrawn
from account " + accountnum + "n");
//return updated balance
return account.getBalance();
}
} catch (InvalidAccountException e) {
e.printStackTrace();
}
}
//Throw exception if account doesn't have enough money
to withdraw
throw new InsufficientFundsException();
}
@Override
public Account inquiry(int accountnum, long sessionID)
throws RemoteException, InvalidSessionException {
//Check if session is active based on sessionID that is
passed in
if(checkSessionActive(sessionID)) {
try {
//Get account and return to the client
Account account = getAccount(accountnum);
26. System.out.println(">> Balance requested for account
" + accountnum + "n");
return account;
} catch (InvalidAccountException e) {
e.printStackTrace();
}
}
return null;
}
@Override
public Statement getStatement(int accountnum, Date from,
Date to, long sessionID) throws RemoteException,
InvalidSessionException, StatementException {
//Check if the session is active based on sessionID from
client
if(checkSessionActive(sessionID)) {
try {
//Get correct user account
Account account = getAccount(accountnum);
System.out.println(">> Statement requested for
account " + accountnum +
" between " + from.toString() + " " + to.toString()
+ "n");
//Create new statement using the account and the
dates passed from the client
Statement s = new Statement(account, from, to);
return s;
} catch (InvalidAccountException e) {
e.printStackTrace();
}
}
//throw exception if statement cannot be generated
27. throw new StatementException("Could not generate
statement for given account and dates");
}
@Override
public Account accountDetails(long sessionID) throws
InvalidSessionException {
//Get account details based on the session ID
//Each session has an associated account, so the
accounts can be retrieved based on a session
//Used on the client for looking up accounts
for(Session s:sessions){
if(s.getClientId() == sessionID){
return s.getAccount();
}
}
//Throw exception if session isn't valid
throw new InvalidSessionException();
}
private Account getAccount(int acnum) throws
InvalidAccountException{
//Loop through the accounts to find one corresponding to
account number passed from the client
//and return it
for(Account acc:accounts){
if(acc.getAccountNumber() == acnum){
return acc;
}
}
//Throw exception if account does not exist
throw new InvalidAccountException(acnum);
}
28. private boolean checkSessionActive(long sessID) throws
InvalidSessionException{
//Loop through the sessions
for(Session s : sessions){
//Checks if the sessionID passed from client is in the
sessions list and active
if(s.getClientId() == sessID && s.isAlive()) {
//Prints session details and returns true if session is
alive
System.out.println(">> Session " + s.getClientId() + "
running for " + s.getTimeAlive() + "s");
System.out.println(">> Time Remaining: " +
(s.getMaxSessionLength() - s.getTimeAlive()) + "s");
return true;
}
//If session is in list, but timed out, add it to
deadSessions list
//This flags timed out sessions for removeAll
//They will be removed next time this method is called
if(!s.isAlive()) {
System.out.println("n>> Cleaning up timed out
sessions");
System.out.println(">> SessionID: " + s.getClientId());
deadSessions.add(s);
}
}
System.out.println();
list
}
// cleanup dead sessions by removing them from sessions
sessions.removeAll(deadSessions);
//throw exception if sessions passed to client is not valid
throw new InvalidSessionException();
29. }
● Session:
package server;
import java.io.Serializable;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
//Session Object
//extends TimerTask, a thread that can be called at certaintime
intervals
//this allows the session to time to be incremented every
second, and can be cancelled after 5mins (300s)
public class Session extends TimerTask implements
Serializable{
//Instance variables for each session object
private int timeAlive;
private Timer timer;
private volatile boolean alive;
private Account account;
public long sessionId;
//static variables to specify max session time, and timer delay
private static final int MAX_SESSION_LENGTH = 60 * 5;
private static final long DELAY = 1000;
public Session(Account account) {
//generate a random 6 digit sessionID
this.sessionId = (int)(Math.random()*900000)+100000;
this.account = account;
this.alive = true;
this.timeAlive = 0;
30. //create timer object to allow the task to be scheduled to
run every second
this.timer = new Timer();
this.startTimer();
System.out.println(">> Session " + sessionId + "
createdn");
}
private void startTimer() {
//schedule timer to run every second
this.timer.scheduleAtFixedRate(this, new
Date(System.currentTimeMillis()), DELAY);
}
@Override
public void run() {
//increment the time the session has been alive
//updates once every second, so it represents the # of
seconds the session has been alive for
this.timeAlive++;
//if session has been alive for 5 minutes
if(this.timeAlive == MAX_SESSION_LENGTH) {
//set alive to false and cancel the timer
this.alive = false;
this.timer.cancel();
System.out.println("n --------------------------nSession " +
this.sessionId + " terminated n -------------------------- ");
System.out.println(this);
System.out.println("-------------------------- ");
}
}
//Getters and Setters
public boolean isAlive() {
return this.alive;
31. }
public long getClientId(){
return this.sessionId;
}
public int getTimeAlive(){
return this.timeAlive;
}
public int getMaxSessionLength(){
return MAX_SESSION_LENGTH;
}
public Account getAccount(){
return this.account;
}
@Override
public String toString() {
return "Account: " + this.account.getAccountNumber() +
"nSessionID: " +
this.sessionId +"nTime Alive: " + this.timeAlive +
"nAlive: " + this.alive;
}
}
● Statement:
package server;
import interfaces.StatementInterface;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
32. import java.util.List;
import java.util.stream.Collectors;
public class Statement implements StatementInterface,
Serializable {
//Instance variables for each Statement object
private List<Transaction> relevantTransactions;
private Date startDate, endDate;
private Account account;
public Statement(Account account, Date start, Date end){
this.relevantTransactions = new ArrayList<>();
this.account = account;
this.startDate = start;
this.endDate = end;
}
@Override
public int getAccountnum() {
return this.account.getAccountNumber();
}
@Override
public Date getStartDate() {
return this.startDate;
}
@Override
public Date getEndDate() {
return this.endDate;
}
@Override
public String getAccoutName() {
return account.getUserName();
33. }
@Override
public List getTransations() {
//Get all the relevantTransactions for the dates passed in
this.account.getTransactions().stream()
//filter transactions based on if the date is before the
given date or not
.filter(transactions ->
transactions.getDate().after(this.startDate) &&
transactions.getDate().before(this.endDate))
//create list from these filtered transactions
.collect(Collectors.toList())
//for each transaction in the list, add it to the
relevantTransactions list that will be returned
.forEach(date -> relevantTransactions.add(date));
return this.relevantTransactions;
}
}
● Transaction:
package server;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.text.DecimalFormat;
import java.util.Date;
//Transaction Class which tracks transaction dates, and other
transaction details
public class Transaction implements Serializable {
//Instance variables
private Date date;
34. private String type;
private double amount;
private Account account;
private double accBalance;
public Transaction(Account account, String type) {
this.account = account;
this.accBalance = 0;
this.type = type;
this.date = new Date(System.currentTimeMillis());
}
public Date getDate() {
return this.date;
}
public int getAccountNumber() {
return this.account.getAccountNumber();
}
public String getType() {
return this.type;
}
public void setType(String type) {
this.type = type;
}
public double getAmount() {
return this.amount;
}
public void setAmount(double amount) {
//update transaction amount and get the new account
balance, based on withdrawal or deposit
35. double amt = this.account.getBalance();
this.accBalance = this.type.equals("Deposit") ?
this.accBalance = amt + this.amount : amt - this.amount;
this.amount = amount;
}
@Override
public String toString() {
//Print transaction details based on transaction type
//Date and Decimals are formatted to print nicely on the
screen
SimpleDateFormat dateFormat = new
SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
DecimalFormat df = new DecimalFormat();
df.setMaximumFractionDigits(2);
if(this.type.equals("Deposit"))
return dateFormat.format(this.date) + "t" + this.type +
"ttt" + this.amount + "tt" + df.format(this.accBalance);
else
return dateFormat.format(this.date) + "t" + this.type +
"tt" + this.amount + "tt" + df.format(this.accBalance);
}
}
8. CONCLUSION:
In this project we have used the concept of distributed systems, to create
two remote consoles, namely the client side and the server side. We have
used the concept of RMI in java, to connect the two consoles. We have
invoked operations run on the client side, and used the concept of RMI to
complete the invoked operations on the server side. While completing the
project, we understood and gained clarity on various concepts like RMI,
distributed systems, etc.