DeXteR

Tutorial

This section is intended to provide a step-by-step walk through of how to transform a simple RMI application to use a declarative approach. Though the code is replicated in this tutorial, you can also download the tutorial source files here.

Step 1 - Download the library

Before starting, head over to the Download section and grab the DeXteR library. The library contains both source and class files, so if you're more adventurous than others please feel free to take a look at the implementation. A word of caution though: this is a research project and the source code has not been refactored to production quality.

Also, make sure that you have the following libraries installed.

Step 2 - Install the library

Installing the library is simple. Just unjar the file into your desired project directory. Note that if you try to run things by simply putting the jar on the classpath, you'll likely get an error when you try to run the modified RMI program.

Step 3 - Create the parameter class

We first want to create a simple class to use as a parameter type. The class, A, will contain a String object, an instance of A, and a method, setMessage, to modify the value of the contained string object. The code for the parameter class is as follows:

package tutorial.test;
import java.io.Serializable;
import java.rmi.Remote;



public class A implements Serializable {

	private static final long serialVersionUID = 7180474689028165865L;
	private String _message;
	private A _next;

	public A(){
	}
	
	public A(String message) {
		this.setMessage(message);
		this._next = null;
	}
	
	public A(String message, A next) {
		this.setMessage(message);
		this._next = next;
	}
	
	public String toString() {
		return getMessage();
	}


	public void setMessage(String message) {
		this._message = message;
	}


	public String getMessage() {
		return (null != _next) ?
				_message + " " + _next.getMessage() : _message;
	}
	
	public A getNext(){
		return _next;
	}
	
}

Note that this class contains no Java RMI information, and we are not going to define a Remote interface or anything.

Step 4 - Create a remote interface

In order to keep things simple, let us create a remote interface IServer with with only one remote method, foo. This method will take three separate objects all of type A, but it will declare each parameter passed using a different passing semantics. The code for the remote interface is as follows:

package tutorial.test;

import java.rmi.Remote;
import java.rmi.RemoteException;

import org.exrpp.annotations.*;

public interface IServer extends Remote{
	public void foo(@Copy A p, @RemoteReference A q, @CopyRestore A r) throws RemoteException;
}
   

Step 5 - Create a server

Our remote server implements the remote interface IServer. The foo method simply calls setMessage on all its parameters. Note that since setMessage will change the string member field of x A, the changes will be reflected on the client-side instance if the parameter is passed by remote-reference or by copy-restore, but not if it's passed by copy. Since A is not remote (and copy-restore is not built into RMI), by default the standard RMI runtime would pass all three parameters by copy. The code for the server is as follows:

package tutorial.test;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

import org.exrpp.annotations.*;

public class Server implements IServer{

	private static final long serialVersionUID = 8652760465703039685L;

	protected Server() throws RemoteException {
		super();
	}
	
	public void foo(@Copy A p, @RemoteReference A q, @CopyRestore A r) throws RemoteException{
	
		System.out.println("[INFO] p before change:" + p);
		p.setMessage("BYE");
		System.out.println("[INFO] p after change:" + p + "\n");

		System.out.println("[INFO] q before change:" + q);
		q.setMessage("BYE");
		System.out.println("[INFO] q after change:" + q + "\n");
		
		System.out.println("[INFO] r before change:" + r);
		r.setMessage("BYE");
		System.out.println("[INFO] r after change:" + r + "\n");
		
	}

	public static void main(String [] args) throws Exception{
		Server s = new Server();
		Registry registry = LocateRegistry.createRegistry(1098);
		System.out.println("Registry found");
		IServer stub = (IServer) UnicastRemoteObject.exportObject(s,0);
		System.out.println("Exported object");
		registry.rebind("s", stub);
		System.out.println("Bound s");
	}

}

Step 6 - Create a client

We have a server with a remote method and a simple parameter class, now all we need is a client which can test that the server performs as expected. The Client class will create three instances of A, print their initial content, call foo on them and then print their new content. If it works, the remote-reference and copy-restore parameters should be changed while the copy parameter should be the same on the client side. The code for the client is as follows:

package tutorial.test;


import java.rmi.RemoteException;
import java.rmi.registry.*;
import java.rmi.server.UnicastRemoteObject;

public class Client {

	public static void main(String[] args){
		int count = 0;
		IServer s = null;

		try{
			Registry registry = LocateRegistry.getRegistry(1098);
			s = (IServer) registry.lookup("s");
			
			A p = new A("WELCOME", new A("to", new A("hello", new A("world"))));
			A q = new A("WELCOME", new A("to", new A("hello", new A("world"))));
			A r = new A("WELCOME", new A("to", new A("hello", new A("world"))));
			
			System.out.println("[INFO] p before change:" + p);
			System.out.println("[INFO] q before change:" + q);
			System.out.println("[INFO] r before change:" + r + "\n");
			
			s.foo(p, q, r);
			
			System.out.println("[INFO] p after change:" + p);
			System.out.println("[INFO] q after change:" + q);
			System.out.println("[INFO] r after change:" + r);

		}catch (java.rmi.NotBoundException e) {
			e.printStackTrace();
		}catch (java.rmi.RemoteException e2) {
			e2.printStackTrace();
		}
	}
	
}

Step 7 - Build the application

Up until now, all of the steps were fairly standard procedure for creating an RMI application. Now the cool part comes into play: transforming standard Java RMI into declarative Java RMI. This process will take the tutorial application, process the annotations, and generate the classes and aspects that are responsible for the transformation.

Conveniently, this just requires executing the batch script build.bat (executed from the directory above the tutorial package).

Step 8 - Run the application

Now that you've transformed your application, all that's left is to run it. You can do so using the batch script run.bat, which launches the server and the client applications.