Demystifying JINI: Non-Secure Server: Part 1

Posted by {"name"=>"Palash Ray", "email"=>"paawak@gmail.com", "url"=>"https://www.linkedin.com/in/palash-ray/"} on June 07, 2014 · 7 mins read

Introduction

What is JINI?

JINI is a framework that enables us to write Java-based distributed systems. In simple terms, it allows you to expose Java classes as services, which can be then called remotely by other Java based clients over the network. For more details, refer to http://river.apache.org/concepts.html.

How is it different from RMI?

  • In RMI, the client should explicitly know about the server and then make a look-up. But JINI has the concept of discovery, where it attempts to discover the service through some known search parameters.
  • RMI does not support distributed transactions or events, JINI has support for that and lot more.
  • RMI does not support dynamic code download from the server, JINI does. This means that the client is more loosely coupled and not necessarily tied to a particular protocol.

Where do I find it?

Its hosted at http://river.apache.org/.

Reason for this blog

I came across this in my current project and wanted to explore more about the hows and whys. The problem was acute shortage of documentation on the internet, probably because very few people use this. Also, the documentation and code sample available at the apache-river site were bloated and far from simple. They just created confusion. So I decided to write my own simple implementation using minimal amount of code and configuration.

Writing a bare bones, non-secure client/server using JINI

High Level Overview

Our implementation will consist of the following 3 components:

  1. The RmiServer: This will expose services which can be invoked as Java method calls across the network.
  2. The RmiClient: This will consume the remote services provided by the RmiServer.
  3. The Registry: This will act as an intermediary between the client and the server. The server will register the services its exposing, with the registry. And the client will do a look-up for the desired service from the registry. We will be using the reggie component of the apache-river project for our Registry.

Please note that to keep this simple, we are not using the dynamic code download feature that JINI provides. A downside of that is, the client also has to be bundled with the JINI-specific libraries at runtime. If you ask me thats not too bad actually.

 Setting up the Maven dependencies

Both the Client and the Server should have the following JINI-libraries dependency:


 net.jini
 jsk-platform
 2.2.2
 
 
 net.jini
 jsk-lib
 2.2.2
 
 
 org.apache.river
 reggie
 2.2.2
 

 Creating the Remote interfaces

We will have the following two interfaces which we will expose as services:

public interface BankAccountService extends Remote {
    long createBankAccount(String userName) throws RemoteException;
}
public interface UserService extends Remote {
    List getCurrentUsers() throws RemoteException;
}

Note that these extend the java.rmi.Remote interface and also, each method throws the java.rmi.RemoteException exception. These two interfaces will be shared between the Server and the Client.

The Server

public class SimpleNonSecureRmiServer {
    private final DiscoveryManagement discoveryManager;
    private final LeaseRenewalManager leaseRenewalManager;
    public SimpleNonSecureRmiServer() throws Exception {
        discoveryManager = new LookupLocatorDiscovery(
                new LookupLocator[] { new LookupLocator("jini://localhost:4160") });
        leaseRenewalManager = new LeaseRenewalManager();
    }
    private void exportAndJoinServices() throws Exception {
        exportAndJoinService(new UserServiceImpl(), discoveryManager, leaseRenewalManager,
                UserService.class.getSimpleName());
        exportAndJoinService(new BankAccountServiceImpl(), discoveryManager, leaseRenewalManager,
                BankAccountService.class.getSimpleName());
    }
    private void exportAndJoinService(Remote service, DiscoveryManagement discoveryManager,
            LeaseRenewalManager leaseRenewalManager, String serviceName) throws Exception {
        // the exporter can export only 1 remote service
        // this is VERY important: prevents the class from getting GC-ed
        Exporter exporter = getExporter();
        Remote exportedService = exporter.export(service);
        JoinManager joinManager = new JoinManager(exportedService, new Entry[] { new Name(serviceName) },
                (ServiceID) null, discoveryManager, leaseRenewalManager);
    }
    private Exporter getExporter() {
        return new JrmpExporter();
        // return new BasicJeriExporter(TcpServerEndpoint.getInstance(0), new
        // BasicILFactory());
    }
    public static void main(String[] args) throws Exception {
        SimpleNonSecureRmiServer server = new SimpleNonSecureRmiServer();
        server.exportAndJoinServices();
        System.out.println("Hello server is ready");
    }
}

The Client

public class SimpleNonSecureRmiClient {
	/**
	 * Needs to be run with the following JVM args:
*
    *
  • -Djava.security.policy=/full-path/RmiClient/src/main/resources/policy.all
  • *
  • -Djava.rmi.server.RMIClassLoaderSpi=net.jini.loader.pref.PreferredClassProvider
  • *
*/     public static void main(String[] args) throws Exception {         System.setSecurityManager(new RMISecurityManager());         LookupLocator lookup = new LookupLocator("jini://localhost:4160");         ServiceRegistrar registrar = lookup.getRegistrar();         UserService userService = (UserService) registrar.lookup(new ServiceTemplate(null,                 new Class[] { UserService.class }, new Entry[] { new Name(UserService.class.getSimpleName()) }));         System.out.println("Getting current users from remote: " + userService.getCurrentUsers());         BankAccountService bankAccountService = (BankAccountService) registrar.lookup(new ServiceTemplate(null,                 new Class[] { BankAccountService.class }, new Entry[] { new Name(BankAccountService.class                         .getSimpleName()) }));         System.out.println("Creating bank account from remote, id: " + bankAccountService.createBankAccount("aaa"));     } }

The Reggie

The reggie should be configured properly, and this must go hand-in-hand with the server. For example, they should use the same protocol, in this case, JRMP. If it uses JERI, then both the server and the reggie should use JERI.
This is the start config file for reggie [start-reggie.conf].

import com.sun.jini.config.ConfigUtil;
import com.sun.jini.start.NonActivatableServiceDescriptor;
import com.sun.jini.start.ServiceDescriptor;
com.sun.jini.start {
    private static codebase = "";
    private static policy = "";
    private static classpath = "lib${/}reggie.jar";
    private static config = "config${/}jrmp-reggie.config";
    static serviceDescriptors = new ServiceDescriptor[] {
    new NonActivatableServiceDescriptor(
        codebase, policy, classpath,
        "com.sun.jini.reggie.TransientRegistrarImpl",
        new String[] { config })
    };
}

After that, we need the following file for specifying the protocol as well [jrmp-reggie.conf]:

import net.jini.jrmp.JrmpExporter;
com.sun.jini.reggie {
    serverExporter = new JrmpExporter();
}

Refer to the JINI documentation for the configuration details.

Putting it all together

To see this in action we need to first start the reggie service. Unpack the start-reggie.zip. From the command prompt, cd into it and:

java -Djava.ext.dirs=lib -jar start.jar config/start-reggie.config

Then, start the server using the main method.
For running the client, you need to create a policy file [called policy.all] containing the following:

grant {
permission java.security.AllPermission;
};

When running the client, give the following JVM arguments:

-Djava.security.policy=/resources/path-to/policy.all

-Djava.rmi.server.RMIClassLoaderSpi=net.jini.loader.pref.PreferredClassProvider
You should see the out from the remote server.

 Resources

The code can be found in GitHub, under the code/jini/unsecure/plain:
https://github.com/paawak/blog.git