| # Using a Custom RMI Socket Factory | Documentation Comments

This tutorial shows you the steps to follow to implement and use a custom RMI socket factory. A custom RMI socket factory is useful if (1) your RMI client and server need to use sockets that encrypt or compress data, and/or (2) your application requires different socket types for different remote objects.

Prior to the JavaTM 2 SDK, v1.2 release, it was possible to create and install a custom `java.rmi.server.RMISocketFactory` subclass used globally for all connections created by the RMI transport. It was not possible, however, to associate a different RMI socket factory on a per-object basis. For example in JDKTM v1.1.x, 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 virtual machine. Also before 1.2, it was necessary to spawn an instance of the `rmiregistry` that used only your custom socket protocol.

As of the Java 2 SDK, v1.2 release, an RMI application can use a custom RMI socket factory on a per-object basis, download a client-side socket factory, and continue to use the default `rmiregistry`.

This tutorial has three parts:

> - Implementing a Custom RMI Socket Factory.
> - Using a Custom RMI Socket Factory in an Application.
> - Compiling and Running the Application.

> The source code for this tutorial is available in the following formats:

> - sockets.zip
> - sockets.tar
> - sockets.tar.Z

Many people are interested in secure communication between RMI clients and servers. For more information see Using RMI with SSL.

## Implementing a Custom RMI Socket Factory

There are three steps to implementing a custom RMI socket factory:

1. Implement a custom `ServerSocket` and `Socket`.
2. Implement a custom `RMIClientSocketFactory`.
3. Implement a custom `RMIServerSocketFactory`.

> ### Step 1:
> Implement a custom `ServerSocket` and `Socket`

> The type of socket to use is an application-specific decision. If your server sends or receives sensitive data, you might want a socket that encrypts the data.

> For this example, the custom RMI socket factory will create sockets that perform simple XOR encryption. This type of encryption will protect data from a casual snooper sniffing packets on the wire, but is easily decoded by a knowledgeable cryptanalyst.

> The custom XOR socket implementation includes the following sources. XOR sockets use special input and output stream implementations to handle xor-ing the data written to or read from the socket.

> - `XorInputStream.java`
> - `XorOutputStream.java`
> - `XorServerSocket.java`
> - `XorSocket.java`

> ### Step 2:
> Implement a custom `RMIClientSocketFactory`

> The client-side RMI socket factory, `XorClientSocketFactory`, implements the `java.rmi.server.RMIClientSocketFactory` interface. The client socket factory needs to implement the `createSocket` method to return the approriate client socket instance, an `XorSocket`.

> The client socket factory must implement the `java.io.Serializable` interface so that instances can be serialized to clients. It is also important to implement the `equals` and `hashCode` methods so that the RMI implementation will reuse the socket factory's connections to remote objects using equivalent factories.

>     package examples.rmisocfac;

>     import java.io.*;
>     import java.net.*;
>     import java.rmi.server.*;

>     public class XorClientSocketFactory
>         implements RMIClientSocketFactory, Serializable {

>         private byte pattern;

>         public XorClientSocketFactory(byte pattern) {
>     	this.pattern = pattern;
>         }

>         public Socket createSocket(String host, int port)
>     	throws IOException
>         {
>     	return new XorSocket(host, port, pattern);
>         }

>         public int hashCode() {
>     	return (int) pattern;
>         }

>         public boolean equals(Object obj) {
>     	return (getClass() == obj.getClass() &&
>     		pattern == ((XorClientSocketFactory) obj).pattern);
>         }
>     }

> ### Step 3:
> Implement a custom `RMIServerSocketFactory`

> The server-side RMI socket factory, `XorServerSocketFactory`, implements the `java.rmi.server.RMIServerSocketFactory` interface. The server socket factory needs to implement the `createServerSocket` method to return the appropriate server socket instance, an `XorServerSocket`.

> The server socket factory does not need to implement the `Serializable` interface because server socket factory instances are not serialized to clients. The factory should implement the `equals` and `hashcode` methods so that the RMI implementation will reuse the socket factory's accept connection for equivalent factories.

>     package examples.rmisocfac;

>     import java.io.*;
>     import java.net.*;
>     import java.rmi.server.*;

>     public class XorServerSocketFactory
>         implements RMIServerSocketFactory {

>         private byte pattern;

>         public XorServerSocketFactory(byte pattern) {
>     	this.pattern = pattern;
>         }

>         public ServerSocket createServerSocket(int port)
>     	throws IOException
>         {
>     	return new XorServerSocket(port, pattern);
>         }

>         public int hashCode() {
>     	return (int) pattern;
>         }

>         public boolean equals(Object obj) {
>     	return (getClass() == obj.getClass() &&
>     		pattern == ((XorServerSocketFactory) obj).pattern);
>         }

>     }

## Using a Custom Socket Factory in an Application

There are only two more steps to complete when using a custom RMI socket factory for a remote object:

1. Write a server application that creates a remote object and exports it to use your custom `RMIClientSocketFactory` and `RMIServerSocketFactory`. Store a reference to the remote object's stub in an RMI registry so that clients can look it up.

2. Write a client application that looks up the stub for the remote object and invokes a remote method. The custom socket factories do not need to be referenced in the client application. The client-side RMI socket factory will be downloaded to the client when the client looks up the stub for the remote object.

