RAFDA/Guide

From SystemsResearch

Jump to: navigation, search

Contents

Overview

RAFDA is a Java-based middleware system that abstracts over object location, making it possible for application code to call a method on a remote object in the same way as for a local object. Its main design principles are:

  • Generality: any object should be remotely accessible, regardless of its class or those of its method parameters and results.
  • Flexibility: it should contain little fixed policy.
  • Interoperability: it should conform to open standards where appropriate.

The current release meets these to a reasonable degree:

  • Classes do not have to extend any particular superclasses, or implement any particular interfaces, in order to be remotely accessible or to be transmitted as parameters or results. This allows third-party code to be used without modification. More importantly, the programmer need not consider distribution while designing core application logic. Current implementation choices do impose some special cases and limitations; these are described in section XXX.
  • RAFDA has pre-configured, but not fixed, policies for controlling how objects are transmitted across the network and how they are encoded. A flexible policy framework supports fine-grain control over these aspects when desired.
  • RAFDA inter-operates with Web Services: any object that is remotely accessible can also be accessed as a Web Service.

Getting Started

Default Policies

Out of the box, without any application-specific policy configuration, RAFDA can be used to run non-distribution-aware code. This requires the programmer to augment the non-distributed application with harness code that makes one or more objects remotely accessible, establishes connections between them, and then calls the desired application method.


In this mode RAFDA functions as follows:

  • Values of primitive type are transmitted by value.
  • Objects are transmitted by reference.


Exposing an Object for Remote Access

This section shows how an instance of an arbitrary class, Person, can be exposed as a Web Service. The Person code is as follows:

public class Person
    public String name = null;
    
    public Person(String name) {
        this.name = name;
    }
        
    public Person() {
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}

The Person class has not been written with any consideration for remote access; however, an instance of Person can be deployed as a Web Service using the RRT. To deploy an object the following is required:

  • a reference to the object to deploy
  • a list of the methods within this object that should be remotely accessible
  • a name for the Web Service

To deploy an object, the server makes a single call to the expose method in the RafdaRunTime class:

void expose(Object objectToExpose, Class<?> remoteType, String serviceName);

The first argument specifies the methods within the object that should be remotely accessible. A list of methods can be unambiguously specified using a Java class or interface in the form of an instance of java.lang.Class (note that interfaces in Java are still represented by java.lang.Class objects). It is important to note that the deployed object does not need to extend or implement the class or interface specified by the java.lang.Class object. This type is known as the deployment interface though it can be a class or an interface. If a class is used then the code in the class is ignored; only the method signatures are relevant. Remote reference holders believe that the type of the deployed object is that of the deployment interface, irrespective of its actual class. In this example, all methods in the object are to be deployed and so the object’s own class can be used.

The second argument is a reference to the object to be deployed, and the third is a name for the deployed Web Service.

Notice the call RRT.get().startup() this is a call to tell the RRT's connection listener to be activated. Without this call the RRT will not accept incoming connections. This is a requirement of all servers and allows the server to expose objects before allowing clients to connect.

The server code follows:

package uk.ac.stand.dcs.rafda.rrt;

public class Server {
    public static void main(String[] args) throws Exception {
        Diagnostic.setLevel(Diagnostic.FINAL);
        Person p = new Person("Scott");
        RRT.get().expose(p, Person.class, "somePerson");
        RRT.get().startup();
    }
}

When this server class is compiled and run, the following output is produced by the RRT, as a result of the diagnostic level 'FINAL' that is specified:

uk.ac.stand.dcs.rafda.rrt.infrastructure.RafdaRunTimeImpl::startConnectionListener : RRT started on anya:5001
uk.ac.stand.dcs.rafda.rrt.infrastructure.RafdaServices::expose : Exposed instance of (class example01.Person) with remote type
  (class example01.Person) and with name (somePerson). UUID = b41acc8b-3573-4595-b027-045a4ebf27d

In this example and all others throughout the guide the following line must be added to obtain the diagnostic output shown here:

Diagnostic.setLevel(Diagnostic.FINAL);

