CS551 Calculator Lab Assignment 3

Deadline: Oct. 23 (Th) Midnight

Submission: a zipped package (containing source code and report) to H:\program_submission

This documentation has three parts: Lab3-A, Lab3-B, and Report.

1. Lab 3a - Remote Procedure Call – RPC

The Counter Server

You need to use classes in folders jrpcgen and jarpc. So make appropriate changes to your classpath.

Copy the Counter files from the Lab3a directory into a directory of your own. Inspect the three files:

  • cou.x is the interface definition file for the counter server. There are three operations defined:
    inc() - increment the counter
    dec() - decrement the counter
    set(int) - set the counter
  • docount.java is the simple counting server that implements this interface
  • couTest.java is a simple client to test this server.

To make a working setup you will need to use the RPC Compiler to generate the stubs that will do all the remote communication. The simple client and server classes that I have written make use of these stubs.

From the directory where you have placed the server files you will need to run the IDL compiler twice, once to generate the server stubs ( -Ss ) and once to generate the client stubs ( -Sc ). In both cases you will need the -n switch to suppress C compiler pre-processing:
java jrpcgen -n -Ss cou.x
java jrpcgen -n -Sc cou.x

This will automatically write some Java files in your directory. If you get an error saying that the class definition for SymbolTranslation cannot be found, then you have probably not included the folder jrpcgen in your classpath.

cou.java is the client stub and provides local calls for inc(), dec(), & set(). These calls include all the extra communication and XDR routines to cause this execution to take place on a server.

couServer.java is the server stub and enables implements the three functions, providing the "wrapping" that enables them to be remotely accessed.

Compile the java files in your directory

To test this you will need three DOS windows:

Note that a couple of these windows will display IndexOutOfBounds exception messages during operation of the server. These can be ignored.

In the first window you will need to run the Portmapper program. This is the component, which listens out for RPC requests and directs them to the appropriate RPC server. In a Unix system this is already there but Windows needs this to be explicitly launched. Once you have started Portmapper you can leave it running for the whole lab.
java JRPC.Portmapper

In the second window you need to run your server:
java docount

The third window is where you will be running your client but a good first step is to use the RPCInfo utility to check that the RPC is "listening":
java RPCInfo
If everything is set up correctly you should see something like this:

Program Ver Proto Port Service

100000 2 TCP 111 -

100000 2 UDP 111 -

77 1 UDP 1084 -

77 1 TCP 1086 -

This shows that there are four RPC programs listening. RPC programs are identified by numbers - the "well-known" number 100000 identifies the Portmapper program and it is listening, via UDP and TCP on port number 111. This is "well known" port number so all remote clients know how to contact Portmapper in order to get things started.
The other program "77" is our program. If you inspect cou.x you will see that this is the number that has been assigned to the "counter" service. The two port numbers that have been given to our program were selected by the Portmapper. Our program is not "well-known" so when a counter client needs a counter server it talks first to Portmapper and asks "what is the port number for program number 77?"
If everything is in order you can run the counter client:
java couTest
and you should see the counter being incremented and decremented with corresponding messages on the server screen. You may also see some IndexOutOfBoundsException messages - do not worry about these - there is a bug in the library we are using.

Calculator server

Using what you learn from the counter server example you should be able to build a RPC based calculator server:

Add another Distribution menu - "RPC"

  • CalculatorImpl constant
  • DistMenu changes
  • Add a new checkDistrib case to CalculatorImplFactory - RPC will work in both JVM's
  • Create a new package directory - "rpc"
Dealing with the IDL

Take a copy of my IDL file – Lab3a\calculator.x. Put it in your rpc directory.

  • Look at the contents of this file. As you will see it defines:
  • A structure “calcReturn” that will be used to return results from your server
  • Another structure “calcArgs” that will be used to pass parameters to your server.
  • A “magic number” ( 567890 ) for your calculator service
  • A version ( 1 ) for your service
  • Four remote procedures (add(), subtract()…) that will be implemented. Each of these procedures will be passed two integers and return a “calcReturn” structure. Each procedure also has a “magic number” – 1, 2, 3 & 4.

