Previous lesson 4/6 |home|

Remote Method Invocation (Level II)

Customization with RMI

Betty was very excited with the features she just tried. She was wondering how to do that if she has a wonderful remote object and doesn't want some people to access it even if they know the name of the object. How can she do that with some security in mind.

First, you can install a security manager on your server and client applications, then use policy file to restrict access. For example, you can use codeBase property in the policy file to restrict scope and port number.

  
grant codeBase "file:." {
    permission java.net.SocketPermission "*:1024-", "connect,accept";
};

The above policy file restricts only local file protocol and localhost with port number 1024 access permission.

The following policy file will grant all permissions of any kind to code residing in the RMI directory on the C: drive:

  
grant codeBase "file:C:/RMI/-" {
    permission java.security.AllPermission;
};

Second, you can use customized sockets by subclassing Socket or ServerSocket or implementing RMIClientSocketFactory and RMIServerSocketFactory interfaces respectively. According to the RMI API, an RMIClientSocketFactory instance or an RMIServerSocketFactory instance is used by the RMI runtime in order to obtain client sockets or server sockets for RMI calls. A remote object can be associated with an RMIClientSocketFactory or an RMIServerSocketFacotry when it is created/exported via the constructors or exportObject methods of java.rmi.server.UnicastRemoteObject and java.rmi.activation.Activatable.

To illustrate such feature, we will use these interfaces:

And the remote object can be exported with this method.

UnicastRemoteObject.exportObject(Remote obj,int port,RMIClientSocketFactory csf, RMIServerSocketFactory ssf);

Betty decided to write a customized socket program to test this feature.

She wrote a social security number look-up program. If the client program gets a name from a user, and sends it to an RMI server, the social security number will be returned from the RMI server. Both client and server applications use customized sockets to make a communication. If the named user is not designated, the server will fail to start.

The program is created with the following files:

  1. SSN.java
  2. SSNImpl.java
  3. SSNClientSocket.java
  4. SSNServerSocket.java
  5. SSNServer.java
  6. SSNClient.java
  7. myrmi.policy all permission policy file.

The following lists all the files.

  
//SSN.java
package customization;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface SSN extends Remote {
    public String getSSN(String name)
                    throws RemoteException;
}

//SSNImpl.java
package customization;

import java.rmi.*;
import java.util.*;

public class SSNImpl implements SSN {
    private Map dir;
    public SSNImpl() {
        dir = new HashMap();
        dir.put("Betty","123-34-5678");
        dir.put("Mark", "234-56-7890");
        dir.put("Cathy", "345-67-8901");
        dir.put("Aaron", "456-78-9012");
    }
    	 
    public String getSSN(String name) throws RemoteException {
        if (dir.containsKey(name))
             return (String)dir.get(name);
        else
             return null; 
    }
}

//SSNServerSocket.java
package customization;

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

public class SSNServerSocket implements RMIServerSocketFactory {
    private static String user = null;

    public ServerSocket createServerSocket(int port)
	throws IOException
    {
	return new ServerSocket(port);
    }
    public static void setUser(String usr) {
        user = usr;
    }
    public static SSNServerSocket getServerSocket() {
        return new SSNServerSocket();
    }
    public boolean equals(Object o) {
        return (getClass() == o.getClass() &&
		user.equalsIgnoreCase("Betty"));

    }
    public int hashCode() {
         return user.hashCode();
    }
    
}

//SSNClientSocket.java
package customization;

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

public class SSNClientSocket implements RMIClientSocketFactory, Serializable {
    private static String user = "Betty";

    public Socket createSocket(String host, int port)
	throws IOException
    {
	return new Socket(host, port);      
    }
    public static void setUser(String usr) {
        user = usr;
    }
    public static SSNClientSocket getClientSocket() {
        return new SSNClientSocket();
    }
    public boolean equals(Object o) {
        return (getClass() == o.getClass() &&
		user.equalsIgnoreCase("Betty"));

    }
    public int hashCode() {
         return user.hashCode();
    }
}

//SSNServer.java
package customization;

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

public class SSNServer {

    public static void main(String args[]) {

	if (System.getSecurityManager() == null) {
	    System.setSecurityManager(new SecurityManager());
	}
	
    
	try {
	   
	    SSNImpl ssn = new SSNImpl();
            SSNClientSocket.setUser("Betty");
	    RMIClientSocketFactory csf = SSNClientSocket.getClientSocket();
	    SSNServerSocket.setUser("Betty");
            RMIServerSocketFactory rsf = SSNServerSocket.getServerSocket();
            if (rsf != null) {
	        SSN stub =
		    (SSN) UnicastRemoteObject.exportObject(ssn, 0, csf, rsf);
	        LocateRegistry.createRegistry(2004);
	        Registry registry = LocateRegistry.getRegistry(2004);	
	        registry.rebind("SSN", stub);
	        System.out.println("SSN Server is ready to listen ...");
            }else 
                System.out.println("You are not an authorized user");
	    
	} catch (Exception e) {
	    System.out.println("SSNImpl exception: " + e.getMessage());
	    e.printStackTrace();
	}
    }
}

//SSNClient.java
package customization;

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

public class SSNClient {

    public static void main(String args[]) {

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

        try {
	    Registry registry = LocateRegistry.getRegistry(2004);
            SSN stub = (SSN) registry.lookup("SSN");
            String betty = stub.getSSN("Betty");
            System.out.println("Betty's SSN: " + betty);
            String mark = stub.getSSN("Mark");
            System.out.println("Mark's SSN: " + mark);

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

}

Note that when your customized sockets implement socket factory interfaces, you need to implement createXXX methods and override equals() and hashCode() methods.

To run the program, you will get the result as follows.

 C:\   Command Prompt
 

C:\myrmi>javac -d . SSN.java SSNClient.java SSNClientSocket.java SSNServer.java
SSNServerSocket.java SSNImpl.java

C:\myrmi>set classpath=

C:\myrmi>start rmiregistry

C:\myrmi>start java -Djava.security.policy=myrmi.policy customization.SSNServer

C:\myrmi>java -Djava.security.policy=myrmi.policy customization.SSNClient
Betty's SSN: 123-34-5678
Mark's SSN: 234-56-7890

C:\myrmi>

Till now, you have scratched a little bit deep in RMI technology. To learn more, you need to read through the RMI specification.

Javacamp.org is trying every effort to make the tutorial more readable. Your comments are very important to us. Let's know what your opinion and how we can do better about this tutorial. Please fill this comments form. Thanks for your reading.

Check your skill

Previous lesson 5/6 |home|