The above output was produced by an RRT running on machine anya on port 5001. Indeed, all example output in this guide is generated from machine anya using the default port configuration. The manner in which the RRT determines which network interface and port to bind to, and how this can be overridden, is described later. The hexadecimal string is an automatically generated UUID. Note that this output states the class of Person to be example01.Person, indicating that the Person class is in package example01. This reflects the actual output that will be obtained when running the examples. The package statements have been omitted from the code here for brevity.

The Web Service is now deployed and ready for use via the following URLs:

http://anya:5001/somePerson
http://anya:5001/b41acc8b-3573-4595-b027-045a4ebf27d

which can be more generally specified as:

http://<hostname>:<port>/<name>
http://<hostname>:<port>/<uuid>

WSDL for the deployed service can be obtained in the standard manner by appending ?wsdl to these URLS, for example:

http://anya:5001/somePerson?wsdl

The above code is included in sample.src.example01 supplied with rrt.jar.

If the RAFDA custom class loader is not employed, as described later, then some limitations apply to the objects that can be deployed:

  • A deployed object must have a no-arguments constructor.
  • A final class cannot be used as the deployment interface.
  • No direct field access should be performed on a remote reference; instead, get and set methods must always be used. Such direct field access operations are possible but the semantics are undefined.

The first and second limitations can be overcome using the RAFDA custom class loader.

Remotely Accessing an Exposed Object

A client can obtain a remote reference to an exposed object based on its name, the name of the machine and port on which the RRT that exposed it is running. The client makes a call to the RafdaRunTime method getObjectByName() and specifies the host name and port of the RRT to connect to, along with the name of the exposed object. The returned object can be cast to the correct type. Note that unlike many middleware systems, the client does not need to refer to remote objects using interface types. The class of the remote reference is that of the interface with which the remotely referenced object was exposed, not the actual class of the exposed object.

public static IRafdaRunTimeRemote getRemote(InetSocketAddress isa);
Object getRemoteReference(String serviceName);

Note that the RRT binds to a specific interface on its host, which is displayed at startup:

startConnectionListener : RRT started on <hostName>:<Port>

On a call to getObjectByName(), it is this hostname that must be specified; generally the RRT does not bind to the localhost interface. Network interface bindings are discussed later.

The following client code obtains a remote reference to the Person object exposed by the server, and calls some methods on it. To the client it is indistinguishable whether the referenced object is local or remote.

import uk.ac.stand.dcs.rafda.rrt.RRT;

public class Client {
    public static void main(String[] args) throws Exception {
        Person p = (Person) RRT.getRemote(new InetSocketAddress("anya",5001)).getRemoteReference("somePerson");
        System.out.println("p1 name is " + p.getName());
        p.setName("Stuart");
        System.out.println("p1 name is " + p.getName());
    }
}

The above code is also included in sample.src.example 01 supplied with rrt.jar.

Pass-by-value vs. Pass-by-reference

During remote method call, the arguments and return values that cross address-space boundaries may be primitive types or reference types. Primitive types are immutable in Java and so are always passed by-value. Interface types can be passed across the network by-value or by-reference though Java local semantics are pass-by-reference. The RRT is capable of passing objects both by reference and by value.

Conventional Web Service semantics are pass-by-value and so if the client accessing the a deployed object in an RRT is a client implemented using another Web Service technology the RRT will employ standard pass-by-value Web Service semantics. The RRT can serialize instances of any class for transmission across the network using automatically generated custom serializers. If an object is to be transmitted by-value then some limitations apply:

  • The object cannot make use of native code.
  • All fields must be publicly accessible. This limitation can be overcome using the RAFDA custom class loader, discussed later.

If the client is also executing within an RRT, then the system behaves as a Distributed Object Model and will default to pass objects by-reference in order to preserve local semantics. The parameter passing semantics are completely under the control of the programmer and the manner in which they are altered is described later. If an object is passed by-reference then it must be remotely accessible to the remote reference holder. The RRT must therefore automatically deploy objects that are passed by-reference. The semantics of this are discussed after the following example.

This example illustrates the pass-by-reference semantics employed when using the RRT as a Distributed Object Model. The server deploys a Person instance as before, though in this case the Person holds a reference to a Dog instance. The revised Person class follows:

<div id="example2" />

public class Person {

    public String name = null;
    
    public Dog dog = null;
    
