Write a simple jini application

Introduction

In this tutorial, I plan to show how to write a very simple jini application. This tutorial uses code from Chapter 9 of Jan Newmarch‘s website. For this tutorial you will create 4 files. You can download the source code from this link. If you want a more comprehensive example, you can view the source code for this project (medical_report_system-final).

  1. HelloIntf.java is an interface that has two very simple methods (to send hello message and to receive hello message).
  2. HelloImpl.java implements these methods, and are hosted on server side. Client interact with this class through the HelloIntf interface.
  3. HelloServer is our service provider. It registers itself with the lookup service, and sends the object of the interface to this lookup service, when it is requested (In other words Server knows about HelloImpl).
  4. HelloClient is our client. It uses the HelloIntf  interface methods to send and receive messages from the service provider (our server) using the lookup service. It knows nothing about the implementation. Client however needs a proxy object (which in our case is just a jar created using HelloIntf.java interface). This proxy object is provided during our implementation through a webserver which we host using a tool that comes with jini.

Whenever we transfer message using RMI it has to implement java.io.Serializable interface. Also the HelloIntf.java methods throw RemoteException. Therefore, during implementation in both HelloServer and HelloClient we need to catch RemoteException while implementing or calling these methods.

1. HelloIntf.java

import java.rmi.RemoteException;

public interface HelloIntf {
    public String getHello() throws RemoteException;
    public void setHello(String message) throws RemoteException;
}

2. HelloImpl.java

import java.rmi.RemoteException;

public class HelloImpl implements HelloIntf, java.io.Serializable {
    @Override
    public String getHello() throws RemoteException {
        return "Server is sending hello!!";
    }
    @Override
    public void setHello(String message) throws RemoteException {
        System.out.print("Server: Message from client :");
        System.out.println(message);
    }
}

Note: As you can see both 1 and 2 above are normal java classes and interfaces, except that the implementation implements Serializable and the interface throws RemoteException.

3. HelloServer.java

import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryEvent;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceItem;
import net.jini.core.lookup.ServiceRegistration;
import net.jini.core.lease.Lease;
import net.jini.core.lookup.ServiceID ;
import net.jini.lease.LeaseListener;
import net.jini.lease.LeaseRenewalEvent;
import net.jini.lease.LeaseRenewalManager;

public class HelloServer implements  DiscoveryListener, LeaseListener{
    protected LeaseRenewalManager leaseManager = new LeaseRenewalManager();
    protected ServiceID serviceID = null;
    protected HelloIntf helloIntf = null;

    public static void main(String[] args) {
        HelloServer s = new HelloServer();

        Object keepAlive = new Object();

        synchronized (keepAlive) {
            try {
                keepAlive.wait();
            } catch(InterruptedException e) {}
        }
    }

    public HelloServer() {
        helloIntf = new HelloImpl();

        System.setSecurityManager(new RMISecurityManager());

         LookupDiscovery discover = null;
        try {
            discover = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);
        } catch(Exception e) {
            System.out.println("Discovery failed " + e.toString());
            System.exit(1);
        }

        discover.addDiscoveryListener(this);
    }

    @Override
    public void discovered(DiscoveryEvent discoveryEvent) {
ServiceRegistrar[] registrars = discoveryEvent.getRegistrars();

        for (int n = 0; n < registrars.length; n++) {
            ServiceRegistrar registrar = registrars[n];

            // serviceID could be null, don't worry about that
            ServiceItem item = new ServiceItem(serviceID, helloIntf, null);
            ServiceRegistration reg = null;

            try {
                reg = registrar.register(item, Lease.FOREVER);
            } catch(RemoteException e) {
                System.out.println("Register exception " + e.toString());
                continue;
            }

            System.out.println("Service registered with id " + reg.getServiceID());

            // set lease renewal in place
            leaseManager.renewUntil(reg.getLease(), Lease.FOREVER, this);

        }
    }

    @Override
    public void discarded(DiscoveryEvent discoveryEvent) {

    }

    @Override
    public void notify(LeaseRenewalEvent leaseRenewalEvent) {
        System.out.println("Lease experied " + leaseRenewalEvent.toString());
    }
}

Note: This class is not a clean implemntation (some variables are not needed). The main function creates the object of the server and makes sure it does not exit. In the constructor, HelloIntf gets its object using HelloImpl. Then the instance of this class is added to discovery listener. As you can see the HelloServer implements Discovery listener, this class has to implement methods like discovered and discarded. The discovered method is called when the client/lookup service discovers the service. In the discovered method for all registrar objects the server add a service item that wraps our HelloIntf implementation object and registers it with the registrar. It also makes sure our lease gets renewed all the time.

4. HelloClient

import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;

import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryEvent;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceTemplate;

public class HelloClient implements DiscoveryListener{

    public static void main(String[] args) {
        new HelloClient();

        try {
            Thread.sleep(100000L);
        } catch(InterruptedException e) {}
    }

    public HelloClient() {
        System.setSecurityManager(new RMISecurityManager());

        LookupDiscovery discover = null;

        try {
            discover = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);
        } catch (Exception e) {
            System.out.println(e.toString());
            System.exit(1);
        }

