Sample Application Using RMI

The base sample application described in Chapter 5, Access Control, simulated a highly simplified banking scenario. This program has the objects representing the bank and the customer accounts in the same JVM as the client-object accessing these bank objects. In this section, we extend this base application so that it is split into the client and server parts and the client uses RMI to interact with the server. Discussion on security aspects of the RMI is deferred till we get comfortable with the basic RMI operation.

The base sample application has interfaces BankIntf, AccountIntf and the corresponding implementation classes Bank and Account. We want to expose these as RMI remote interfaces. This can be achieved by modifying the interfaces BankIntf and AccountIntf so that they extend Remote and their methods throw exception RemoteException. Recall that the sources for the base application are present in srcorgjstkexampleank subdirectory of JSTK installation. However, let us not change the existing source files. Instead, we write the separate remote interfaces with same method signatures (except for additional exception RemoteException in throw clause of the methods) and the corresponding implementation classes. The implementation classes will be written as wrappers to the original implementation classes Bank and Account. This design allows us to add processing in the wrapper methods, if the need arises later on.

All the source and script files for the compilation and execution of this sample application are available in srcjsbookch8ex1 subdirectory of JSTK installation. For compiling the source files and running the programs, it is assumed that ex1 directory, along with its files and subdirectories, is copied to c:ch8 directory.

If you look into this directory, you find that the base application files are not available here. Recall that the base application source files are part of the srcorgjstk subtree. The compiled classes are expected in the jar file jstk.jar. Refer to the subsection The Sample Application, in the section Applying JAAS to A Sample Application, of Chapter 5, Access Control for the description of base application source files.

Let us go over the RMI-based sample application by looking at the remote interface RemoteBank and its implementation class RemoteBankImpl, corresponding to the base sample application interface BankIntf and implementation class Bank. The definition of the interface RemoteBank is shown in Listing 8-1.

Listing 8-1. Remote interface RemoteBank
// File: %JSTK_HOME%srcjsbookch8ex1commonRemoteBank.java
package common;
import org.jstk.example.bank.Exceptions;

public interface RemoteBank extends java.rmi.Remote {
  public RemoteAccount openAccount(java.math.BigDecimal initialDeposit)
      throws java.rmi.RemoteException;
  public void closeAccount(String acctNo)
      throws Exceptions.AccountNotFound, Exceptions.AccountClosed,
          java.rmi.RemoteException;
  public RemoteAccount getAccount(String acctNo)
      throws Exceptions.AccountNotFound, java.rmi.RemoteException;
  public RemoteIterator accounts() throws java.rmi.RemoteException;
}

Compare it with the source code of the interface BankIntf in Listing 5-9. What changes do you notice? Besides the fact that RemoteBank extends the interface Remote and the individual methods throw the exception RemoteException, you can see that the return types AccountIntf and Iterator have been replaced by the types RemoteAccount and RemoteIterator. The types RemoteAccount and RemoteIterator are also remote interfaces, defined within the same package that has the interface RemoteBank.

Class RemoteBankImpl implements this remote interface and wraps BankIntf, as shown in Listing 8-2.

Listing 8-2. Implementation class RemoteBankImpl
// File: %JSTK_HOME%srcjsbookch8ex1serverRemoteBankImpl.java
package server;
import org.jstk.example.bank.Exceptions;

public class RemoteBankImpl extends java.rmi.server.UnicastRemoteObject
    implements common.RemoteBank {
  private org.jstk.example.bank.BankIntf bi;
  public RemoteBankImpl(BankIntf bi) throws java.rmi.RemoteException {
    this.bi = bi;
  }
  public common.RemoteAccount openAccount(java.math.BigDecimal
      initialDeposit) throws java.rmi.RemoteException {
    return new RemoteAccountImpl(bi.openAccount(initialDeposit));
  }
  public common.RemoteAccount getAccount(String acctNo)
      throws Exceptions.AccountNotFound, java.rmi.RemoteException {
    return new RemoteAccountImpl(bi.getAccount(acctNo));
  }
  // ...
  // code of other methods omitted
}

The class RemoteBankImpl extends the class UnicastRemoteObject of the package java.rmi.server. Due to special properties of UnicastRemoteObject, this makes references of RemoteBankImpl valid in remote clients as long as the JVM hosting the RemoteBankImpl object is up and running. There are other, more powerful mechanisms to make a concrete class accessible to the remote clients but their discussion is beyond the scope here. Also, they don't help in illustrating any new security concept.

