The original page is available at: http://courses.dce.harvard.edu/~cscie160/hw5-05.htm

Prepared by Charlie Sawyer

Modified by Umar Kalim

Lab 5


Notes


Objective

Develop distributed Automated Teller Machine (ATM) using Java Remote Method Invocation (RMI) with support for multiple accounts.

You will be reimplementing the distributed Automated Teller Machine (ATM) using Java's Remote Method Invocation (RMI). You will build on the principals from the socket based ATM.

Functional Requirements

The ATM will simulate a real world automated teller machine.

The ATM must support a variety of accounts.

The ATM must support the following operations:

The ATM will run in its own process and will handle remote requests from a client running in some other process.

Design

As before, you will define an ATM interface that will be implemented by both the real ATM and a client side proxy for the ATM, called a stub. The client will use a stub that will proxy all the operations of the ATM and delegate them to a skeleton on the server. This skeleton then knows how to dispatch each call to the actual implementation object on the server. This time, rather than hand-implementing the ATM proxy, you will use the rmic code generator to create the RMI stub for your ATM interface.

The ATM will now include several individual Accounts and each of the ATM methods must now include a parameter that allows the account number to be specified. For simplicity we'll assume the account identifier type is int.

One of the challanges of distributed computing is connecting a client to the first remote object. Once the first object reference is obtained it can be used to gain access to other remote components. In fact it might be a good idea if the entire job of this first object we connect to should be providing references to other objects. This design is so common it has been detailed as a common design pattern called factory. A factory is a remote object whose main job is to provide references to other remote objects, i.e. it is a navigational starting point to find our way around objects on the server side. In this system, you will create an ATM factory that the client will use to get a reference to a remote ATM.

The ATM factory is a server side object with a remote interface just like the ATM. You will create an ATMFactory interface that has a single method getATM() that returns a remote reference to the serverside ATM instance.

You will need to make some changes to your ATM interface to make it RMI–ready, including extending java.rmi.Remote and declaring the methods throws RemoteException. Additionally, to register your implementation objects, ATMImpl and ATMFactoryImpl, with the RMI subystem to receive requests, will need to "export" them via java.rmi.server.UnicastRemoteObject. In less formal terms this means that your implementation classes must build on the services provided by this special class by defining your classes to inherit from it.

The server process will start up, create an ATMFactoryImpl instance, and then register it with the RMI naming service or registry using java.rmi.Naming. The client will then be able to lookup the ATM factory and connect to it. Then the client will use the getATM() method of the factory to get a remote reference to an ATM instance.

Architecture

The following diagram illustrates how a factory can be used in bootstrapping a remote application. In the diagram only two ATM instances are shown, but imagine a real banking system with hundreds of ATMs. The factory is aware of the remote locations of each ATM instance and can provide any one reference to a client application. Without the factory, all of the ATM instances would need to be registered in the RMI registry and the client would need to know a unique name for each ATM instance. In this case a factory is extremely useful.

And the following diagram show how the RMI stubs are used to dispatch method calls to remote implementation objects. Consider how this design differs from your proxy implementation in th previous assignment and why.

The following diagram shows the entire flow from the clients perspective. First the client looks up the ATMFactory in the RMI registry. Once it has the factory it can call getATM() to get a reference an actual ATM. And once it has a reference to an ATM it can call methods on it.


Assignment

All classes should be in the package:

   cs425.lab4

Account

Develop an Account class that represents an individual account. The Account should contain account specific data like a balance and methods for manipulating that data. The ATM implementation will create several Account instances to be manipulated by the client.

ATM

Modify your ATM interface to be an RMI remote interface. Add the int account number parameter to each of the method signatures.

ATMImpl

Create an RMI remote implementation ATMImpl that implements the remote ATM interface. You will need to leverage java.rmi.server.UnicastRemoteObject to make your implementation remotely accessible.

When the ATMImpl is created it should create some accounts and store them in a collection (a job for the constructor). At the very minimum create accounts with the following initial values.