> ### Step 1:
> Write a server application

> If communication with a remote object requires the use of custom sockets, you need to inform the RMI runtime which custom socket factories to use when you export the remote object. When your application exports the object specifying custom socket factories, the RMI runtime will use the corresponding custom `RMIServerSocketFactory` to create a server socket to accept incoming calls to the remote object. Also, the RMI runtime will create a stub that refers to the corresponding custom `RMIClientSocketFactory`. This client socket factory will be used to create connections upon initating a remote invocation to the remote object using the stub.

> This example is similar to the example in the tutorial Getting Started Using RMI, but uses custom socket factories instead of the default sockets used by the RMI implementation.

> The application uses the following `Hello` remote interface:

>     package examples.rmisocfac;

>     public interface Hello extends java.rmi.Remote {
>         String sayHello() throws java.rmi.RemoteException;
>     }

> The server application creates a remote object implementing the `Hello` remote interface and exports the object to use custom socket factories using the `java.rmi.server.UnicastRemoteObject.exportObject` method that takes the custom socket factories as arguments. Next, it creates a local registry and, in that registry, it binds a reference to the remote object's stub with the name "Hello".

>     package examples.rmisocfac;

>     import java.io.*;
>     import java.rmi.*;
>     import java.rmi.server.*;
>     import java.rmi.registry.*;

>     public class HelloImpl implements Hello {

>         public HelloImpl() {}

>         public String sayHello() {
>             return  "Hello World!";
>         }

>         public static void main(String args[]) {

>     	if (System.getSecurityManager() == null) {
>     	    System.setSecurityManager(new SecurityManager());
>     	}

>             byte pattern = (byte) 0xAC;
>     	try {
>     	    /*
>     	     * Create remote object and export it to use
>     	     * custom socket factories.
>     	     */
>     	    HelloImpl obj = new HelloImpl();
>     	    RMIClientSocketFactory csf = new XorClientSocketFactory(pattern);
>     	    RMIServerSocketFactory ssf = new XorServerSocketFactory(pattern);
>     	    Hello stub =
>     		(Hello) UnicastRemoteObject.exportObject(obj, 0, csf, ssf);

>     	    /*
>     	     * Create a registry and bind stub in registry.
>     	     *
>     	    LocateRegistry.createRegistry(2002);
>     	    Registry registry = LocateRegistry.getRegistry(2002);
>     	    registry.rebind("Hello", stub);
>     	    System.out.println("HelloImpl bound in registry");

>     	} catch (Exception e) {
>     	    System.out.println("HelloImpl exception: " + e.getMessage());
>     	    e.printStackTrace();
>     	}
>         }
>     }

> ### Step 2:
> Write a client application

> The client application obtains a reference to the registry used by the server application. It then looks up the remote object's stub and invokes its remote method `sayHello`:

>     package examples.rmisocfac;

>     import java.rmi.*;
>     import java.rmi.registry.*;

>     public class HelloClient {

>         public static void main(String args[]) {

>     	if (System.getSecurityManager() == null) {
>     	    System.setSecurityManager(new SecurityManager());
>     	}

>             try {
>     	    Registry registry = LocateRegistry.getRegistry(2002);
>                 Hello obj = (Hello) registry.lookup("Hello");
>                 String message = obj.sayHello();
>                 System.out.println(message);

>             } catch (Exception e) {
>     	    System.out.println("HelloClient exception: " +
>                                    e.getMessage());
>                 e.printStackTrace();
>             }
>         }

>     }

## Compiling and Running the Application

There are four steps to compile and run the application:

1. Compile the remote interface, client, and server classes
2. Run `rmic` on the implementation class
3. Start the server
4. Run the client

> Step 1:
> Compile the remote interface, client, and server classes

>     javac -d . Hello.java
>     javac -d . HelloClient.java
>     javac -d . HelloImpl.java
>     javac -d . XorClientSocketFactory.java
>     javac -d . XorInputStream.java
>     javac -d . XorOutputStream.java
>     javac -d . XorServerSocket.java
>     javac -d . XorServerSocketFactory.java
>     javac -d . XorSocket.java

> Step 2:
> Run `rmic` on the implementation class

>     rmic -d . examples.rmisocfac.HelloImpl

> Step 3:
> Start the server

>     java -Djava.security.policy=policy examples.rmisocfac.HelloImpl

> The server output should look like this:

>           HelloImpl bound in registry

> Step 4:
> Run the client

> In another window start the client application making sure that the application classes are in the class path:

>     java -Djava.security.policy=policy examples.rmisocfac.HelloClient

> The client output should look like this:

>           Hello World!

> Note: Both the server and client applications use a security policy file that grants permissions only to files in the local class path (the current directory). The server application needs permission to accept connections, and both the server and client applications need permission to make connections. The permission `java.net.SocketPermission` is granted to the specified codebase URL, a "file:" URL relative to the current directory. This permission grants the ability to both accept connections from and make connections to any host on unprivileged ports (that is ports >= 1024).

>     grant codeBase "file:." {

>         permission java.net.SocketPermission "*:1024-", "connect,accept";
>     };

Copyright © 2003 Sun Microsystems, Inc. All Rights Reserved. Please send comments to: `rmi-comments@java.sun.com` |