We need a program with the main() method to instantiate a RemoteBankImpl object and register it to a RMI Registry. RMI Registry is a program that maintains a directory of remote references indexed by string names, so that client programs can locate the remote reference, and invoke methods on the remote object through the remote reference. The class RemoteBankServer, shown in Listing 8-3, does exactly this by instantiating a RemoteBankImpl instance and registering it to the RMI Registry.

Listing 8-3. RMI Server program to host RemoteBankImpl
// file: srcjsbookch8ex1serverRemoteBankServer.java
package server;
import org.jstk.example.bank.server.DefaultBankPersistenceManager;

public class RemoteBankServer {
  public static void main(String args[]) throws Exception {
    DefaultBankPersistenceManager bpm =
        new DefaultBankPersistenceManager(System.getProperties());
    org.jstk.example.bank.BankIntf bank = bpm.load();
    RemoteBankImpl rbi = new RemoteBankImpl(bank);
    Java.rmi.Naming.rebind("MyRemoteBank", rbi);
    System.out.println("RemoteBank Server ready.");
  }
}

What about the client side? Recall that the main client program of the base sample application, the BankClient, gets initialized with an object of type BankIntf. But looking up the RMI Registry gives the client program a remote reference of type RemoteBank. To make BankClient work with this remote reference, we write a proxy class BankProxy, implementing interface BankIntf, that keeps a reference to RemoteBank returned by the RMI Registry and forwards all method invocations to this reference. The definition of the class BankProxy is shown in Listing 8-4.

Listing 8-4. Wrapper class BankProxy
// file: srcjsbookch8ex1clientBankProxy
package client;

import org.jstk.example.bank.Exceptions;
import org.jstk.example.bank.AccountIntf;

public class BankProxy implements org.jstk.example.bank.BankIntf {
  private common.RemoteBank rb;
  public BankProxy(common.RemoteBank rb){
    this.rb = rb;
  }
  public AccountIntf openAccount(java.math.BigDecimal initialDeposit){
    try {
      return new AccountProxy(rb.openAccount(initialDeposit));
    } catch (java.rmi.RemoteException re){
      throw new RuntimeException(re);
    }
  }
  public AccountIntf getAccount(String acctNo)
      throws Exceptions.AccountNotFound {
    try {
      return new AccountProxy(rb.getAccount(acctNo));
    } catch (java.rmi.RemoteException re){
      throw new RuntimeException(re);
    }
  }
  // ...
  // code of other methods omitted
}

As the methods of the interface BankIntf do not throw the exception RemoteException, this exception has been wrapped around in a RuntimeException, which need not be declared. Keep in mind that this may not be the ideal behavior for real applications, as the application may want to handle RemoteException differently.

With the above classes in place, it is trivial to modify the base sample application client program BankClientShell, shown in Listing 5-11a and Listing 5-11b, to get the remote reference from RMI Registry, create a BankProxy object and initialize BankClient with this proxy object. Rest of the program doesn't change. The modified class is named RMIBCShell and its definition is shown in Listing 8-5.

Listing 8-5. The client program for the RMI based sample application
// file: %JSTK_HOME%srcjsbookch8ex1clientRMIBCShell.java
package client;

import org.jstk.example.bank.client.BankClient;

public class RMIBCShell {
  public static void main(String[] args) throws Exception {
    BankClient bc = new BankClient();
    common.RemoteBank rbank = (common.RemoteBank)
      java.rmi.Naming.lookup("rmi://" + args[0] + "/" + "MyRemoteBank");
    bc.init(new BankProxy(rbank));
    while (true){
      System.out.print("rbcsh>");
      System.out.flush();
      String cmdline = new java.io.BufferedReader(
          new java.io.InputStreamReader(System.in)).readLine();
      String[] cmdargs = cmdline.split("\s");

      String result = bc.execCommand(cmdargs);
      System.out.println(result);
    }
  }
}

The classes comprising the complete RMI-based sample application, including those from the base application, their relationships and interactions are shown in Figure 8-1a and 8-1b. It is instructive to compare this with the classes shown in Figure 5-3 explaining the base sample application.

Figure 8-1a. Sample application interfaces and classes.


Figure 8-1b. Sample Application Architecture with RMI