Use the RPC Compiler to generate the server and client stubs.
You will need to add an extra switch – “-p rpc” – which will cause the Java files generated to be in the rpc package.
java jrpcgen -n -Ss –p rpc rpc\calculator.x
java jrpcgen -n -Sc –p rpc rpc\calculator.x
Notice that there are four Java files generated:

  • calculator.java– this is the client stub
  • calculatorServer.java – this is the server stub
  • calcReturn.java – this class represents the “structure” that was defined in the IDL file. If you look at the file you will see that there are class members for each component of the structure.
  • calcArgs.java – this class represents the other “structure” that was defined in the IDL file.
  • Compile these files
An RPC Impl

Make a copy of socket.PlainCalculatorImpl in the rpc directory and change its package. You can adapt this file to become the PlainCalculatorImpl for the rpc package:

  • Add an import line so that this class will have access to the JaRPC classes:
    import JRPC.*;
  • Add a second import line so that this class will have access to the java.net classes:
    import java.net.*;
  • Take a look atcouTest (from the counter server ) to see how a connection is made to an RPC server. This needs to be done in the newConnection() method of the PlainCalculatorImpl. Note that the client variable ( which is the stub through which we talk to the server ) needs to be of type calculatorin this case ( the name of the client stub we have generated ) rather than cou which was the client stub in the counter example.
    You should also change the hard-wired “localhost” parameter to be the value typed in by the user and given as a parameter to the constructor. (so change “localhost” to host)
  • The reuseConnection method simply sets the client variable to be equal to the one that was passed in. (Not sure what this means . The lab works fine because of newConnection(), even if you leave the implementation of reuseConnection() blank. Any suggestions?)
  • The arithmetic calls will need to be modified to send the calculation off to the RPC server. Bear in mind that the connection to the server is now represented by the client variable and that methods such as “add” can be invoked on that object ( look in couTest.java ).
    The tricky part is getting the values into, and out of, the calcArgs and calcReturn structures for communication with the server.
  • To send the arguments you need a new calcArgs object:
    calcArgs args = new calcArgs();
    with its fields set to the values of a and b:
    args.x = (float)a;
    args.y = (float)b;
  • To receive the answer you need to do the reverse:
    calcReturn calcRet = client.add_1( args );
    if( calcRet.success == 0 )
    return (double)calcRet.value;
    but remember that you will need to use try/catch constructions and locate the declarations of variables accordingly.
An RPC CalcServer

Now take a look at docount ( from the counter server ) and see how the actual server relates to the server stub (couServer). Save this file (the docount file) in your rpc directory, naming it CalcServer.java, and base your RPC server on this file:

  • Get the package right.
  • Rename the class and change all the other references to “docount” to “CalcServer”
  • It will now extend calculatorServer , which is the server stub that you have generated.
  • The methods need to be defined along the lines of:
    public calcReturn add( calcArgs args ){
    }
  • What happens in these methods is more or less the mirror image of what happened in the impl:
  • Extract the arguments from args

float a = args.x;

float b = args.y;

  • Create a brand new calcReturn object

calcReturn calcRet = new calcReturn();

  • Perform the operation

calcRet.value = a+b;

  • Assign the appropriate fields:
  • success = 0 is used to indicate success i.e.calcRet.success = 0;
  • The value field holds the result of your calculation (done that with calcRet.value = a+b;)
  • The errMsg should be set to “” i.e. calcRet.errMsg = "";
  • Return this object.

return calcRet;

To test the Lab:

  • Start the Portmapper first,
  • Run the server (rpc.CalcServer)
  • Run the client (Calc).

2. Lab3b RMI Calculator

Getting familiar with RMI

Copy the example code from Lab3b\score

Change to your score directory.

Compile the code
javac *.java

Generate stub and skeleton for the remote RMIServer object
rmic -keep RMIServer
- the keep option means that the intermediate, machine written .java files will be kept. Take a look at them.