Account Number Initial Balance
0000001 $ 0
0000002 $100
0000003 $500

When a remote request comes in, the banking transaction should be carried out on the appropriate account.

Run rmic on this implementation to create stub proxies.

ATMFactory

Create an ATMFactory interface that is RMI ready and supports a single getATM() method.

ATMFactoryImpl

Create the RMI ready implementation of the factory interface. It should return a remote reference to an ATM instance.

Run rmic on this implementation to create stub proxies.

Server

Create a new Server class. The Server is used to startup the server process. Since you've created a convenient factory object, the only work the Server has to do is create an instance of the factory, export it and register it with the RMI registry.

Register the factory with the following URL name:

   //localhost/atmfactory

Client

To drive you system, create the following Client class and include this exact class with your submission:

  package cs425.lab4;

  import java.net.MalformedURLException;
  import java.rmi.Naming;
  import java.rmi.NotBoundException;
  import java.rmi.RemoteException;
  import java.rmi.UnknownHostException;
  
  public class Client {
     public static void main(String[] args) {
        ATM atm = null;
        try {
           ATMFactory factory = (ATMFactory)Naming.lookup("//localhost/atmfactory");
           atm = factory.getATM();
        } catch (MalformedURLException mue) {
           mue.printStackTrace();
        } catch (NotBoundException nbe) {
           nbe.printStackTrace();
        } catch (UnknownHostException uhe) {
           uhe.printStackTrace();
        } catch (RemoteException re) {
           re.printStackTrace();
        }
        if (atm!=null) {
           try {
              // get initial account balance
              System.out.println("Initial Balances");
              System.out.println("Balance(0000001): "+atm.getBalance(0000001));
              System.out.println("Balance(0000002): "+atm.getBalance(0000002));
              System.out.println("Balance(0000003): "+atm.getBalance(0000003));
              System.out.println();
              // make $1000 depoist in account 0000001 and get new balance
              System.out.println("Depositting(0000001): 1000 ");
              atm.deposit(0000001, 1000);
              System.out.println("Balance(0000001): "+atm.getBalance(0000001));
              // make $100 withdrawal from account 0000002 and get new balance
              System.out.println("Withdrawing(0000002): 100 ");
              atm.withdraw(0000002, 100);
              System.out.println("Balance(0000002): "+atm.getBalance(0000002));
              // make $500 deposit in account 0000003 and get new balance
              System.out.println("Depositting(0000003): 500 ");
              atm.deposit(0000003, 500);
              System.out.println("Balance(0000003): "+atm.getBalance(0000003));
              // get final account balance
              System.out.println();
              System.out.println("Final Balances");
              System.out.println("Balance(0000001): "+atm.getBalance(0000001));
              System.out.println("Balance(0000002): "+atm.getBalance(0000002));
              System.out.println("Balance(0000003): "+atm.getBalance(0000003));
           } catch (RemoteException re) {
              System.out.println("An exception occurred while communicating with the ATM");
              re.printStackTrace();
           }
        }
     }
  }

Demonstration

To demonstrate your system you will need to start three distinct processes.

First startup the rmi registry:

	C:\> rmiregistry

Then startup the server:

	C:\> java cs425.lab4.Server

Once the server is up and running, start up the client:

	C:\> java cs425.lab4.Client

You should see the following output from the Client:

	Initial Balances
	Balance(0000001): 0.0
	Balance(0000002): 100.0
	Balance(0000003): 500.0

	Depositting(0000001): 1000
	Balance(0000001): 1000.0
	Withdrawing(0000002): 100
	Balance(0000002): 0.0
	Depositting(0000003): 500
	Balance(0000003): 1000.0

	Final Balances
	Balance(0000001): 1000.0
	Balance(0000002): 0.0
	Balance(0000003): 1000.0

Instructions for submitting your work

  1. You are required to demonstrate your implementation during the lab.
  2. Create an archive (.zip) of your project folder and submit it online.

Last updated: 28th November, 2007