1.45k likes | 1.58k Views
Part 5: Java RMI—Remote Method Invocation. CIS 5930-04 – Spring 2001. http://aspen.csit.fsu.edu/it1spring01 Instructors: Geoffrey Fox , Bryan Carpenter Computational Science and Information Technology Florida State University Acknowledgements: Nancy McCracken
E N D
Part 5: Java RMI—Remote Method Invocation CIS 5930-04 – Spring 2001 http://aspen.csit.fsu.edu/it1spring01 Instructors: Geoffrey Fox , Bryan Carpenter Computational Science and Information Technology Florida State University Acknowledgements: Nancy McCracken Syracuse University dbc@csit.fsu.edu
Remote Method Invocation • Java RMI is a mechanism that allows a Java program running on one computer (e.g., the client) to apply a method to an object on adifferent computer (e.g., the server). • In itself, the syntax of the remote invocation looks exactly like an ordinary Java method invocation. The remote method call can be passed arguments computed in the context of the local machine. It can return arbitrary values computed in the context of the remote machine. The RMI system transparently forwards these arguments and results. • RMI is an implementation of the of the Distributed Object programming model—similar to CORBA, but simpler, and specialized to the Java language dbc@csit.fsu.edu
Example • Assume code running in the local machine holds a remote reference to an object obj on a remote machine: res = obj.meth(arg) ; obj ResType meth(ArgType arg) { . . . return new ResImpl(. . .) ; } Local Machine Remote Machine dbc@csit.fsu.edu
Central Components of Java RMI • Remote objects—these are normal Java objects, but their class extends some RMI library class that incorporates support for remote invocation. • Remote references—object references that effectively refer to remote objects, typically on a different computer. • Remote interfaces—normal Java interfaces, that specify the “API” of a remote object. They should extend the marker interface, java.rmi.Remote. The remote interface must be known to both the local and remote code. dbc@csit.fsu.edu
Some Supporting Technologies • Registries—places where the local machine initially looks to find a reference to a remote object. • Serialization—reduction of Java objects to a representation that can be communicated as a byte stream (for arguments and results). • Dynamic class loading—needed in various places. One example is when a remote method returns an object whose class (e.g. ResImpl) was not previously known on the calling machine. Also used for stubs—see later. • Security manager—used to control the behavior of code loaded from a remote host. dbc@csit.fsu.edu
Related Approaches to Networking • Sockets • Traditionally quite hard to program, although the java.net package makes it relatively easy to establish a socket network connection to another host. Communication takes place via streams, but you must define the detailed protocols for message exchange yourself. • High-level compared with Unix sockets, but low-level compared to RMI. • Remote Procedure Call(RPC) • An earlier UNIX protocol to allow calling remote procedures. • Programmers register their application with a host port mapper. • Protocols for parameter-passing support a limited number of types. • CORBA • See next semester’s course. . . dbc@csit.fsu.edu
References • Core Java 2, Volume II, Chapter 5: “Remote Objects”. • Java RMI, Troy Bryan Downing, IDG books, 1998. • “Getting Started Using RMI”, and other documents, at: http://java.sun.com/products/jdk/rmi/ dbc@csit.fsu.edu
Getting Started dbc@csit.fsu.edu
The Remote Interface • In RMI, a common remote interface is the minimum amount of information that must be shared in advance between “client” and “server” machines. It defines a high-level “protocol” through which the machines will communicate. • A remote interface is an ordinary Java interface, which must extent the marker interface java.rmi.Remote. • All methods in a remote interface must be declared to throw the java.rmi.RemoteException exception. dbc@csit.fsu.edu
A Simple Example • A file MessageWriter.java contains the interface definition: import java.rmi.* ; public interface MessageWriter extends Remote { void writeMessage(String s) throws RemoteException ; } • This interface defines a single remote method, writeMessage(). dbc@csit.fsu.edu
java.rmi.Remote • The interface java.rmi.Remote is a marker interface. • It declares no methods or fields; however, extending it tells the RMI system to treat the interface concerned as a remote interface. • In particular we will see that the rmic compiler generates extra code for classes that implement remote interfaces. This code allows their methods to be called remotely. dbc@csit.fsu.edu
java.rmi.RemoteException • Requiring all remote methods be declared to throw RemoteException was a philosophical choice by the designers of RMI. • RMI makes remote invocations look syntactically like local invocation. In practice, though, it cannot defend from problems unique to distributed computing—unexpected failure of the network or remote machine. • Forcing the programmer to handle remote exceptions helps to encourage thinking about how these partial failures should be dealt with. • See the influential essay: “A Note on Distributed Computing” by Waldo et al, republished in The Jini Specification: http://java.sun.com/docs/books/jini dbc@csit.fsu.edu
The Remote Object • A remote object is an instance of a class that implements a remote interface. • Most often this class also extends the library class java.rmi.server.UnicastRemoteObject. This class includes a constructor that exports the object to the RMI system when it is created, thus making the object visible to the outside world. • Usually you will not have to deal with this class explicitly—your remote object classes just have to extend it. • One fairly common convention is to name the class of the remote object after the name of the remote interface it implements, but append “Impl” to the end. dbc@csit.fsu.edu
A Remote Object Implementation Class • The file MessageWriterImpl.java contains the class declaration: import java.rmi.* ; import java.rmi.server.* ; public class MessageWriterImpl extends UnicastRemoteObject implements MessageWriter { public MessageWriterImpl() throws RemoteException { } public void writeMessage(String s) throws RemoteException { System.out.println(s) ; } } dbc@csit.fsu.edu
Remarks • The constructor MessageWriterImpl() has an empty body. But recall that if there is no explicit constructor invocation in the body of a subclass constructor, it implicitlyinvokes super(). • Hence the vital constructor of UnicastRemoteObject is called. • This constructor is declared to throw RemoteException. The MessageWriterImpl() constructor must be declared to throw this exception in turn, otherwise there will be a compiler error message. • Of course the class must also define all the methods of the remote interface MessageWriter, which it implements. dbc@csit.fsu.edu
Compiling the Remote Object Class • To compile classes that implement Remote, you must use the rmic compiler. The reasons will be discussed later. For example: sirah$ rmic MessageWriterImpl dbc@csit.fsu.edu
Client and Server Programs • We have completed the Java files for the remote object class itself, but we still need the actual client and server programs that usethis class. • In general there are some pieces of administrivia one has to deal with—publishing class files and installing security managers. • To minimize distractions, we initially make the simplifying assumption that both client and server have copies of all class files for MessageWriter (e.g., they may share access through shared NFS directories). • Then we also don’t need a security manager, because all codeis “local”, and therefore trusted. dbc@csit.fsu.edu
A Server Program • We assume the file HelloServer.java contains the class declaration: import java.rmi.* ; public class HelloServer { public static void main(String [] args) throws Exception { MessageWriter server = new MessageWriterImpl() ; Naming.rebind(“messageservice”, server) ; } } dbc@csit.fsu.edu
Remarks • To avoid cluttering this illustrative code with try-catch statements, we simply have the main() method throw all exceptions (not good practice in general). • This program does two things: • It creates a remote object with local name server. • It publishes a remote reference to that object with external name “MessageWriter”. • The call to Naming.rebind() places a reference to server in an RMI registry running on the local host (i.e., the host where the HelloServer program is run). • Client programs can obtain a reference to the remote object by looking it up in this registry. dbc@csit.fsu.edu
A Client Program • We assume the file HelloClient.java contains the class declaration: import java.rmi.* ; public class HelloClient { public static void main(String [] args) throws Exception { MessageWriter server = (MessageWriter) Naming.lookup( “rmi://sirah.csit.fsu.edu/messageservice”) ; server.writeMessage(“Hello, other world”) ; } } dbc@csit.fsu.edu
Remarks • Again the program does two things: • It looks up a reference to a remote object with external name “MessageWriter”, and stores the returned reference with local name server. • Finally (!), it invokes the remote method, writeMessage(), on server. • The call to Naming.lookup() searches ina remoteRMI registry. Its argument is a URL,with protocol tag “rmi”. • This example assumes the remote object lives on the host “sirah”, and has been registered in the default RMI registry (which happens to listen on port 1099) on that machine. dbc@csit.fsu.edu
Compiling and Running the Example • Compile HelloServer and HelloClient on their respective hosts, e.g.: sirah$ javac HelloServer merlot$ javac HelloClient • Either ensure client and server share the current directory, or copy all files with names of the form MessageWriter * .class to the client’s current directory. • Then. . . dbc@csit.fsu.edu
Running HelloClient/HelloServer dbc@csit.fsu.edu
Running HelloClient/HelloServer dbc@csit.fsu.edu
Running HelloClient/HelloServer dbc@csit.fsu.edu
Running HelloClient/HelloServer dbc@csit.fsu.edu
Running HelloClient/HelloServer dbc@csit.fsu.edu
Remark on Using the RMI Registry • In this example we ran the RMI registry on its default port. • In general this is probably a bad idea, especially if the server is used by many people, because there is no mechanism to prevent interference. • It is better to start a registry a non-default port number of your own choice, e.g.: sirah$ rmiregistry 4956 & • The Naming calls become, e.g.: Naming.rebind(“rmi://sirah.csit.fsu.edu:4956/messageservice”, server) ; Naming.lookup(“rmi://sirah.csit.fsu.edu:4956/messageservice”) ; dbc@csit.fsu.edu
The Mechanics of Remote Method Invocation dbc@csit.fsu.edu
Is RMI a Language Extension? • Invocation of a method on a remote object reproduces the “look and feel” of local invocation amazingly well. • Yet the internal mechanics of remote invocation are muchmore complex than local invocation: • Arguments—which may be objects of arbitrary complexity—are somehow collected together into messages suitable for shipping across the Internet. • Results (or exceptions) are similarly shipped back. • Perhaps surprisingly, RMI involves essentially nofundamental modification to the Java language, compiler, or virtual machine. • The illusion of remote invocation is achieved by clever libraries, plus one relatively simple “post-processor” tool (rmic). dbc@csit.fsu.edu
Exchanging Remote References • A good feature of RMI is that references to other remote objects can be passed as arguments to, and returned as results from, remote methods. • Starting with one remote object reference (presumably obtained from an RMI registry) a client can, for example, obtain references to additional remote objects—returned by methods on the first one. dbc@csit.fsu.edu
Example: a Printer Directory • Perhaps more relevant on LAN than the Internet, but it illustrates the idea: public interface Printer extends Remote { void print(String document) throws RemoteException ; } public interface PrinterHub extends Remote { Printer getPrinter(int dpi, boolean isColor) throws RemoteException ; } • A client might initially obtain a PrinterHub reference from the RMI registry. The remote object contains some table of printers on the network. • An individual Printer interface is returned to the client, according to specifications given in getPrinter(). dbc@csit.fsu.edu
Remote References have Interface Type • This is a powerful feature, but there is one interesting restriction: • If a particular argument or result of a remote method itself implements Remote, the type appearing in the method declaration must be aremote interface. The declared type cannot be a remote implementation class. • We have also seen earlier that the remote object reference returned by Naming.lookup() can be cast to the expected remote interface type. • However, this reference cannot be cast it to the implementation class of the remote object! A ClassCastException will occur if you try. dbc@csit.fsu.edu
Stubs • What this tells us is that, however they are obtained—and however they look—remote references are not, in reality, Java references to remote objects. They are Java references to local objects that happen to implement the same remote interfaces as the remote objects concerned. • The local Java object referenced is actually an instance of a stub class. dbc@csit.fsu.edu
Some Important Parts of RMI • Stubs. • Each remote object class has an associated stub class, which implements the same remote interfaces. An instance of the stub class is needed on each client. Client-side remote invocations are “actually” local invocations on the stub class. • Serialization. • Arguments and results have to be “marshaled”—converted to a representation that can be sent over the Net. In general this is a highly non-trivial transformation for Java objects. Serialization is also used for distributing stubs. • The Server-side “Run-time System”. • This is responsible for listening for invocation requests on suitable IP ports, and dispatching them to the proper, locally resident remote object. dbc@csit.fsu.edu
Architecture Internet Client Server Call stub method locally Call remote object method locally Send marshaled arguments RMI “Run-time” System Remote Object Stub Client Code Send marshaled result or exception Return value or throw exception Return value or throw exception dbc@csit.fsu.edu
The Role of rmic • The only “compiler” technology peculiar to RMI is the rmicstub generator. • The input to rmic is a remote implementation class, compiled in the normal way with javac (for example). • The stub generator outputs a new class that implements the same remote interfaces as the input class. • The methods of the new class contain code to send arguments to, and receive results from, a remote object, whose Internet address is stored in the stub instance. dbc@csit.fsu.edu
Example Operation of rmic • An earlier example of a remote implementation class: public class MessageWriterImpl extends UnicastRemoteObject implements MessageWriter { . . . public void writeMessage(String s) throws RemoteException { . . . } } • We issue the command: rmic –v1.2 –keep MessageWriterImpl • The flag –v1.2 avoids generation of unnecessary code, needed only for backward compatibility (including “skeleton classes”). • The flag –keep causes the intermediate Java source to be retained. • Output files will be MessageWriterImpl_Stub.java and MessageWriterImpl_Stub.class. dbc@csit.fsu.edu
The Generated Stub Class public final class MessageWriterImpl_Stub extends java.rmi.server.RemoteStub implements MessageWriter, java.rmi.Remote { . . . public MessageWriterImpl_Stub(java.rmi.server.RemoteRef ref) { super(ref); } public void writeMessage(java.lang.String $param_String_1) throws java.rmi.RemoteException { try { ref.invoke(this, $method_writeMessage_0, new java.lang.Object[] {$param_String_1}, 4572190098528430103L); } . . . } } dbc@csit.fsu.edu
Remarks on the Stub Class • The stub class includes an inherited field ref,of type RemoteRef. • Essentially the stub class is just a wrapper for this remote reference. • Remote methods are dispatched through the invoke() method on ref. • This is passed an array of Objects holding the original arguments (in general it also returns an Object). • It is also passed arguments to identify the particular method to be invoked on the server. • Essentially the stub wrapper is providing compile-time type safety. The actual work is done in library classes that don’t know the compile-time type in advance. dbc@csit.fsu.edu
Marshalling of Arguments • Objects passed as arguments to invoke() must be marshaled for transmission over the network. • The representation of a Java object inside the Java Virtual Machine is complex: • An object includes references to all its fields, which may themselves be objects. These may in turn reference other objects. • An object will reference its class object, which contains runtime information about the object’s type. • This internal representation is also not standardized—different vendors implementation of the JVM will certainly use different representations. • If objects are to be exchanged between JVMs, we need a standardized way to encode all the information. dbc@csit.fsu.edu
Object Serialization • Java has a general framework for converting objects (and groups of objects) to an external representation that can later be read back into an arbitrary JVM. • This framework is called Object Serialization. • Object serialization is very important to RMI, but it has other applications as well. • For example, a running program can use object serialization to dump the current state of a particular object to a file. Much later, another program can read the file and reincarnate an exact replica of the original object. This is a mechanism for object persistence. dbc@csit.fsu.edu
I/O Streams • The technology for serializing and deserializing objects is found in a pair of the many I/Ostreamclasses of Java. • In general an output stream (for example) can be associated with various targets: • a file, an Internet socket connection, an internal Java array of bytes to which one is writing externally formatted data, etc. • The abstract superclass OutputStream provides low-level write methods like: public void write(byte [] buffer) throws IOException {. . .} Subclasses may override the implementation for a particular output target. • Subclasses may also add extra methods that take more general data, convert them to a byte array, then invoke write() to do the final output. dbc@csit.fsu.edu
Object Streams • ObjectOutputStream is a subclass that adds methods including: public void writeInt(int val) throws IOException {. . .} public void writeFloat(float val) throws IOException {. . .} etc, and most interestingly: public void writeObject(Object obj) throws IOException, . . . {. . .} • Similarly ObjectInputStream extends InputStream and adds: public int readInt() throws IOException {. . .} etc, and: public Object readObject() throws IOException, . . . {. . .} dbc@csit.fsu.edu
Using Object Streams • We can use the writeObject() method of an ObjectOutputStream to write an object to a file, an Internet socket connection, etc. • Later we use the readObject() method of an ObjectInputStream to read an object from the same file, the other end of the socket connection, etc. • When deserialization occurs, a new object is created in the second JVM. As far as possible this is a perfect replica of the the original object. dbc@csit.fsu.edu
Serialization Preserves Object Graphs • Consider this binary tree node class: class Node implements Serializable { Node() {} Node(Node left, Node right) { this.left = left ; this.right = right ; } private Node left, right ; } • We create a small tree, d, by: Node a = new Node(), b = new Node() ; // Leaves Node c = new Node(a, b) ; Node d = new Node(c, null) ; dbc@csit.fsu.edu
Serializing and Deserializing a Tree • Write out the root of the tree: out.writeObject(d) ; a b c d a’ b’ • Read a node later by: • Node e = (Node) in.readObject() ; c’ e • The whole of the original tree is reproduced. Copies a’, b’, c’ of the original sub-nodes are recreated along with e. The pattern of references is preserved. dbc@csit.fsu.edu
Referential Integrity is Preserved • This behavior is not limited to trees. • In this example both b and c reference a single object a. • Again the pattern of links is preserved. When the root object is reconstructed from its serialized form, a single a’, referenced twice, is also created. • Generally referential integrity is preserved amongst all objects written to a single ObjectOutputStream. a c b d a’ c’ b’ e dbc@csit.fsu.edu
The Serializable Interface • Serializable is another marker interface. An object’s class must implement Serializable if it is to be passed to writeObject(). If it doesn’t, a NotSerializableException will be thrown. • Implementing Serializable doesn’t appear to affect the way the Java compiler and JVM treat the class in general—it seemsto be simply a safety feature in ObjectOutputStream. dbc@csit.fsu.edu
Argument Passing in RMI • In general any object-valued argument or result of a remote method must either implement Remote or Serializable. • If the argument or result implements Remote, it is effectively passed by (remote) reference. • If it implements Serializable, it is passed by serialization and copying. Referential integrity is preserved within the limits of the arguments of a single invocation, as described above. dbc@csit.fsu.edu