    public Person(String name, Dog dog) {
        this.name = name;
        this.dog = dog;
    }
    
    public Person() {
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String string) {
        name = string;
    }
    
    public Dog getDog() {
        return dog; 
    }
    
    public void setDog(Dog d) {
        dog = d;
    } 
}

The Dog class is as follows:

public class Dog { 

    public String name = null; 
    
    public int age = 0; 
    
    public Dog() { 
    } 
    
    public Dog(String name, int age) { 
        this.name = name; 
        this.age = age; 
    } 
    
    public String getDogName() {
        return name; 
    } 
    
    public void setDogName(String n) { 
        name = n; 
    } 
    
    public int getAge() { 
        return age; 
    } 
    
    public void setAge(int a) { 
        age = a; 
    } 
    
    public void sayHello() { 
        System.out.println("Dog is " + name + " aged " + age); 
    } 
}

An instance of a Person with a Dog is instantiated in a server as follows:

import uk.ac.stand.dcs.nds.util.Diagnostic;
import uk.ac.stand.dcs.rafda.rrt.RRT;
import uk.ac.stand.dcs.rafda.rrt.infrastructure.RRTConfiguration;

public class Server {
     public static void main(String[] args) throws Exception {
            RRTConfiguration.allowAutoExposure = true;
            Diagnostic.setLevel(Diagnostic.FINAL);
            Person p = new Person("Scott", new Dog("serverDog", 10));
            RRT.get().expose(p, Person.class, "somePerson");
     }
}

The following client is created to access the remote Person, obtain a reference to its Dog instance and perform some operations on that Dog:

import java.net.InetSocketAddress;
import uk.ac.stand.dcs.rafda.rrt.RRT;
import uk.ac.stand.dcs.rafda.rrt.infrastructure.RRTConfiguration;

public class Client {
    private static final String HOSTNAME = "anya";

    public static void main(String[] args) throws Exception {
            RRTConfiguration.allowAutoExposure = true;
            Diagnostic.setLevel(Diagnostic.FINAL);
            Person p = (Person) RRT.getRemote(new InetSocketAddress(HOSTNAME, 5001)).getRemoteReference("somePerson");
            p.getDog().sayHello();
            Dog d = new Dog("clientDog", 5);
            p.setDog(d);
            p.getDog().sayHello();
            System.out.println("dog is " + d);
            System.out.println("dog is " + p.getDog());
            System.out.println("dog class is " + d.getClass());
            System.out.println("dog class is " + p.getDog().getClass());
            System.out.println("dog == dog ? " + (d == p.getDog()));
    }
}

This client first accesses the Person’s existing Dog, which is server-side. It then creates a new Dog and sets the Person’s Dog to be this new Dog. The annotated server output follows:

RafdaRunTimeImpl::startConnectionListener : RRT started on anya:5001
RafdaServices::expose : Exposed instance of (class example02.Person) with remote type (class example02.Person) 
       and with name (somePerson). UUID = 087b709d-79f0-4713-882e-9472ff20f762
RafdaRunTimeImpl::determineReferenceType : Automatically exposing the object of class (example02.Dog) with L-type (example02.Dog)                    
       using the reference type (example02.Dog). Calling toString() on this object results in:    (example02.Dog@15db314)
RafdaServices::expose : Exposed instance of (class example02.Dog) with remote type (class example02.Dog) 
       and with no name. UUID = 2cc8f31b-618e-4752-a843-5442f8cc16b1 
Dog is serverDog aged 10

The annotated client output follows:

RafdaRunTimeImpl::determineReferenceType : Automatically exposing the object of class (example02.Dog) with L-type (example02.Dog) 
        using the reference type (example02.Dog). Calling toString() on this object results in: (example02.Dog@17f409c)
RafdaRunTimeImpl::startConnectionListener : Port 5001 is busy. Automatically choosing port: Trying 5002
RafdaRunTimeImpl::startConnectionListener : RRT started on anya:5002
RafdaServices::expose : Exposed instance of (class example02.Dog) with remote type (class example02.Dog) and with no name. 
        UUID = 1084356c-9236-4481-9b19-1d7a50c1a083
Dog is clientDog aged 5
dog is example02.Dog@17f409c
dog is example02.Dog@17f409c
dog class is class example02.Dog
dog class is class example02.Dog
dog == dog ? true