        discover.addDiscoveryListener(this);
    }

    @Override
    public void discovered(DiscoveryEvent discoveryEvent) {
        ServiceRegistrar[] registrars = discoveryEvent.getRegistrars();
        HelloIntf helloIntf = null;
        Class[] classes = new Class[] {HelloIntf.class};
        ServiceTemplate template = new ServiceTemplate(null, classes, null);

        for (int n = 0; n < registrars.length; n++) {
            System.out.println("Lookup service found");
            ServiceRegistrar registrar = registrars[n];

            try {
                helloIntf = (HelloIntf) registrar.lookup(template);
            } catch (RemoteException e) {
                System.out.println("HelloIntf null");
                continue;
            }

            // Use helloIntf object we got from server
            try {
                System.out.println("Client: From server: " + helloIntf.getHello());
                helloIntf.setHello("I am client, saying hi to server");
            } catch (RemoteException e) {
                e.printStackTrace();
                continue;
            }

            // Should I exit now or not, Have to look at this later
            System.exit(0);
        }
    }

    @Override
    public void discarded(DiscoveryEvent discoveryEvent) {

    }
}

Note: The discovered method is again similar to that in server. Except that it has to create an array with the template of type of object it wants to search. In this case it wants an object of type HelloIntf. When it finds that object, it executes the methods of that interface one for saying hello and another for receiving message sent from server.

5. Compilation

Put all of the above 4 files in a src directory inside your project directory (lets call it simple_message_exchange). Set your CLASSPATH variable as

CLASSPATH=$PATH:$JINI_EXAMPLES/lib/jsk-lib.jar:$JINI_EXAMPLES/lib/jsk-platform.jar

What it means is you have to add jsk-lib.jar and jsk-platform.jar to your classpath for the classes to be compiled. If you are using an IDE during the development you can add these jars to the lib directory and add it as library to the IDE. To run through command line (I am using Linux, OpenJDK 8) you can copy tools.jar from JINI installation to the lib directory. Create another directory called all_rmi_lib and inside this directory copy following files from JINI installation.

browser.jar group.jar mercury.jar reggie.jar
checkconfigurationfile.jar jarwrapper.jar norm.jar reggie.policy
checkser.jar jini-core.jar outrigger.jar sharedvm.jar
classdep.jar jini-ext.jar outrigger-logstore.jar start.jar
classserver.jar jsk-all.policy outrigger-snaplogstore.jar start-transient-reggie.config
computedigest.jar jsk-debug-policy.jar phoenix-group.jar sun-util.jar
computehttpmdcodebase.jar jsk-lib.jar phoenix-init.jar tools.jar
destroy.jar jsk-platform.jar phoenix.jar transient-reggie.config
envcheck.jar jsk-resources.jar prebuilt-outrigger-logstore.jar
fiddler.jar mahalo.jar preferredlistgen.jar

Change start-transient-reggie.config file to look like this.

import com.sun.jini.start.ServiceDescriptor;
import com.sun.jini.start.NonActivatableServiceDescriptor;
com.sun.jini.start {
 private static codebase = "http://localhost:8080/reggie-dl.jar";
 private static policy = "/home/username/prjdir/simple_message_exchange/all_rmi_lib/reggie.policy";
 private static classpath = "/home/username/prjdir/simple_message_exchange/all_rmi_lib/reggie.jar";
 private static config = "/home/username/prjdir/simple_message_exchange/all_rmi_lib/transient-reggie.config";
static serviceDescriptors = new ServiceDescriptor[] {
 new NonActivatableServiceDescriptor(
 codebase, policy, classpath,
 "com.sun.jini.reggie.TransientRegistrarImpl",
 new String[] { config })
 };
}

Note: Change username/prjdir to the location where your project is hosted. For our all_rmi_lib to be passed through security, we have to add one more line to reggie.policy file. Add following lines (beside the default) to reggie.policy file.

grant codeBase "file:all_rmi_lib${/}*"{
 permission java.security.AllPermission;
};

Also make sure jsk-all.policy looks like this

grant {
 permission java.security.AllPermission;
};

Now you can compile all the files. Go inside the src directory and run following command

$ javac *.java

Create the jar file

$ jar cf simple_message-dl.jar HelloIntf.class

6. Execution

a. Create a host_this directory (outside src) and copy simple_message-dl.jar, jsk-dl.jar and reggie-dl.jar to this directory. Host this directory using lib/tools.jar

$ java -jar ../lib/tools.jar -port 8080 -dir host_this/ -verbose

b. Start rmid service

$ rmid &

Note: Ignore the ExecPermission/ExecOptionPermission permission checks warning

c. Start the lookup service

$ java -Djava.security.policy=all_rmi_lib/jsk-all.policy -jar all_rmi_lib/start.jar all_rmi_lib/start-transient-reggie.config

d. Start the server (from inside src)

$ java -Djava.rmi.server.codebase=http://localhost:8080/simple_message-dl.jar -Djava.security.policy=../all_rmi_lib/jsk-all.policy HelloServer

e. Start the client (from inside src)

Once the server starts and displays registration id, start the client with following command.

$ java -Djava.security.policy=../all_rmi_lib/jsk-all.policy HelloClient

After some time, it should display

Lookup service found
Client: From server: Server is sending hello!!
Server: Message from client :I am client, saying hi to server

6. Conclusion

I hope this tutorial will help you understand JINI. Cheers !!

Advertisements

Tags: , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s