Now that we understand the structure of the RMI-based sample application, let us compile and run it. The first step is to make sure that the base sample application code has already been compiled and archived in the jar file jstk.jar. This is done by building the JSTK software by running build program Apache Ant from the JSTK installation directory. Assuming that these compiled classes are present in archive file buildjstk.jar and your working directory is c:ch8ex1, issue the following commands:

C:ch8ex1>javac -classpath %JSTK_HOME%uildjstk.jar 
common*.java server*.java client*.java
C:ch8ex1>rmic server.RemoteBankImpl server.RemoteAccountImpl 
server.RemoteIteratorImpl
					

The second command runs the RMI compiler and generates stub classes corresponding to the remote implementation classes. You can list these stub classes in the server subdirectory:

C:ch8ex1>dir server*_Stub.class
						... lines deleted ...
02/01/2003  06:56p               5,243 RemoteAccountImpl_Stub.class
02/01/2003  06:56p               4,905 RemoteBankImpl_Stub.class
02/01/2003  06:56p               3,597 RemoteIteratorImpl_Stub.class

A stub class implements the corresponding remote interface and is implicitly used by the RMI client to communicate with the RMI server. It should be possible for the client program to load these stub classes, either from local CLASSPATH or from URLs specified by the RMI server.

The next task is to package all the classes of the base sample application and the RMI extension in following archive files: client.jar, server.jar, common.jar and server_stub.jar. The contents of these jar files are summarized in Table 8-1.

Table 8-1. Archive files for the RMI-based sample application
Jar filePackagesBrief Description
client.jarclient; org.jstk; org.jstk.example. bank.clientUsed by only the client.
server.jarserver; org.jstk.example.bank.serverUsed by only the server.
common.jarcommon; org.jstk.example.bankUsed by both.
server_stub.jarStub classes of server.Stubs for client.

This particular packaging of the class files is important to illustrate various RMI and security concepts. It is straightforward to create these jar files from the compiled class files and hence the commands are not shown here. You can find these and earlier compilation commands in the script file comp.bat within the srcjsbookch8ex1 subdirectory.

We are now ready to run the sample application. In this round, we will not use the stub download capability of RMI, saving it for the next section where we talk about the security issues involved in running downloaded class files. To prevent automatic download, we make sure that a JVM has all the classes it needs available through the local CLASSPATH.

To run the complete application, open three command shell windows—one for running the RMI Registry, one for the server and one for the client. Let us begin with the RMI Registry.

C:ch8ex1>set CLASSPATH=common.jar;server_stub.jar
C:ch8ex1>rmiregistry
					

The CLASSPATH needs to be set so that RMI Registry is able to load the stub classes from the local file system. Failure to do so has no impact on the RMI Registry but causes the server program to fail at startup. This appears somewhat counter-intuitive. What happens is that the RMI Registry attempts to load the stub classes when the server program tries to bind an instance of the implementation class to the registry, is not able to locate the class, and returns an error to the server program.

Run the server program in the second window.

C:ch8ex1>java -cp server.jar;common.jar server.RemoteBankServer
RemoteBank Server ready.

As the message indicates, the server is ready and waiting to serve requests.

Finally, run the client program in the third window and carry out a few transactions.

C:ch8ex1>java -cp client.jar;common.jar;server_stub.jar 
client.RMIBCShell localhost
rbcsh>open 2000.00   --
						open an account with 2000.00 as initial deposit.
Account Opened: 1000 -- account with number 1000 opened.
rbcsh>balance        -- show the balance of newly created account
Current Balance: 2000.00 -- opening amount is available as current balance
rbcsh>withdraw 200.00  -- take out 200.00
Withdrawn: 200.00
rbcsh>balance         -- show the current balance
Current Balance: 1800.00
rbcsh>statement      -- show the transaction history
----------------- BEGIN BANK STATEMENT -----------------
Statement Date : Sun May 25 22:07:48 PDT 2003
Account#       : 1000
Account Status : OPEN
Transactions   :
Sun May 25 22:06:49 PDT 2003   OPEN   0.00   0.00  account open
Sun May 25 22:06:49 PDT 2003   CREDIT   2000.00   2000.00  cash deposit
Sun May 25 22:07:37 PDT 2003   DEBIT   200.00   1800.00  cash withdrawal
------------------ END BANK STATEMENT ------------------
rbcsh>quit
					

Congratulations! You got a moderately complex RMI-based application working. This would prove to be a good basis for further exploration in the subsequent sections.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.21.108