How to get a stub?
public class Client { public static void main( String[ ] args ) { RemoteMap map = new RemoteMap_Stub( “130.216.38.98”, 1024 ); // Assign map to a stub. try { String number = ( String )map.get( “Dave” ); } catch( RemoteException e ) { System.out.println( “Remote exception: “ + e ); } } public class Server { public static void main( String[ ] args ) { try { RemoteMap map = new RemoteMapAdapter( new Hashtable( ), 1024 ); // Populate the map with data. … } catch( RemoteException e ) { System.out.println( “Remote exception: “ + e ); } }
Specify the IP address and port for the remote object
Specify the port number to listen on
This is a hypothetical implementation and is not supported in Java RMI.
SE 325 Unit 2: Introduction to Middleware with Java RMI 26
Hard-coded location
This technique offers no location transparency Location transparency is desirable for many reasons:
Object migration
There is often a need to move objects across physical machines
Hardwire retiring and repurposing, changes in network administration (e.g. introducing a firewall)
Clients often interact with several remote objects
E.g. consider a banking application with thousands of remote BankAccount objects
One physical machine typically hosts many remote objects
If each remote object has to specify a particular port to listen on, there will likely be port number clashes
SE 325
Unit 2: Introduction to Middleware with Java RMI
27
RemoteObject
«interface» java.rmi.Remote
Given any instance of RemoteObject, you can ask it for its stub
RemoteMap map = new RemoteMapAdapter( new Hashtable( ) ); RemoteMap stub = ( RemoteMap )RemoteObject.toStub( map );
java.rmi.server.RemoteObject +toStub(in obj : Remote) : Remote
java.rmi.server.RemoteServer
Recall that the stub encapsulates the location of the RemoteObject it represents
java.rmi.server.UnicastRemoteObject
RemoteMapAdapter
But how can a client, running in a different JVM, get hold of the stub?
SE 325 Unit 2: Introduction to Middleware with Java RMI 28
A common solution
A common technique in distributed computing which allows a client to acquire a reference to a remote object is to use a naming service
A naming service is a centralised resource that a number of applications use as a “phone book”-like resource. That is, it is an easily locatable and well-known application that maps logical names to actual servers so client programs can easily locate and use appropriate server applications. (Grosso 2002)
In Java RMI, the “phone book” is a white-pages style book where “logical names” are strings Java RMI’s naming service returns a stub object
SE 325 Unit 2: Introduction to Middleware with Java RMI
Finding means retrieval based on case-sensitive string equality
29
Java RMI’s naming service: Registry (Reggie)
Registry is Java RMI’s naming service
Registry specifies the «interface»Remote interface of the naming service The JDK includes «interface» a default Registry implementation, +bind(in name : String, in obj : Remote) +list() : String[ ] RegistryImpl +lookup(in name : String) : Remote
+rebind(in name : String, in obj : Remote) +unbind(in name : String) RemoteException
AlreadyBoundException
NotBoundException
AccessException
SE 325
Unit 2: Introduction to Middleware with Java RMI
30
Reggie
Registry JVM
“my account”
“phone book” : RegistryImpl : BankAccount_Stub
…
: RemoteMap_Stub
RMI run-time
To start up the naming service:
rmiregistry Running registry starts up a new JVM in which a Registry implementation object listens for incoming requests
SE 325 Unit 2: Introduction to Middleware with Java RMI 31
Client JVM 2: lookup
RMI run-time
Registry JVM
: RegistryImpl
Server JVM
: RemoteMapAdapter
RMI run-time
RMI run-time
1: bind
But, using a naming service, we’re faced with the same problem as earlier! How does a server get a stub for the remote Registry object so that it can register a stub for its RemoteMapAdapter object? How does a client get a stub for the remote Registry object so that it can then retrieve a stub for a remote RemoteMap object?
SE 325 Unit 2: Introduction to Middleware with Java RMI 32
Class LocateRegistry
Class LocateRegistry includes a set of (class) methods which return a proxy to a remote Registry object
Returns a stub for a Registry object running on the local machine and listening on port 1099.
java.rmi.registry.LocateRegistry +getRegistry() : Registry +getRegistry(in port : int) : Registry +getRegistry(in port : int, in host : String) : Registry
Specifies the location where the Registry object is expected to be executing
«interface» Registry
RemoteException
Note that a getRegistry call does not actually make a connection to the (remote) Registry object. It simply creates a proxy object for it – this will succeed even if the remote Registry object is not actually running. Therefore, a subsequent method invocation on the stub object returned may fail.
SE 325 Unit 2: Introduction to Middleware with Java RMI 33
Using LocateRegistry
public class Client { public static void main( String[ ] args ) { try { Registry stubForReggie = LocateRegistry.getRegistry( ); RemoteMap stubForMap = ( RemoteMap ) stubForReggie.lookup( “phoneBook” ); stubForMap.get( “Dave” ); } catch( RemoteException e ) { System.err.println( “Remote exception “ + e ); The client, server and } Registry object are } assumed to be public class Server { running on the same public static void main( String[] args ) { physical machine try { RemoteMap map = new RemoteMapAdapter( new Hashtable( ) ); Registry stubForReggie = LocateRegistry.getRegistry( ); stubForReggie.bind( “phoneBook”, map ); } catch( RemoteException e ) { } catch( MalformedURLException e ) { } } } 325 SE Unit 2: Introduction to Middleware with Java RMI
34
2: lookup( “phone book” )
Client JVM Registry JVM
RMI run-time stubForReggie “phone book” stubForMap RMI run-time : RemoteMapAdapter_Stub : RegistryImpl
3: get( “Dave” ) Server JVM
map : RemoteMapAdapter
RMI run-time
stubForReggie
1: bind( “phone book”, map’s stub )
35
SE 325
Unit 2: Introduction to Middleware with Java RMI
Class Naming
Naming is a convenience class which mirrors any Registry object In response to a Naming method call (e.g. lookup), the following happens:
The name (URL) argument passed to a Naming method (e.g. lookup) is parsed and used as the argument to one of LocateRegistry’s get( ) methods LocateRegistry returns a stub object for the remote Registry object The Naming class uses the stub object to invoke the method (e.g. lookup) on the remote Registry object
SE 325
java.rmi.Naming +bind(in name : String, in obj : Remote) +list(in name : String) : String[ ] +lookup(in name : String) : Remote +rebind(in name : String, in obj : Remote) +unbind(in name : String)
java.rmi.registry.LocateRegistry
RemoteException
AlreadyBoundException
NotBoundException
The name argument for all methods identifies both the location of the Registry and the name of the Remote object for which a stub is required.
AccessException
MalformedURLException
In general, use Naming as opposed to LocateRegistry directly.
36
Unit 2: Introduction to Middleware with Java RMI
public class Client { public static void main( String[ ] args ) { try { RemoteMap stubForMap = ( RemoteMap )Naming.lookup( “phoneBook” ); … } catch( RemoteException e ) { /* Will also catch AccessException as this is a subclass of RemoteException. */ } catch( NotBoundException e ) { } catch( MalformedURLException e ) { } }
public class Server { public static void main( String[ ] args ) { try { RemoteMapAdapter = new RemoteMapAdapter( new Hashtable( ) ); Naming.rebind( “phoneBook”, map ); } catch( RemoteException e ) { } catch( MalformedURLException e ) { } } }
SE 325 Unit 2: Introduction to Middleware with Java RMI
Surprisingly (?), this program will not exit immediately after the call to rebind( ).
37
void bind( String name, Remote obj ) throws RemoteException, AlreadyBoundException, AccessException, MalformedURLException
String[ ] list( ) throws RemoteException, MalformedURLException Remote lookup( String name ) throws RemoteException, NotBoundException, MalformedURLException void rebind( String name, Remote obj ) throws RemoteException, AccessException, MalformedURLException
Attempts to return the stub object associated with the name argument Returns an array of names bound within the registry Attempts to bind name to the stub object, obj
unbind( String name ) throws RemoteException, NotBoundException, AccessException, MalformedURLException
Attempts to remove the binding for the specified name within the registry
Attempts to replace the binding for the specified name in the registry with the supplied stub, obj
RemoteException
AlreadyBoundException AccessException
Thrown if there is a problem communicating with the registry Thrown by bind( ) if the name argument is already bound to a stub object
NotBoundException
Thrown if the Registry denies the caller access
Thrown by lookup( ) and unbind( ) if the name specified is not actually bound to a stub Thrown by all of Naming’s methods if the name argument is not formatted correctly
Unit 2: Introduction to Middleware with Java RMI 38
MalformedURLException
SE 325
Format of Naming methods’ name argument
The name argument of Naming’s methods is a URL-formatted string:
//hostname:port-number/service-name E.g. //comp.lancs.ac.uk:1100/MyAccount
Taken together, hosthame and port-number identify the machine on which the Registry implementation is running and the port number it is listening on service-name identifies the logical name of the remote object whose stub is held by the Registry hostname and port-number both have default values:
hostname – “localhost” port-number – 1099
These are all equivalent names
SE 325
{
//localhost:1099/MyAccount //localhost/MyAccount :1099/MyAccount MyAccount
39
Unit 2: Introduction to Middleware with Java RMI
Conclusion to Java’s naming service
The good
A nice solution to the bootstrapping problem in distributed systems
The JDK includes the Registry interface, a default implementation (RegistryImpl) and an associated stub class (RegistryImpl_Stub) Any Java program can communicate with a remote Registry by:
Ease of use Fast
1. Creating a RegistryImpl_Stub instance using class LocateRegistry 2. Using the RegistryImpl_Stub object to access a remote RegistryImpl object to obtain a stub for a remote application object
Application developers simply use the Naming class to interact with the naming service
The bad
No searching facilities Single point of failure Limited scalability
E.g. “give me all the stubs for remote objects that implement the Printer interface”
Flat structure Only Remote objects running on the same physical machine as an RMI registry can be registered with that registry …
Unit 2: Introduction to Middleware with Java RMI 40
SE 325
: Interceptor
Rogue JVM Registry JVM
RMI run-time stubForLegitService “service” stubForReggie RMI run-time : Interceptor_Stub LegitService_Stub : RegistryImpl
2: list( ) 3: lookup( “service” ) 4: rebind( “service”, interceptor’s stub )
: LegitService
Server JVM
RMI run-time
stubForReggie
1: bind( “service”, legitService’s stub ) This forms the basis of a man-in-the-middle attack.
41
SE 325
Unit 2: Introduction to Middleware with Java RMI
Deployment issues
The RMI registry’s JVM needs access to:
The Server’s JVM needs access to:
The compiled remote interface and its implementation The _Skel and _Stub files associated with the implementation The server program
Compiled _Stub files for all remote objects to be registered Plus the compiled interface file and the compiled files for all interfaces and classes the interface depends on
The Client’s JVM:
The compiled remoter interface and associated _Stub The client program
RemoteMapAdapter_Stub RemoteMapAdapter_Skel RemoteMapAdapter Client Server
RemoteMap
42
SE 325
Unit 2: Introduction to Middleware with Java RMI
Server JVM
Client JVM
Registry JVM
Our first Java RMI application involves a bunch of class files – but where should they be deployed?
What are the semantics of parameter passing?
Let’s start by reviewing Java’s model for passing parameters within the same JVM
Values of primitive data types are passed by value Object references are also passed by value, meaning that objects themselves are passed by reference
SE 325
Unit 2: Introduction to Middleware with Java RMI
43
Passing primitive data values
/* Main method. */ … int myCounter = 0; incr( mycounter ); … public void incr( int i ) { i = i + 1; }
incr( )
main( )
i myCounter
1 0 0
When a primitive data value is passed, a separate copy of it is made within the context of the called method. Changes to the local copy of the value are thus not reflected in the original value.
SE 325 Unit 2: Introduction to Middleware with Java RMI 44
Passing object references
Address Object
/* Main method. */ … MutableInteger myCounter = new MutableInteger( 0 ); incr( myCounter ); …
class MutableInteger { private int value; public MutableInteger( int initialValue ) { value = initialValue; }
10A
MutableInteger value 0 1
Heap
…
…
setValue
newValue
1
getValue
incr( ) main( ) i 10A myCounter 10A
}
public void setValue( int newValue ) { value = newValue; } public int getValue( ) { return value; }
When an object reference is passed, any changes to the state of the actual object referenced persist following the method call.
SE 325
public void incr( MutableInteger i ) { i.setValue( i.getValue( ) + 1 ); }
45
Unit 2: Introduction to Middleware with Java RMI
Parameter passing with RMI
With Java RMI, the parameter passing model differs Any “thing” can be passed as a parameter (or returned from a method) so long as it is serializable So what things are serializable?
Instances of stub classes Primitive data values Any object whose class implements java.io.Serializable
SE 325 Unit 2: Introduction to Middleware with Java RMI 46
Parameter passing with RMI
Primitive data values
These are passed by value – this is identical to the single-JVM model for parameter passing
References to non-remote objects
In contrast to the single-JVM model, non-remote objects are passed by value
References to remote objects
A reference to a remote object is of course a stub – the stub is passed by value, meaning that remote objects are effectively passed by reference
SE 325
Unit 2: Introduction to Middleware with Java RMI
47
References to non-remote objects
«interface» java.rmi.Remote «interface» java.io.Serializable
java.rmi.server.UnicastRemoteObject MutableInteger -value : int «interface» SomeService +doSomething(in i : MutableInteger) SomeService_Impl +getValue() : int +setValue(in newValue : int)
public void doSomething throws RemoteException { i.setValue( i.getValue( ) + 1 ); }
SE 325
Unit 2: Introduction to Middleware with Java RMI
48
References to non-remote objects Server JVM
Client JVM
:SomeService_Stub RMI run-time : SomeService_Impl
RMI run-time
/* Client. */ MutableInteger i = new MutableInteger( 0 ); … stub.doSomething( i );
Following the call to doSomething( ), the state of the MutableInteger object is unchanged.
Java’s serialization mechanism is used to create a copy of the MutableInteger referenced by i. The copy is sent to the Server JVM, deserialised, and used as the actual argument in the call on the SomeService remote object. Any changes to the state of the MutableInteger object on the server are local to that copy and not communicated back to the client.
SE 325 Unit 2: Introduction to Middleware with Java RMI 49
References to non-remote objects
«interface» java.rmi.Remote
«interface» java.io.Serializable
/* Client code. */ … MutableInteger i = new MutableInteger( 0 ); MutableInteger j = stub.doSomething( i );
java.rmi.server.UnicastRemoteObject MutableInteger -value : int SomeService_Impl +getValue() : int +setValue(in newValue : int)
«interface» SomeService +doSomething(in i : MutableInteger) : MutableInteger
Following the call to doSomething( ), i and j refer to different MutableInteger objects
public MutableInteger doSomething( MutableInteger i ) throws RemoteException { i.setValue( i.getValue( ) + 1 ); return i; }
With this design, in response to the return statement in doSomething( ), Java’s serialization mechanism makes a new copy of the MutableInteger object on the server and returns this to the client. The stub on the client deserialises the returned value, creating a new copy on the client.
SE 325 Unit 2: Introduction to Middleware with Java RMI 50
References to remote objects
Consider extending the BankAccount interface with an additional operation to transfer funds between accounts
«interface» BankAccount +getBalance() : Money +getName() : String +getNumber() : String +deposit(in amount : Money) +withdraw(in amount : Money) +transfer(in destination : BankAccount, in amount : Money)
class BankAccount_Impl extends UnicastRemoteObject implements BankAccount { … public void transfer( BankAccount destination, Money amount ) { balance.subtract( amount ); destination.deposit( amount ); } }
SE 325 Unit 2: Introduction to Middleware with Java RMI 51
References to remote objects
Stubs, account_1 and account_2, for remote BankAccount objects
BankAccount_Imp l objects
Server JVM
Client JVM
RMI run-time
RMI run-time
/* Client. */ account_1 = ( BankAccount )Naming.lookup( “account_1” ); account_2 = ( BankAccount )Naming.lookup( “account_2” ); Money amountToTransfer = new Money( 100, 0 ); account_1.transfer( account_2, amountToTransfer );
account_1 and account_2 are stubs (i.e. remote object references). In the call to transfer( ), the account_2 stub is serialized with the copy being sent to the BankAccount_Impl object, represented by the account_1 stub, on the server.
SE 325 Unit 2: Introduction to Middleware with Java RMI 52
References to remote objects
A stub that refers to a Remote object which actually runs in the same JVM
Server JVM
Client JVM
account_ 2 destinatio n
RMI run-time
RMI run-time
class BankAccount_Impl extends UnicastRemoteObject implements BankAccount { … public void transfer( BankAccount destination, Money amount ) { balance.subtract( amount ); destination.deposit( amount ); } }
SE 325
destination is a copy of the stub object referred to by account_2 on the client. In this scenario, destination actually represents a BankAccount_Impl object which is local; despite being in the same JVM all method calls made on the stub are handled by the RMI run-time. RMI Unit 2: Introduction to Middleware with Java 53
References to remote objects
You might try and access the implementation directly …
class BankAccount_Impl extends UnicastRemoteObject implements BankAccount { private Money balance; … public void transfer( BankAccount destination, Money amount ) { balance.subtract( amount ); destination.balance.add( amount ); } }
Any good?
SE 325
Unit 2: Introduction to Middleware with Java RMI
54
References to remote objects: substitution
When passing as an argument a local reference to a Remote implementation object (e.g. an instance of a subclass of UnicastRemoteObject), the Java runtime automatically substitutes the object for its stub.
Recall how we used Java RMI’s naming service to register an object
/* Server. */ RemoteMapAdapter map = new RemoteMapAdapter( … ); Naming.rebind( “phoneBook”, map );
Recall too that a Registry object stores stub objects
map, a reference to a RemoteMapAdapter object, is replaced with a reference to a RemoteMapAdapter_Stub object which represents the RemoteMapAdapter It is the stub that is serialized and set to the Registry object
SE 325 Unit 2: Introduction to Middleware with Java RMI 55