This code is in sample.src.example02 supplied with rrt.jar.

Exposure Semantics

As the client output in example 02 (above) shows, the Dog object had to be exposed in order to be passed by reference. This can be done manually, or as in the above example, by turning auto-exposure on. These options are coded as follows:

RRTConfiguration.allowAutoExposure = true;
RRT.get().expose(d, Dog.class, "exposedDog");

When automatically exposing an argument or return value during a remote method call, the RRT decides which exposure interface is appropriate using the following rules:

  1. If the object is already exposed using its own class as exposure interface, then no steps need to be taken.
  2. The object may already be exposed using another exposure interface or not exposed at all. The RRT checks the signature of the argument or return value in the method being called and exposes the object using that signature class as the exposure interface. For example, if a method putFish(Fish f) is called and an instance of BigFish (where BigFish is a subtype of Fish) is supplied then the BigFish instance will be automatically exposed using Fish as the exposure interface.

Exposing a Single Object with Multiple Exposure interfaces

A single object can be exposed multiple times under different exposure interfaces. Provided there is a structurally equivalent method in the class of the object to be exposed for every method in the deployment interface, the exposure will succeed. The exposed object does not need to be of a class that extends or implements the exposure interface. Example 03 consists of a Student exposed in multiple different ways. The Student class is defined as follows:

<div id="example03" />

public class Student extends Person {
    private int matric = 33747;

    public int getMatricNumber() {
        return matric;
    }
    public boolean isMammalian() {
        return true;
    }
 
    public String toString() {
        return name;
    }
}

Several interfaces are defined, all of which contain methods present in the Student class:

public interface StudentInterface {
    public String getName();
    public int getMatricNumber();
}

public interface PersonInterface {
    public String getName();
}

public interface MammalInterface {
    public boolean isMammalian();
}

A Student is instantiated and exposed three times using different exposure interfaces. A single object appears as if it is three separate Web Services as it is exposed multiple times using these different exposure interfaces (actually, interfaces in this case). Note that the Student does not need to implement the interface under which it is exposed.

import uk.ac.stand.dcs.nds.util.Diagnostic;
import uk.ac.stand.dcs.rafda.rrt.RRT;

public class Server {
    public static void main(String[] args) throws Exception {
        Diagnostic.setLevel(Diagnostic.FINAL);
        Student s = new Student();
        RRT.get().expose(s, Student.class, null);
        RRT.get().expose(s, PersonInterface.class, "person");
        RRT.get().expose(s, MammalInterface.class, "mammal");
        RRT.get().expose(s, StudentInterface.class, "student");
    }
}

The client then obtains three distinct remote references to the three different Web Services. To the client, each reference appears to reference a distinct object that is typed as the respective exposure interface.

import java.net.InetSocketAddress;
import uk.ac.stand.dcs.nds.util.Diagnostic;
import uk.ac.stand.dcs.rafda.rrt.RRT;

public class Client {
    private static final String HOSTNAME = "anya";

    public static void main(String[] args) throws Exception {
        Diagnostic.setLevel(Diagnostic.FINAL);
        PersonInterface p = (PersonInterface) RRT.getRemote(new InetSocketAddress(HOSTNAME, 5001)).getRemoteReference("person");
        MammalInterface m = (MammalInterface) RRT.getRemote(new InetSocketAddress(HOSTNAME, 5001)).getRemoteReference("mammal");
        Person pc = (Person) RRT.getRemote(new InetSocketAddress(HOSTNAME, 5001)).getRemoteReference("student");
        
        System.out.println(p.getName());
        System.out.println(pc.getName());
        System.out.println(m.isMammalian());
    }
}

RAFDA Rules and Limitations

A number of special cases are imposed by the rules of the Java language:


Examples:

See PolicySpecialCaseTests.getConstructorCallingExposedMethodByReference() (follow method name link for source)

PolicySpecialCaseTests.getPrivateConstructorByReference

ConnectionTests.getAnonymousClassExposedAsParameterizedInterface

ConnectionTests.compareToOverloadedImplementingComparable

ConnectionTests.getDynamicallyGeneratedClassExposedAsInterfaceByValue

Personal tools