package org.dancres.jini;

import java.io.IOException;

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

import net.jini.discovery.*;

import net.jini.lookup.*;

import net.jini.core.lookup.ServiceItem;
import net.jini.core.lookup.ServiceTemplate;

import net.jini.space.JavaSpace;

/**
   In JINI 1.x, using ServiceDiscoveryManager could be a little problematic
   as client's using it had to have a codebase server from which sdm-dl.jar
   (containing the ServiceDiscoveryManager's stub) could be obtained by LUS'en
   it registered with.  JINI 2.0 introduced JERI which allows us to generate
   remote stubs on the fly and it's used by default in the SDM (if you *really*
   want to change this, check out the configuration variable
   <code>eventListenerExporter</code>).  This makes life very much easier.
 */
public class LookupWithSDM {
    public static void main(String args[]) {
        /*
          Gotta do this to enable remote class downloading
         */
        System.setSecurityManager(new RMISecurityManager());

        try {
        /*
          We use LookupDiscoveryManager because it gives us a great deal
          of power in controlling the LUS'en we will do searches on.
          We can for example, disable all searching via multicast and use
          just a set of LookupLocators or we can do just multicast lookup
          on all groups (or a specific group) or we can do lookup based on
          a combination of multicast and LookupLocators.
         */
        LookupDiscoveryManager myLDM =
            new LookupDiscoveryManager(DiscoveryGroupManagement.ALL_GROUPS,
                                       null, null);

        ServiceDiscoveryManager myManager =
            new ServiceDiscoveryManager(myLDM, null);

        /*
          We're going to do our service finding via the caching mechanism but
          you could also use any of the lookup methods.  However, be aware that
          some of the lookup methods *don't block* which means they are
          executed against current lookup state.  This is significant because
          a recently started SDM might not have found any LUS'en yet.  If
          certain of the lookup methods are invoked under this circumstance
          they will return null rather than block as you might be expecting.
          You can avoid this problem by using some of the other lookup methods.
         */
        ServiceTemplate myTemplate =
            new ServiceTemplate(null, new Class[] {JavaSpace.class}, null);

        /*
          Let's just demonstrate the non-blocking lookup versus the blocking
          first.  We may or may not get a lookup for the non-blocking version
         */
        ServiceItem myMatch = myManager.lookup(myTemplate, null);

        if (myMatch != null) {
        } else {
            System.out.println("!!! Didn't find a match with non-blocking lookup, trying a blocking one !!!");

            /*
              Search for 5 seconds
             */
            try {
                myMatch = myManager.lookup(myTemplate, null, 5000);

                if (myMatch != null) {
                    System.out.println("*** Found a match with a blocking lookup ***");
                    DiscoveryUtil.dump(myMatch);
                }
            } catch (InterruptedException anIE) {
                System.err.println("!!! Whoops blocking lookup interrupted :( !!!");
            }
        }
        
        LookupCache myCache =
            myManager.createLookupCache(myTemplate, null,
                                        new ServiceDiscoveryListenerImpl());


        } catch (RemoteException anRE) {
            System.err.println("Failed to setup cache - exiting");
            anRE.printStackTrace(System.err);
            System.exit(-1);
        } catch (IOException anIOE) {
            System.err.println("Failed to setup managers - exiting");
            anIOE.printStackTrace(System.err);
            System.exit(-1);
        }

        /*
          Make sure we don't exit - this is important because lookup could
          be happening in separate, daemon threads and we need to give them
          time to do their thing.....
         */
        try {
            Object myLock = new Object();

            synchronized(myLock) {
                myLock.wait(0);
            }
        } catch (InterruptedException anIE) {
            System.err.println("Whoops main thread interrupted");
        }
    }

    private static class ServiceDiscoveryListenerImpl
        implements ServiceDiscoveryListener {

        public void serviceAdded(ServiceDiscoveryEvent anEvent) {
            /*
              Because this service has just been added there is no pre-Event
              state hence calling getPreEventServiceItem would return null -
              not much use in this case! :)
             */
            ServiceItem myItem = anEvent.getPostEventServiceItem();

            System.out.println("*** Found a matching service from Cache, hooray! ***");
            DiscoveryUtil.dump(myItem);
        }

        public void serviceChanged(ServiceDiscoveryEvent anEvent) {
            // Don't care
        }

        public void serviceRemoved(ServiceDiscoveryEvent anEvent) {
            /*
              Because this service has just been removed there is no post-Event
              state hence calling getPostEventServiceItem would return null -
              not much use in this case! :)
             */
            ServiceItem myItem = anEvent.getPreEventServiceItem();

            System.out.println("*** A matching service was removed from Cache, boo! ***");
            DiscoveryUtil.dump(myItem);
        }
    }
}
