This talk summarizes material from
The purpose of this presenation is to provide the step-by-step
instructions and low-level implementation details needed to
understand Java's RMI implementation, so you can get started
on your programming. As always, these slides only serve to
highlight the most important points in the readings. You
should read the cited tutorials while sitting in front of a
computers and taking the steps they indicate. It is the best
way to get comfortable with this (and any) new technology.
package examples.hello;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote {
String sayHello() throws RemoteException;
}
- All methods must throw
RemoteException
since remote calls can fail due to network problems.
- It must be declared
public
.
- The data
type of the remote object must be of the interface type (i.e.,
Hello
), not the implementation type (i.e.,
HelloImpl
).
package examples.hello;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.RMISecurityManager;
import java.rmi.server.UnicastRemoteObject;
/** This class serves both as the remote object implementation and the
object server main. These two functions could be separated. */
public class HelloImpl extends UnicastRemoteObject
implements Hello {
public HelloImpl() throws RemoteException {
super();
}
public String sayHello() {
return "Hello World!";
}
public static void main(String args[]) {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
try {
HelloImpl obj = new HelloImpl();
Naming.rebind("HelloServer", obj);
System.out.println("HelloServer bound in registry");
} catch (Exception e) {
System.out.println("HelloImpl err: " + e.getMessage());
e.printStackTrace();
}
}
}
- The implementation must implement all methods in the
interface otherwise it will be abstract.
- It extends
UnicastRemoteObject
so it uses RMI's
default socket-based transport and runs all the time.
- We could have made it run only when activated (more later).
- Constructor is the same as for a non-remote class.
- Arguments and return values can be anything.
- But, if argument or return value is an object then this
object must implement
java.io.Serializable
- Remote objects are passed just like local objects, by
reference, but it's actually a reference to a stub).
- You can implement other methods but these can only be called
from within the JVM running the service.
The requirement that the registry run on the same machine as
the server is a heavy-handed way of eliminating a
man-in-the-middle attack. In this attack, a hostile client
could find a registry, ask it to list()
all its
registered services, then proceed to rebind()
all
those services so they point to him. By forcing the registry
to run on the same machine as the server this possibility is
eliminated assuming, of course, that we trust all the users of
the machine!
A better solution to this problem would be for the registry to
only service those clients that have authenticated with it
using, for example, a public key encryption algorithm. In
fact, we could use HTTP's TLS for this purpose. (Has anyone
done this already?).
package examples.hello;
import java.applet.Applet;
import java.awt.Graphics;
import java.rmi.Naming;
import java.rmi.RemoteException;
public class HelloApplet extends Applet {
String message = "blank";
Hello obj = null;
public void init() {
try {
obj = (Hello)Naming.lookup("//" +
getCodeBase().getHost() + "/HelloServer");
message = obj.sayHello();
} catch (Exception e) {
System.out.println("HelloApplet exception: " +
e.getMessage());
e.printStackTrace();
}
}
public void paint(Graphics g) {
g.drawString(message, 25, 50);
}
}
- The
lookup
function does two things:
- Constructs a registry stub instance in order to access
the registry.
- Uses this stub to call
lookup
method on
registry.
- When the call is made to
sayHello()
then:
- A remote call is sent to server, by stub.
- RMI serializes "Hello world" string and returns it.
- RMI de-serializes it at the client and places it in
message.
<html>
<head>
<title>Hello World</title>
</head>
<body>
<center> <h1>Hello World</h1> </center>
The message from the HelloServer is:
<p>
<applet codebase="."
code="examples.hello.HelloApplet"
width=500 height=120>
</applet>
</body>
</html>
- The codebase is the directory where the applet will download
the .class files.
package examples.activation;
import java.rmi.*;
import java.rmi.activation.*;
public class ActivatableImplementation extends Activatable
implements examples.activation.MyRemoteInterface {
public ActivatableImplementation(ActivationID id, MarshalledObject data)
throws RemoteException {
super(id, 0);
}
public Object callMeRemotely() throws RemoteException {
return "Success";
}
}
- It needs a two argument constructor.
- It implements the remote interface.
package examples.activation;
import java.rmi.*;
import java.rmi.activation.*;
import java.util.Properties;
public class Setup {
public static void main(String[] args) throws Exception {
System.setSecurityManager(new RMISecurityManager());
Properties props = new Properties();
props.put("java.security.policy",
"/home/rmi_tutorial/activation/policy");
ActivationGroupDesc.CommandEnvironment ace = null;
ActivationGroupDesc exampleGroup = new ActivationGroupDesc(props, ace);
ActivationGroupID agi =
ActivationGroup.getSystem().registerGroup(exampleGroup);
String location = "file:/home/rmi_tutorial/activation/";
MarshalledObject data = null;
ActivationDesc desc = new ActivationDesc
(agi, "examples.activation.ActivatableImplementation",
location, data);
MyRemoteInterface mri = (MyRemoteInterface)Activatable.register(desc);
System.out.println("Got the stub for the ActivatableImplementation");
Naming.rebind("ActivatableImplementation", mri);
System.out.println("Exported ActivatableImplementation");
System.exit(0);
}
}
- Creates all the information necessary for the activatable
class without creating an instance.
- Passes the information about the class to the
rmid
.
- Passes the identifier (name) to the
rmiregistry
.
- The job of the
ActivationDesc
is to provide all
the information that rmid
will require to create a
new instance of the implementation class.