# Creating a Custom RMI Socket Factory

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 Java TM 2 SDK, v1.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 Java SDK, v1.2 Beta3, 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.

Since the release of the Java 2 SDK, v1.2, it's been 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:

> - An example of how to create an RMI socket factory that produces a single type of socket.
> - An example of how to create a RMI socket factory that produces more than one type of socket.
> - An example of how to use a custom RMI socket factory in an application.

Many people are interested in secure communication between RMI clients and servers. For the RMI/SSL story see The Scoop on RMI and SSL.

## Creating an RMI Socket Factory That Produces a Single Type of Socket

There are four steps to creating a custom RMI socket factory, that produces a single type of socket.

1. Decide upon the type of socket to be produced.
2. Write a client-side socket factory that implements `RMIClientSocketFactory`.
3. Implement the `RMIClientSocketFactory createSocket` method.
4. Write a server-side socket factory that implements `RMIServerSocketFactory`.
5. Implement the `RMIServerSocketFactory createServerSocket` method.

> ### Step 1:
> Decide Upon the Type of Socket to be Produced

> 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.

> For 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:
> Write a client-side socket factory that implements `RMIClientSocketFactory`

> Begin the implementation of a client-side RMI socket factory by implementing the `RMIClientSocketFactory` interface. The custom socket factory for this example will be called `CompressionClientSocketFactory`.

> Below is the code for the class `CompressionClientSocketFactory` as well the code for the next step, overriding the `createSocket` 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:
> Implement the `RMIClientSocketFactory createSocket` method.

> Since the function of an RMI socket factory is to supply the RMI runtime with sockets, the `CompressionClientSocketFactory` needs to provide an implementation of the `RMIClientSocketFactory createSocket` method, so that it creates and returns sockets of the correct type -- `CompressionSocket`. Notice that in the above code, a `CompressionSocket` is created and returned.

> ### Step 4:
> Write a server-side socket factory that implements `RMIServerSocketFactory`

> Begin the implementation of a server-side RMI socket factory by implementing the `RMIServerSocketFactory` interface. The custom socket factory for this example will be called `CompressionServerSocketFactory`.

> Below is the code for the class `CompressionServerSocketFactory` as well the code for the next step, implementing the `createServerSocket` 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:
> Implement the `RMIServerSocketFactory createServerSocket` method.

> Implementing `createServerSocket` in your RMI socket factory is almost identical to implementing `createSocket`, except `createServerSocket` needs to create and return a socket of type `CompressionServerSocket`.

> 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.

## Creating an RMI Socket Factory that Produces More Than One Type of Socket

To create an RMI socket factory capable of producing more than one type of socket, you start by following the steps for creating a factory which produces a single socket type. Only a little more information is necessary to create a "wrapper" for the different socket types.

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:
> Decide Upon the Type of Socket to be Produced

> This custom RMI socket factory will produce three types of sockets: `XorSocket`, `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:
> Implement `RMIClientSocketFactory`

> The above class, `MultiClientSocketFactory`, implements `RMIClientSocketFactory`.

> ### Step 3:
> Implement the `RMIClientSocketFactory createSocket` method.

> 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 `createSocket` method. If the `protocol` field of the `MultiClientSocketFactory` constructor is equal to "xor" then a `XorSocket` is created and returned. If the `protocol` field is equal to "compression" then a `CompressionSocket` is created and returned.

> ### Step 4:
> Write a server-side socket factory that implements `RMIServerSocketFactory`

> `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:
> Implement the `RMIServerSocketFactory createServerSocket` method.

> Overriding `createServerSocket` is almost identical to overriding `createSocket`. As in step three, the `RMIServerSocketFactory` `createServerSocket` method, the type of socket created and returned is determined by the `protocol` field of the `MultiClientSocketFactory` constructor.

## Using Your 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. In the remote object implementation, write a constructor that calls the `UnicastRemoteObject` (or `Activatable`) constructor that takes `RMIClientSocketFactory` and `RMIServerSocketFactory` parameters.
2. Write a `java.security.policy` file that allows your program to create sockets.

Note: In this example, for simplicity, we will use a policy file that gives global permission to anyone from anywhere. Do not use this policy file in a production environment. For more information on how to properly open up permissions using a `java.security.policy` file, please refer to to the following documents

> `Default Policy Implemenation and Policy File Syntax`
> `Permission in the Java 2 SDK`

> ### Step 1:
> Write a Remote Object Constructor that Calls the `UnicastRemoteObject` Constructor that takes `RMIClientSocketFactory` and `RMIServerSocketFactory` parameters.

> 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 `UnicastRemoteObject`, this notification is accomplished by creating a remote object constructor that calls the following version of the `UnicastRemoteObject` 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 type `XorSocket`.

> `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:
> Write a `java.security.policy` file that allows your program to create sockets.

> The policy file for this example, is not secure and should not be used in a production environment.

> The policy file for this example looks like this:

> `grant {`
> `// Allow everything for now`
> `permission java.security.AllPermission;`
> `};`

Next is a version of the "Hello World" example that uses `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;
            }

Take a look at the modified version of the client class, `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();
                   }

                }

            }

Last in this example is the source for `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();`
`}`
`}`
`}`

To download this example and the supporting files, click here.

Directions on how to compile and run the above "Hello World" example can be found here.

*As used on this web site, the terms "Java virtual machine" or "JVM" mean a virtual machine for the Java platform.

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