// This is the first iteration of a Hello, World
// service--it publishes a proxy that returns
// a string when asked by clients.

package corejini.chapter5;

import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryEvent;
import net.jini.discovery.LookupDiscovery;
import net.jini.core.lookup.ServiceItem;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceRegistration;
import java.util.Hashtable;
import java.io.IOException;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.rmi.RMISecurityManager;

// This is the proxy object that will be downloaded
// by clients.  It's serializable and implements
// our well-known HelloWorldServiceInterface.
class HelloWorldServiceProxy implements Serializable, 
    HelloWorldServiceInterface {
    public HelloWorldServiceProxy() {
    }
    public String getMessage() {
        return "Hello, world!";
    }
}

// HelloWorldService is the "wrapper" class that
// handles publishing the service item.
public class HelloWorldService implements Runnable {
    // 10 minute leases
    protected final int LEASE_TIME = 10 * 60 * 1000; 
    protected Hashtable registrations = new Hashtable();
    protected ServiceItem item;
    protected LookupDiscovery disco;
    
    // Inner class to listen for discovery events
    class Listener implements DiscoveryListener {
        // Called when we find a new lookup service.
        public void discovered(DiscoveryEvent ev) {
            System.out.println("discovered a lookup service!");
            ServiceRegistrar[] newregs = ev.getRegistrars();
            for (int i=0 ; i<newregs.length ; i++) {
                if (!registrations.containsKey(newregs[i])) {
                    registerWithLookup(newregs[i]);
                }
            }
        }
        
        // Called ONLY when we explicitly discard a
        // lookup service, not "automatically" when a
        // lookup service goes down.  Once discovered,
        // there is NO ongoing communication with a
        // lookup service.
        public void discarded(DiscoveryEvent ev) {
            ServiceRegistrar[] deadregs = ev.getRegistrars();
            for (int i=0 ; i<deadregs.length ; i++) {
                registrations.remove(deadregs[i]);
            }
        }
    }

    public HelloWorldService() throws IOException {
        item = new ServiceItem(null, createProxy(), null);
        
        // Set a security manager
        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new RMISecurityManager());
        }
        
        // Search for the "public" group, which by
        // convention is named by the empty string
        disco = new LookupDiscovery(new String[] { "" });
        
        // Install a listener.
        disco.addDiscoveryListener(new Listener());
    }
    
    protected HelloWorldServiceInterface createProxy() {
        return new HelloWorldServiceProxy();
    }
    
    // This work involves remote calls, and may take a
    // while to complete.  Thus, since it's called from
    // discovered(), it will prevent us from responding
    // in a timely fashion to new discovery events.  An
    // improvement would be to spin off a separate short-
    // lived thread to do the work.
    protected synchronized void registerWithLookup(ServiceRegistrar registrar) {
        ServiceRegistration registration = null;

        try {
            registration = registrar.register(item, LEASE_TIME);
        } catch (RemoteException ex) {
            System.out.println("Couldn't register: " + ex.getMessage());
            return;
        }
        
        // If this is our first registration, use the
        // service ID returned to us.  Ideally, we should
        // save this ID so that it can be used after
        // restarts of the service
        if (item.serviceID == null) {
            item.serviceID = registration.getServiceID();
            System.out.println("Set serviceID to " + item.serviceID);
        }
        
        registrations.put(registrar, registration);
    }
    
    // This thread does nothing but sleep, but it
    // makes sure the VM doesn't exit.
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000000);
            } catch (InterruptedException ex) {
            }
        }
    }
    
    // Create a new HelloWorldService and start
    // its thread.
    public static void main(String args[]) {
        try {
            HelloWorldService hws = new HelloWorldService();
            new Thread(hws).start();
        } catch (IOException ex) {
            System.out.println("Couldn't create service: " +
                               ex.getMessage());
        }
    }
}


