This page shows you the steps to follow to create and install a custom RMI socket factory. A custom RMI socket factory is useful if (1) you want your RMI client and server to talk across sockets that encrypt or compress your data, or (2) you want to use different types of sockets for different connections.
Installing your own RMI socket factory allows the RMI transport
layer to use a non-TCP or custom transport protocol over IP, rather than
TCP, provided by java.net.Socket
, which RMI uses by default.
Before the release of the JDK1.2, it was possible to create a custom
java.rmi.RMISocketFactory
subclass that produced a type of socket other than java.net.Socket
for use by the RMI transport. It was not possible, however, for an installed
RMI socket factory to produce different types of sockets on an object-by-object
basis. For example in JDK1.1, an RMI socket factory could
not produce SSL sockets for one object and use the Java Remote Method Protocol
(JRMP) directly over TCP for a different object in the same Java Virtual
Machine (JVM). Also before 1.2, it was necessary to spawn an instance of
the rmiregistry
that spoke only your custom socket protocol.
Then in Beta3 JDK1.2, it was possible for RMI clients to use a custom RMI socket factory, but the socket factory could not be downloaded, so the client had to be able to find the socket factory class locally.
Now, in this release of the JDK, it is possible to create a custom RMI
socket factory that produces the type of socket connection you want when
you want on a per-object basis, download a client-side socket factory,
and continue to use the default rmiregistry
.
The rest of this tutorial is laid out as follows:
Many people are interested in secure communication between RMI clients and servers. For the RMI/SSL story see The Scoop on RMI and SSL.
RMIClientSocketFactory
.RMIClientSocketFactory createSocket
method.RMIServerSocketFactory
.RMIServerSocketFactory createServerSocket
method.Step 1:
The type of socket to be produced is an application-specific decision. You get to choose the type of socket that is appropriate for your application. If your server handles a lot of sensitive data, you might want a socket that encrypts the data. If your server deals with video, you are likely to need a socket that does compression.
Decide Upon the Type of Socket to be ProducedFor this example, the RMI socket factory will produce sockets that provide data compression. We will produce
examples.rmisocfac.CompressionSocket
sockets from the page Creating a New Socket Type.Step 2:
Begin the implementation of a client-side RMI socket factory by implementing the
Write a client-side socket factory that implementsRMIClientSocketFactory
RMIClientSocketFactory
interface. The custom socket factory for this example will be calledCompressionClientSocketFactory
.Below is the code for the class
CompressionClientSocketFactory
as well the code for the next step, overriding thecreateSocket
method. An explanation of that step follows the code example.
package examples.rmisocfac;
import java.io.*;
import java.net.*;
import java.rmi.server.*;
public class CompressionClientSocketFactory
implements RMIClientSocketFactory, Serializable {
public Socket createSocket(String host, int port)
throws IOException
{
CompressionSocket socket =
new CompressionSocket(host, port);
return socket;
}
}
Step 3:
Since the function of an RMI socket factory is to supply the RMI runtime with sockets, the
Implement theRMIClientSocketFactory createSocket
method.CompressionClientSocketFactory
needs to provide an implementation of theRMIClientSocketFactory createSocket
method, so that it creates and returns sockets of the correct type --CompressionSocket
. Notice that in the above code, aCompressionSocket
is created and returned.Step 4:
Begin the implementation of a server-side RMI socket factory by implementing the
Write a server-side socket factory that implementsRMIServerSocketFactory
RMIServerSocketFactory
interface. The custom socket factory for this example will be calledCompressionServerSocketFactory
.Below is the code for the class
CompressionServerSocketFactory
as well the code for the next step, implementing thecreateServerSocket
method. An explanation of that step follows the code example.
package examples.rmisocfac;
import java.io.*;
import java.net.*;
import java.rmi.server.*;
public class CompressionServerSocketFactory
implements RMIServerSocketFactory, Serializable {
public ServerSocket createServerSocket(int port)
throws IOException
{
CompressionServerSocket server = new CompressionServerSocket(port);
return server;
}
}
Step 5:
Implementing
Implement theRMIServerSocketFactory createServerSocket
method.createServerSocket
in your RMI socket factory is almost identical to implementingcreateSocket
, exceptcreateServerSocket
needs to create and return a socket of typeCompressionServerSocket
.Now that you have worked through one example of creating an RMI socket factory, you have all the experience necessary to move on to creating a socket factory capable of producing more than one type of socket, which is the next example.
For this example, the custom RMIClientSocketFactory
class will
be named MultiClientSocketFactory
, because it supports multiple
socket types, and likewise, the custom RMIServerSocketFactory
class will be named MultiServerSocketFactory
. Each of these socket
factories has a constructor that specifies which protocol should be supported
for this instance of the object.
Now, following the steps to create a custom socket factory, from the previous example, we will first decide which types of sockets to produce.
Step 1:
This custom RMI socket factory will produce three types of sockets:
Decide Upon the Type of Socket to be ProducedXorSocket
,CompressionSocket
and the default,java.net.Socket
.The source code for the implementation of sockets of type
XorSocket
can be found here.Step one is now complete. Below is the source code for steps 2-5. Following the code is an explanation of each of the remaining steps.
package examples.rmisocfac; import java.io.*; import java.net.*; import java.rmi.server.*;public class MultiClientSocketFactory implements RMIClientSocketFactory, Serializable { /* * Get the default RMISocketFactory */ private static RMISocketFactory defaultFactory = RMISocketFactory.getDefaultSocketFactory(); private String protocol; private byte[] data; public MultiClientSocketFactory(String protocol, byte[] data) { this.protocol = protocol; this.data = data; } /* * Override createSocket to call the default * RMIClientSocketFactory's createSocket method. This * way, you'll get a TCP connection if you don't * specify compression or xor */ public Socket createSocket(String host, int port) throws IOException { if (protocol.equals("compression")) { return new CompressionSocket(host, port); } else if (protocol.equals("xor")) { if (data == null || data.length != 1) throw new IOException("invalid argument for XOR protocol"); return new XorSocket(host, port, data[0]); } return defaultFactory.createSocket(host, port); } }Step 2:
The above class,
ImplementRMIClientSocketFactory
MultiClientSocketFactory
, implementsRMIClientSocketFactory
.
Step 3:
Since the socket factory created in this example can produce two different types of sockets in addition to sockets of the default type, it is necessary to override the
Implement theRMIClientSocketFactory createSocket
method.createSocket
method. If theprotocol
field of theMultiClientSocketFactory
constructor is equal to "xor" then aXorSocket
is created and returned. If theprotocol
field is equal to "compression" then aCompressionSocket
is created and returned.
Step 4:
Write a server-side socket factory that implementsRMIServerSocketFactory
package examples.rmisocfac;
import java.io.*;
import java.net.*;
import java.rmi.server.*;
public class MultiServerSocketFactory
implements RMIServerSocketFactory, Serializable
{
/*
* Get the default RMISocketFactory
*/
private static RMISocketFactory defaultFactory =
RMISocketFactory.getDefaultSocketFactory();
private String protocol;
private byte[] data;
public MultiServerSocketFactory(String protocol, byte[] data) {
this.protocol = protocol;
this.data = data;
}
/*
* Override createServerSocket to call the default
* RMIServerSocketFactory's createServerSocket method, if
* an invalid protocol is specified.
*/
public ServerSocket createServerSocket(int port)
throws IOException
{
if (protocol.equals("compression")) {
return new CompressionServerSocket(port);
} else if (protocol.equals("xor")) {
if (data == null || data.length != 1)
throw new IOException("invalid argument for XOR protocol");
return new XorServerSocket(port, data[0]);
}
return defaultFactory.createServerSocket(port);
}
}
Step 5:
Overriding
Implement theRMIServerSocketFactory createServerSocket
method.createServerSocket
is almost identical to overridingcreateSocket
. As in step three, theRMIServerSocketFactory
createServerSocket
method, the type of socket created and returned is determined by theprotocol
field of theMultiClientSocketFactory
constructor.
UnicastRemoteObject
(or Activatable
) constructor that
takes RMIClientSocketFactory
and RMIServerSocketFactory
parameters.java.security.policy
file that allows your program to
create sockets.java.security.policy
file, please refer to to the following documents
http://java.sun.com/products/jdk/1.2/docs/guide/security/PolicyFiles.html
http://java.sun.com/products/jdk/1.2/docs/guide/security/permissions.html
Next is a version of the "Hello World" example that usesStep 1:
If you create your own RMI socket factory, then you need a way to tell the RMI runtime which type of socket factory to use. Assuming the server extends
Write a Remote Object Constructor that Calls theUnicastRemoteObject
Constructor that takesRMIClientSocketFactory
andRMIServerSocketFactory
parameters.UnicastRemoteObject
, this notification is accomplished by creating a remote object constructor that calls the following version of theUnicastRemoteObject
constructor:
protected UnicastRemoteObject(int port, RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
Below is a
HelloImpl
constructor from the original RMI "Hello World" example appropriately modified for a socket of typeXorSocket
.
public HelloImpl(String protocol, byte [] pattern)
throws RemoteException
{
super(0, new MultiClientSocketFactory(protocol, pattern),
new MultiServerSocketFactory(protocol, pattern));
}
Notice that the
UnicastRemoteObject
constructor,
protected UnicastRemoteObject(int port, RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
is called from the
HelloImpl
constructor. Once your custom socket factory is set, sockets of the desired type will be used for your RMI client-server application.Step 2:
The policy file for this example, is not secure and should not be used in a production environment.
Write ajava.security.policy
file that allows your program to create sockets.The policy file for this example looks like this:
grant {
// Allow everything for now
permission java.security.AllPermission;
};
MultiClientSocketFactory
and MultiServerSocketFactory
to communicate using sockets of type
XorSocket
.
This example has changed from the original "Hello
World" example in the RMI tutorial. Most notably, the client in this
example is not an applet. In addition, the client class in this
example is called HelloClient
. The last difference is that all
of the classes are in the examples.rmisocfac
package.
It is important to recognize that this example assumes that the client, server, and registry are all run on the same machine.
Below is the interface Hello
, from the file Hello.java
.
Notice that, except for the package name, this interface has not changed
from the original Hello.java
.
package examples.rmisocfac; public interface Hello extends java.rmi.Remote { String sayHello() throws java.rmi.RemoteException; }
HelloClient.java
.
Notice that the RMISecurityManager is installed
at the beginning
of main
. Otherwise the class HelloClient
is no different
than before.
package examples.rmisocfac; import java.rmi.*; public class HelloClient { private static String message = ""; public static void main(String args[]) { //Create and install a security manager if (System.getSecurityManager() == null) System.setSecurityManager(new RMISecurityManager()); try { Hello obj = (Hello) Naming.lookup("/HelloServer"); message = obj.sayHello(); System.out.println(message); } catch (Exception e) { System.out.println("HelloClient exception: " + e.getMessage()); e.printStackTrace(); } } }
HelloImpl
from the file
HelloImpl.java
. Besides the change in the package name, two modifications
have been made so that sockets of type "xor
" will be used for
the RMI calls between the client and the server. Note that the constructor
has been changed to call the version of the UnicastRemoteObject
constructor that takes a client and server socket factory as parameters.
package examples.rmisocfac;
import java.io.*;
import java.rmi.*;
import java.rmi.server.*;
public class HelloImpl
extends UnicastRemoteObject implements Hello {
/*
* Constructor calls constructor of superclass with
* client and server socket factory parameters.
*/
public HelloImpl(String protocol, byte [] pattern) throws RemoteException
{
super(0, new MultiClientSocketFactory(protocol, pattern),
new MultiServerSocketFactory(protocol, pattern));
}
/*
* Remote method returns String "Hello World!"
* when invoked.
*/
public String sayHello() throws RemoteException {
return "Hello World!";
}
public static void main(String args[]) {
//Create and install a security manager
if (System.getSecurityManager() == null)
System.setSecurityManager(new RMISecurityManager());
byte [] aPattern = { (byte)1011 };
try {
HelloImpl obj = new HelloImpl("xor", aPattern);
Naming.rebind("/HelloServer", obj);
System.out.println("HelloServer bound in registry");
} catch (Exception e) {
System.out.println("HelloImpl err: " + e.getMessage());
e.printStackTrace();
}
}
}
Directions on how to compile and run the above "Hello World" example can be found here.