Generate stub and skeleton for the remote Score object
rmic -keep Score

Start the rmiregistry service
start rmiregistry

Start the server
java RMIServer

In a separate window launch the client
appletviewer RMIClient.html

Understand what the application sets out to achieve and then look for the following parts of the code:

  • The server registers the RMIServer object
  • Inspect the RMIServer interface - it is only concerned with delivering remote object references to the client. These are ScoreInterface references which are wrappers for Score objects on the server
  • Note where the server builds the array of Score objects
  • Note how the client picks up this array but stores ScoreInterface objects rather than Score objects. To do this it must resolve the original reference to "scores" in the RMI Registry.
  • Inspect the ScoreInterface. This defines the methods that can be remotely invoked on Score objects. The two methods offer a short form ( getKey()) and a long form ( getScore()) of the information about the game
  • Note that the Choice object ( the drop down list ) is filled with text obtained by invoking getKey() on the remote Score.
  • The real work is done by FetchScore() in the Button handler. Note again that this is done by invoking a method on the remote Score object.

RMI Calculator

Add a new distribution to your Calculator – name the package rmi and the constant in CalculatorImpl RMI. This will identical to the other distributions you have added with one exception:

  • RMI is not implemented in the Microsoft JVM. This means that distAllowed()in CalculatorImplFactorymust return SUN( rather than SUN || MS ) for the new RMI case.
Making your RMI CalcServer

Define an interface for your RMI CalcServer:

  • Take a copy of one of the Interface files from the scores example
  • Make sure it is in the right package and directory
  • Modify this to implement the calculator functions ( I used CalculatorInterface as the name for this class )

Create a CalcServer class in your rmi package. You could start with a copy of RMIServer from the scores example and see what needs changing:

  • Set the package correctly
  • You will need to implement the interface you have just defined.
  • Your main() method and default constructor will be much like those in RMIServer with some small obvious changes
  • Your method definitions will become really simple because RMI understands how to send and receive doubles. Bear in mind that each of these methods must be declared with throws RemoteException Add some printouts to indicate the method has been called.
  • The name of the server ( as registered with the Registry ) is given by the SERVERNAME variable – I used “calc”

Use the rmic compiler to generate the stubs.

  • As with the rpcgen compiler you need to take steps to make sure that the stubs are in the correct package and the correct directory. Working from the Calculator directory ( the level above the rmi package directory ) you need to enter the following:
    rmic –keep –d . rmi.CalcServer

This will generate .class and .java files in the rmi package directory

Start up your server:

  • Use
    start rmiregistry
    to start the Registry service ( you must restart it from the correct – top level – directory )
  • Use
    java rmi.CalcServer
    to run your server – you should see the “Server ready” message
Making your RMI Impl

Create your rmi.PlainCalculatorImpl class. You can base this on your rpc.PlainCalculatorImpl looking at the RPCClient (PlainCalculatorImpl.java)class in the scores example to see what is needed for rmi

  • Get the package right
  • Replace the JRPC import with a java.rmi.* import
  • Define the URL variable to point to the Registry server name you chose above. Just set it to “calc” because the host portion will be set from the user input dialog.
  • The variable that represents the remote connection ( of type calculator in the RPC example ) needs to be of the type of the interface that you defined earlier ( I used CalculatorInterface )
  • newConnection() needs to do what is done in the RPCClient example to form a connection. The remote connector variable must be the one you have declared in the previous step and the URL that you want to connect to should be built up from rmi:// ,the passed in hostname and the name of the actual server ( calc )

sample code: ci = (CalculatorInterface) Naming.lookup("rmi://"+host+"/"+URL);

  • reuseConnection() needs to set the remote connector variable to be equal to the already created remote connector
  • Your add, subtract etc need to be modified to pass and return the appropriate variable types and to invoke on the remote connector.

3. Report

  • Write a concise precise report on the Lab3 (purpose, description, modifications/improvements).
  • Also include salient differences between RPC/RMI approach versus socket based (Lab2)