Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/fetish/directory/Fada.java


1   package com.fetish.directory;
2   
3   import java.rmi.server.UnicastRemoteObject;
4   import java.rmi.server.RemoteObject;
5   import java.rmi.MarshalledObject;
6   import java.rmi.RemoteException;
7   import java.rmi.Remote;
8   import java.util.HashSet;
9   import java.util.Hashtable;
10  import java.util.Set;
11  import java.util.Collections;
12  import java.util.Iterator;
13  import java.util.Vector;
14  import java.util.TreeSet;
15  import java.util.Hashtable;
16  import java.util.EventListener;
17  import java.util.Vector;
18  import java.util.Enumeration;
19  import java.util.Map;
20  import java.rmi.Remote;
21  import java.io.Serializable;
22  import java.io.*;
23  import java.net.*;
24  import net.jini.core.lookup.*;
25  import net.jini.core.entry.Entry;
26  import net.jini.core.event.EventRegistration;
27  import net.jini.core.event.RemoteEventListener;
28  import net.jini.core.discovery.LookupLocator;
29  import net.jini.lookup.ServiceItemFilter;
30  import net.jini.core.lease.*;
31  import net.jini.lease.*;
32  import com.fetish.directory.*;
33  import com.fetish.directory.tool.*;
34  
35  /** 
36   * The Fetish Advanced Directory Architecture module (FADA)<p>
37   * Sample implementation of the LUSM node specification<br>
38   * This class provides an implementation of the LUSManager and Directory
39   * interfaces.  It can manage Fetish services (registration, deregistration,
40   * lookup) and connect to the FADA architecture, thus allowing clients
41   * that contact this node to get proxies for Fetish services that are
42   * registered in any other place within the FADA architecture.
43   */
44  
45  /*
46   * LUSManager and Directory implementation
47   */
48  public final class Fada extends UnicastRemoteObject
49                implements  LUSManager, Directory, Remote {
50  
51      // Data members
52  
53      private static final int LOCAL_SEARCH = 0;
54    private static final int NEIGHBOR_SEARCH = 1;
55  
56      private int threadCount = 0;
57      
58      private ServiceID id;
59      
60      // search
61      private long seqNum = 0;
62      private SearchSet searchSet;
63      private TreeSet searches = null;
64      
65      // jini information
66      private ServiceRegistrar registrar;
67      private LeaseRenewalManager lrm;
68      private Lease lease;
69      private String url;
70      
71      /** Maximum time allowed for lookup in milliseconds */
72      private int maxLookupTime = 3000;
73      
74      // hierarchical structure
75  
76      private EventListener listener;
77      private NeighborRegistry neighbors;
78      private Hashtable registry;
79      private Remote thisStub;
80  
81      // methods
82      /**
83       *
84       * Sets up a node of the FADA architecture.
85       * <p>
86       * Takes a <i>Jini</i> <code>ServiceRegistrar</code> as a parameter.
87       * @param registrar Reference to a Jini Lookup Service (a.k.a. Service
88       * Registrar).
89       * @param url The url this FADA node should be known for.  The url must
90       * be in the form &lt;host&gt;[:&lt;port&gt;].  No protocol prefix must be used.
91       * @throws RemoteException
92       */
93      public Fada( ServiceRegistrar registrar, String url )
94      throws RemoteException {
95  
96      super();
97      try {
98        if( System.getSecurityManager() == null )
99          System.setSecurityManager( new java.rmi.RMISecurityManager() );
100 
101       this.registrar = registrar;
102       this.neighbors = new NeighborRegistry();
103       this.lrm = new LeaseRenewalManager(); // Manages no leases initially
104       this.registry = new Hashtable(0);
105       this.thisStub = RemoteObject.toStub( this );
106       this.searches = new TreeSet();
107       LookupLocator locator = registrar.getLocator();
108       this.url = url;
109       System.out.println( "URL is " + this.url );
110     } catch( Exception ex ) {
111       throw new RemoteException( "Unable to initalize node: " +
112       ex.getMessage() );
113     } catch( Error er ) {
114       throw new RemoteException( "Unable to initalize node: " +
115       er.getMessage() );
116     }
117 
118     } 
119 
120   /**
121    * Stops a FADA node.
122    * Brings down a FADA node, deregistering all registered services from
123    * the Jini Lookup Service underneath (through Lease cancellation)
124    * and deregistering itself as well.
125    * @param password A String with the password for security checking.
126    * @throws java.rmi.RemoteException
127    */
128     public void stop( String password ) throws RemoteException {
129       // Notifiy all other nodes to stop using this one
130       //try {
131       //this.isolate();
132       //} catch( Exception e1 ) {
133       //}
134       //// Cancel leases for all registered services, thus deleting them
135       //Object[] leases = this.registry.values().toArray();
136       //for( int i=0; i<leases.length; i++ ) {
137         //try {
138         //( ( Lease )leases[i] ).cancel();
139         //} catch( Exception e2 ) {
140         //}
141       //}
142       //// Deregister from registrar
143       //try {
144       //this.lease.cancel();
145       //} catch( Exception e3 ) {
146       //}
147     }
148 
149     /**
150      * 
151      */
152     public void setLease( Lease lease ) {
153       if( lease != null ) {
154         this.lease = lease;
155         try {
156         this.lrm.renewFor( lease, lease.FOREVER, null );
157         } catch( Exception e ) {
158         }
159        }
160     }
161 
162     /**
163      * Compares with another object
164      * @return true if <code>other</code> is a reference to this object
165      */
166     public boolean equals( Object other ) {
167       boolean result = false;
168       if( other != null ) {
169       if( other instanceof LUSManager ) {
170         try {
171           result = ( ( ( LUSManager )other ).getServiceID().equals( this.getServiceID() ) );
172         } catch( Exception e ) {
173           result = false;
174         }
175       } else {
176         result = false;
177       }
178     }
179     return result;
180     }
181 
182     /**
183      * Returns the <i>Jini</i> <code>ServiceID</code> for this node.
184      * @return The <i>Jini</i> <code>ServiceID</code>
185      */
186     public ServiceID getServiceID() throws RemoteException {
187       return id;
188     }
189 
190    /**
191     * Returns the <i>Jini</i> URL for this node.
192     * @return The URL for this node in the form <ip/hostname>:<port>
193     */
194    public String getUrl() throws RemoteException {
195        return this.url;
196    }
197 
198     /**
199      * Sets the <i>Jini</i> <code>ServiceID</code> for this node.
200      * <br>For activation purposes (a <i>Jini</i> service should keep its
201      * <code>ServiceID</code> between activations and when restarting after
202      * a crash.
203      * @param id <i>Jini</i> <code>ServiceID</code> for this node.
204      */
205     public void setServiceID( ServiceID id ) throws RemoteException {
206       this.id = id;
207       this.searchSet = new SearchSet( this.id );
208       System.gc();
209     }
210 
211     /**
212      * Gets the <i>Jini</i> <code>LookupLocator</code> for this node.
213      * <br>The <code>LookupLocator</code> is the Lookup Service this node is
214      * registered into.  All Fetish services registered into this node are
215      * alse registered as a <i>Jini</i> service as well.  This method
216      * allows this to happen.
217      * @return <i>Jini</i> <code>LookupLocator</code> this node is
218      * registered into.
219      */
220     public LookupLocator getLocator() throws java.rmi.RemoteException {
221       return this.registrar.getLocator();
222     }
223     
224     // Administration
225     
226     /**
227      * Set the default maximum time allowed for lookups in milliseconds
228      */
229     public void setMaxTime( int maxTime ) throws RemoteException {
230       this.maxLookupTime = maxTime;
231     }
232 
233     /**
234      * Get the default maximum time allowed for lookups in milliseconds
235      */
236     public int getMaxTime() throws RemoteException {
237       return this.maxLookupTime;
238     }
239     
240     // Hierarchical structure
241     //
242     // This functions manage the hierarchical structure that compose the 
243     // directory
244     
245   /**
246    * Tests if there is any connection with any other node
247    * @return false if we are connected to any other node
248    */
249     public boolean isAlone() throws RemoteException {
250     return neighbors.size() == 0;
251     }
252 
253     ///**
254      //* Obtains the Remote reference (proxy) for a node, given its ServiceID.
255      //* <br>
256      //* Nodes are identified by its ServiceID, as its url may contain data
257      //* that's not global, and may return false for entries that should match.
258      //* @param sid The ServiceID of a given node whose url we have
259      //* @returns The LUSManager interface of the node with ServiceID sid
260      //* @throws RemoteException If there was a communication error
261      //*/
262     //private LUSManager contactNeighbor( ServiceID sid, boolean delete )
263     //throws RemoteException {
264       //if( sid == null ) {
265         //return null;
266       //}
267       //if( neighbors.containsKey( sid ) ) {
268         //LUSManager result = ( LUSManager )contactNeighbor( ( String )neighbors.get( sid ), delete );
269         //// Should check node availability
270         //return result;
271       //} else {
272         //return null;
273       //}
274     //}
275 
276     private LUSManager contactNeighbor( String url, boolean delete ) throws RemoteException {
277       boolean error = false;
278       if( url == null ) {
279         return null;
280       }
281     LookupLocator locator = null;
282     Object lusm = null;
283     try {
284       locator = new LookupLocator( "jini://" + url );
285     } catch( Exception e ) {
286     }
287     if( locator == null ) {
288       // Unable to contact remote peer
289       if( delete ) {
290         neighbors.remove( url );
291       }
292     } else {
293       ServiceRegistrar registrar = null;
294       ServiceTemplate template = null;
295       try {
296         registrar = locator.getRegistrar();
297         Class[] classes = { LUSManager.class };
298         template =  new ServiceTemplate(
299           null,
300           classes,
301           null
302         );
303       } catch( Exception ex1 ) {
304       }
305       if( registrar == null ) {
306         if( delete ) {
307           neighbors.remove( url );
308         }
309         return null;
310       }
311       try {
312         lusm = registrar.lookup( template );
313       } catch( Exception ex2 ) {
314       }
315       if( lusm == null ) {
316         if( delete ) {
317           neighbors.remove( url );
318         }
319       }
320     }
321     System.gc();
322     return ( LUSManager )lusm;
323     }
324 
325   /**
326    * Connects this node to another one, thus allowing to create a
327    * graph of FADA nodes.
328    * @param node <code>LUSManager<code> to connect this node to.
329    * @return true if the connection was successful
330    * @throws RemoteException
331    */
332   public boolean connect( LUSManager node ) throws RemoteException {
333 
334     if( node == null ) {
335       return false;
336     }
337     String nodeUrl = null;
338     ServiceID sid = null;
339     try {
340       nodeUrl = node.getUrl();
341       sid = node.getServiceID();
342     } catch( Exception e ) {
343       return false;
344     }
345     if( ( nodeUrl == null ) || ( sid == null ) ) {
346       return false;
347     }
348     
349     if( ( neighbors.contains( nodeUrl ) ) || ( this.id.equals( sid ) ) )
350       return false;
351     try {
352       node.justConnect( ( LUSManager )this );
353       neighbors.put( nodeUrl, sid );
354     } catch( Exception e ) {
355       if( neighbors.contains( nodeUrl ) ) {
356         neighbors.remove( nodeUrl );
357         System.gc();
358       }
359       return false;
360     }
361     return true;
362   }
363 
364   public boolean justConnect( LUSManager node ) throws RemoteException {
365 
366     if( node == null )
367       return false;
368     ServiceID sid = null;
369     String nodeUrl = null;
370     try {
371       sid = node.getServiceID();
372       nodeUrl = node.getUrl();
373     } catch( Exception e ) {
374       return false;
375     }
376     if( ( sid == null ) || ( nodeUrl == null ) ) {
377       return false;
378     }
379     
380     if( ( neighbors.contains( nodeUrl ) ) || ( this.id.equals( sid ) ) )
381       return false;
382     neighbors.put( nodeUrl, sid );
383     return true;
384   }
385 
386   /**
387    * Disconnects this node from the specified node, leaving all other
388    * connections untouched
389    * @param node The node to disconnect this node from
390    * @return true if the disconnection was succesfull (this node was
391    * connnected to the node specified as a parameter)
392    * @throws RemoteException
393    */
394     public boolean disconnect( LUSManager node ) throws RemoteException {
395       String nodeUrl = null;
396       ServiceID nodeSid = null;
397       try {
398       nodeUrl = node.getUrl();
399       nodeSid = node.getServiceID();
400       } catch( Exception e ) {
401       }
402       if( ( nodeUrl == null ) || ( nodeSid == null ) ) {
403         return false;
404       }
405       if( this.neighbors.contains( nodeUrl ) ) {
406         this.neighbors.remove( nodeUrl );
407         try {
408         boolean state = node.justDisconnect( ( LUSManager )this );
409         return state;
410       } catch( Exception e ) {
411         return false;
412       }
413       } else {
414         return false;
415       }
416     }
417 
418   /**
419    * Disconnects this node from the specified node, leaving all other
420    * connections untouched
421    * @param node The node to disconnect this node from
422    * @return true if the disconnection was succesfull (this node was
423    * connnected to the node specified as a parameter)
424    * @throws RemoteException
425    */
426     public boolean justDisconnect( LUSManager node ) throws RemoteException {
427       ServiceID nodeSid = null;
428       String nodeUrl = null;
429       try {
430         nodeSid = node.getServiceID();
431         nodeUrl = node.getUrl();
432       } catch( Exception e ) {
433       }
434       if( ( nodeSid == null ) || ( nodeUrl == null ) ) {
435         return false;
436       }
437       if( this.neighbors.contains( nodeUrl ) ) {
438         this.neighbors.remove( nodeUrl );
439         return true;
440       } else {
441         return false;
442       }
443     }
444 
445   /**
446    * Disconnects this node from the specified node, leaving all other
447    * connections untouched
448    * @param node The node to disconnect this node from
449    * @return true if the disconnection was succesfull (this node was
450    * connnected to the node specified as a parameter)
451    * @throws RemoteException
452    */
453     public boolean disconnect( String node ) throws RemoteException {
454 
455       if( this.neighbors.contains( node ) ) {
456       LUSManager neighbor = null;
457       String neighborUrl = null;
458         try {
459         neighbor = contactNeighbor( node, false );
460         neighborUrl = neighbor.getUrl();
461       } catch( Exception e ) {
462       }
463       if( ( neighbor == null ) || ( neighborUrl == null ) ) {
464         return false;
465       }
466       try {
467         this.neighbors.remove( neighborUrl );
468         } catch( Exception ex4 ) {
469         } catch( Error er ) {
470         }
471         try {
472         boolean state = neighbor.justDisconnect( this.url );
473         return state;
474       } catch( Exception e ) {
475         return false;
476       }
477       } else {
478         return false;
479       }
480     }
481 
482   /**
483    * Disconnects this node from the specified node, leaving all other
484    * connections untouched
485    * @param node The node to disconnect this node from
486    * @return true if the disconnection was succesfull (this node was
487    * connnected to the node specified as a parameter)
488    * @throws RemoteException
489    */
490     public boolean justDisconnect( String node ) throws RemoteException {
491       if( this.neighbors.contains( node ) ) {
492         this.neighbors.remove( node );
493         return true;
494       } else {
495         return false;
496       }
497     }
498 
499     /**
500      * Disconnects this node from all nodes.
501      * <br>The node becomes an isolated one, a Fetish community on its own.
502      * @throws RemoteException
503      */
504     public void isolate() throws RemoteException {
505       String[] neighborUrls = this.neighbors.getUrls();
506       if( neighborUrls == null )
507         return;
508       if( neighborUrls.length == 0 )
509         return;
510       String auxUrl = null;
511     for( int i = 0; i < neighborUrls.length; i++ ) {
512       try {
513         auxUrl = neighborUrls[i];
514         LUSManager neighborLusm = contactNeighbor( auxUrl, false );
515         if( neighborLusm != null ) {
516           new Thread( new Isolator( neighborLusm ) ).start();
517         }
518       } catch( Exception e ) {
519       }
520     }
521       this.neighbors.clear();
522     }
523 
524     private class Isolator implements Runnable {
525       LUSManager neighbor;
526       public Isolator( LUSManager neighbor ) {
527         this.neighbor = neighbor;
528       }
529       public void run() {
530         try {
531           this.neighbor.justDisconnect( Fada.this.url );
532         } catch( Exception e ) {
533         }
534       }
535     }
536 
537   ///**
538    //* Returns an array with all the nodes this one has been connected to
539    //* @return <code>LUSManager</code> array - The nodes this one is
540    //* connected to.
541    //* @throws RemoteException
542    //*/
543     //public LUSManager[] getNeighbors() throws RemoteException {
544       //LUSManager[] result = new LUSManager[ this.neighbors.size() ];
545       //Object[] aux = this.neighbors.entrySet().toArray();
546       //if( result == null )
547         //return new LUSManager[0];
548       //if( result.length == 0 )
549         //return new LUSManager[0];
550       //for( int i=0; i<aux.length; i++ ) {
551         //result[i] = contactNeighbor( ( ServiceID )( (java.util.Map.Entry)aux[i] ).getKey(), false );
552       //}
553       //return result;
554     //}
555     
556   /**
557    * Returns an array with all the nodes this one has been connected to
558    * @return <code>LUSManager</code> array - The nodes this one is
559    * connected to.
560    * @throws RemoteException
561    */
562     public LUSManager[] getNeighbors() throws RemoteException {
563       String[] aux = this.neighbors.getUrls();
564       if( aux == null )
565         return new LUSManager[0];
566       if( aux.length == 0 )
567         return new LUSManager[0];
568       LUSManager[] result = new LUSManager[ this.neighbors.size() ];
569       String auxUrl = null;
570       int j = 0;
571       for( int i=0; i<aux.length; i++ ) {
572         auxUrl = ( String )aux[i]; 
573         LUSManager auxNode =  contactNeighbor( auxUrl, true );
574         if( auxNode != null ) {
575           result[j] = auxNode;
576           j++;
577         }
578       }
579       LUSManager[] result2 = null;
580       if( j > 0 ) {
581         result2 = new LUSManager[ j ];
582       for( int i = 0; i < j; i++ ) {
583         result2[i] = result[i];
584       }
585       }
586       System.gc();
587       return result2;
588     }
589     
590     // Directory methods
591     //
592     // Methods implementing the behaviour specified in the Directory interface
593 
594   /**
595    * Not used
596    */
597   public void startedSearches( SearchIDInterface sid, int started )
598   throws RemoteException {
599     searchSet.startedSearches( sid, started );
600   }
601 
602   /**
603    * Not used
604    */
605   public void startedSearches( SearchIDInterface search, ServiceID service )
606   throws RemoteException {
607     // Adds the service to the list of nodes to wait results from
608     // on search identified by SearchID search
609   }
610 
611   /**
612    * Not used
613    */
614   public void finishedSearch( SearchIDInterface sid ) throws RemoteException {
615     // Decreases count of started threads to wait results from
616     searchSet.finishedSearch( sid );
617   }
618 
619      /**
620       * Extends the search through the connections available from the node.
621       * @param criteria - The <i>Jini</i> <code>SearchCriteria</code> to
622       * lookup.
623       * @param excluded - The <code>node</code> to exclude from the search.
624       * Usually the node that propagated the search process, it means, this one.
625       * @param target - The <code>node</code> to give the lookup results to.
626       * @param searchID - The <code>SearchID</code> of this lookup process.
627       * @param maxHops - Maximum number of hops allowed, to limit the lookup
628       * process in extension.  It is decreased in every hop.  When it reaches
629       * 0 the lookup process is no longer extended to any neighbor.
630       * @param timeout - Maximum number of milliseconds to wait for results, to
631       * limit the lookup process in time.
632       * @param maxResponses - Maximum number of responses allowed, to limit
633       * the lookup process in space.
634       * @throws RemoteException
635       */
636      private void extendLookup(  SearchCriteriaInterface criteria,
637                                  String excluded, 
638                                  String target, SearchIDInterface searchID,
639                                  int maxHops, long timeout, int maxResponses ) {
640 
641     // If we have reached the maximum number of hops, leave
642     if( maxHops == 0 ) {
643       return;
644     }
645     // number of threads started
646     int started = 0;
647 
648       // starts the neighbors search
649       // gets the neighbors that must be visited
650 
651     Object[] neighbors2search = this.neighbors.getUrls();
652     if( neighbors2search == null ) {
653       return;
654     }
655     if( neighbors2search.length == 0 ) {
656       return;
657     }
658 
659     LUSManager neighbor = null;
660     LUSManager excludedNode = null;
661     LUSManager targetNode = null;
662     String neighborUrl = null;
663     ServiceID neighborSID = null;
664 
665     try {
666       // Notifiy number of searches started, all neighbors + this node
667       startedSearches( searchID, neighbors2search.length + 1 );
668       for( int i = 0; i < neighbors2search.length; i++ ){
669         try {
670           neighborUrl = ( String )neighbors2search[i];
671           targetNode = contactNeighbor( target, false );
672           neighbor = contactNeighbor( neighborUrl, false );
673           excludedNode = contactNeighbor( excluded, false );
674           if( neighbor == null ) {
675             if( targetNode != null ) {
676               ( ( Directory )targetNode ).addLookupResult(
677                 searchID, null
678               );
679             }
680             return;
681           }
682           boolean proceed = false;
683           if( excludedNode == null ) {
684             proceed = true;
685           }
686           if( !proceed ) {
687             proceed =
688             !neighborSID.equals( excludedNode.getServiceID() );
689           }
690           if( proceed ) {
691             new Thread( 
692               new Explorer(  NEIGHBOR_SEARCH,
693                     criteria,
694                     neighborUrl,
695                     target,
696                     searchID,
697                     maxHops - 1,
698                     timeout,
699                     maxResponses ) 
700             ).start();
701           } else {
702           // Notifiy target to decrease thread count,
703           // we're not doing any search
704             ( ( Directory )targetNode ).addLookupResult(
705               searchID, null
706             );
707           }
708           connect( targetNode );
709         } catch( Exception e ) {
710           // Unable to connect.  Reference should be annotated.
711         }
712       }
713     } catch( Exception e ) {
714     }
715   }
716 
717     /**
718      * Starts the lookup process on a node.
719      * <br>The lookup process will be extended to all connected nodes, which in
720      * turn will extend the search process to all connected nodes, granting
721      * full graph lookup.  It follows a flooding-style algorithm.
722      * @param criteria The <i>Jini</i> <code>SearchCriteria</code> for
723      * the lookup.
724      * @param nHops Maximum number of lookup extension hops allowed.  The
725      * lookup process will search all nodes at a distance less or equal to
726      * nHops.
727      * @param timeout Maximum number of milliseconds the lookup process will
728      * last.  When that time has passed the current results are returned.
729      * @param maxResponses Maximum number of responses we want.  When
730      * maxResponses is reached the current results are returned, discarding
731      * further results.
732      * @throws RemoteException
733      */
734     public ServiceMatches lookup(
735       SearchCriteriaInterface criteria, 
736       int nHops,
737       long timeout,
738       int maxResponses
739   ) throws RemoteException {
740 
741         // initializes the search process
742         SearchIDInterface searchID = searchSet.newSearch( maxResponses );
743 
744       // remote search
745     extendLookup(
746       criteria,
747       null,
748       this.url,
749       searchID,
750       nHops,
751       timeout,
752       maxResponses
753     );
754 
755         // local search
756     new Thread (
757       new Explorer(
758         LOCAL_SEARCH,
759         criteria,
760         this.url,
761         searchID,
762         nHops,
763         timeout,
764         maxResponses
765       )
766     ).start();
767         
768         // return result
769         ServiceMatches result = searchSet.getResults( searchID );
770         return result;
771     }
772 
773     /**
774      * Returns all services registered in this node.
775      */
776   public ServiceMatches getServices() throws RemoteException {
777     ServiceMatches sm = filterResults(
778       registrar.lookup(
779         new ServiceTemplate( null, null, null ),
780         Integer.MAX_VALUE
781       )
782     );
783     return sm;
784      }
785 
786   /**
787    * Lookup method for extension of lookup process between nodes.
788    * <br>
789    * @param criteria - The <i>Jini</i> <code>SearchCriteria</code>
790    * used to perform the <i>Jini</i> lookup.
791    * @param id - The <code>SearchID</code> for this lookup process.
792    * @param target - The node that started the search process.
793    * @param excluded - The node that should be excluded from lookup
794    * extension.  Usually the node that extends the lookup process to this
795    * one.
796      * @param maxHops Maximum number of lookup extension hops allowed.  The
797      * lookup process will search all nodes at a distance less or equal to
798      * maxHops.
799      * @param timeout Maximum number of milliseconds the lookup process will
800      * last.  When that time has passed the current results are returned.
801      * @param maxResponses Maximum number of responses we want.  When
802      * maxResponses is reached the current results are returned, discarding
803      * further results.
804      * @throws RemoteException
805      */
806 
807     public boolean lookup(
808       SearchCriteriaInterface criteria, 
809       SearchIDInterface id,
810       String target,
811       String excluded,
812       int maxHops,
813       long timeout,
814       int maxResponses
815     ) throws RemoteException {
816 
817     // If SearchID is in the list, return right NOW
818     if( searches.contains( id ) )
819       return false;
820 
821     // Otherwise, add SearchID to the list
822     searches.add( id );
823     ServiceMatches resultsm = new ServiceMatches( null, 0 );
824 
825     // Extend lookup process to every connected node but the excluded
826     extendLookup(
827       criteria,
828       excluded,
829       target,
830       id,
831       maxHops,
832       timeout,
833       maxResponses
834     );
835 
836     // Lookup internally
837     new Thread(
838       new Explorer(
839         LOCAL_SEARCH,
840         criteria,
841         target,
842         id,
843         maxHops,
844         timeout,
845         maxResponses
846       )
847     ).start();
848     System.gc();
849     return true;
850     }
851 
852 
853   /**
854    * Register a Fetish service within this node.
855    * <br>@param item - The <i>Jini</i> <code>ServiceItem</code> to register
856    * inside the Lookup Service this node is registered into.
857    * @param leaseDuration - Number of milliseconds we want the service to
858    * be registered.  After that period of time passes the service will be
859    * unregistered and no longer available for lookup.
860    * @return <i>Jini</i> <code>ServiceRegistration</code> returned from
861    * the <i>Jini</i> Lookup Service.
862    * @throws RemoteException
863    */
864   public ServiceRegistration register( ServiceItem item, long leaseDuration )
865   throws java.rmi.RemoteException {
866         
867     ServiceRegistration registration =
868       registrar.register( item, leaseDuration );
869     Lease lease = registration.getLease();
870     ServiceID sid = registration.getServiceID();
871     FetishLease flease = new FetishLeaseImpl( lease, this.registry, sid );
872     this.registry.put( sid, flease );
873     FetishLeaseProxy flproxy =
874       new FetishLeaseProxy(
875         ( FetishLease )RemoteObject.toStub(
876           UnicastRemoteObject.exportObject( flease )
877         ),
878         lease.getExpiration() - System.currentTimeMillis(),
879         lease.getSerialFormat()
880       );
881     FetishServiceRegistration fsr =
882       new FetishServiceRegistration(
883         registration, flproxy
884       );
885     System.gc();
886     return fsr;
887     }
888 
889   public void unregister( ServiceID sid ) throws RemoteException {
890 
891     if( this.registry.containsKey( sid ) ) {
892       FetishLease flease = ( FetishLease )this.registry.get( sid );
893       try {
894         flease.cancel();
895       } catch( Exception e ) {
896         throw new RemoteException( e.getMessage() );
897       }
898     }
899   }
900 
901     /**
902      * Returns a ServiceMatches object
903      * containing ServiceItems registered in this FADA.
904      */
905     private ServiceMatches filterResults( ServiceMatches sm ) {
906       Vector v = new Vector( 0, 1 );
907       for( int i = 0; i < sm.totalMatches; i++ ) {
908         if( registry.containsKey( sm.items[i].serviceID ) ) {
909           v.add( sm.items[i] );
910         }
911       }
912       ServiceItem si[] = new ServiceItem[ v.size() ];
913       for( int i = 0; i < v.size(); i++ ) {
914         si[i] = ( ServiceItem )v.get( i );
915       }
916       System.gc();
917       return new ServiceMatches( si, v.size() );
918     }
919 
920 
921   //public EventRegistration notify( ServiceTemplate tmpl,
922                    //int transitions,
923                    //RemoteEventListener listener,
924                    //MarshalledObject handback,
925                    //long leaseDuration )
926   //throws java.rmi.RemoteException {
927       //EventRegistration registration =  registrar.notify(
928                       //tmpl,
929                       //transitions,
930                       //listener,
931                       //handback,
932                       //leaseDuration
933                         //);
934      // 
935       //return registration;
936   //}
937 
938     // LUS Manager methods
939     //
940     // Methods implementing the behaviour needed to manage
941     // the federation of nodes
942     /**
943      * 
944      */
945     public void addLookupResult( SearchIDInterface searchID, ServiceMatches sm )
946     throws RemoteException {
947        searchSet.addResult( searchID, sm );
948     }
949 
950     /**
951      * This class starts dedicated threads to extend the search 
952      */
953 
954   private class Explorer implements Runnable {
955 
956     private SearchCriteriaInterface crit;
957     private String target, neighbor;
958     private SearchIDInterface search;
959     private int id;
960     private int maxHops;
961     private long timeout;
962     private int maxResponses;
963     private int searchType = LOCAL_SEARCH;
964 
965     public Explorer (
966       int searchType,
967       SearchCriteriaInterface crit,
968       String target,
969       SearchIDInterface search,
970       int maxHops,
971       long timeout,
972       int maxResponses
973     ) {
974 
975       id = threadCount++;
976       this.searchType = searchType;
977       this.crit = crit;
978       this.target = target;
979       this.search = search;
980       this.maxHops = maxHops;
981       this.timeout = timeout;
982       this.maxResponses = maxResponses;
983       this.neighbor = null;
984     }
985 
986     public Explorer (
987       int searchType,
988       SearchCriteriaInterface crit,
989       String neighbor,
990       String target,
991       SearchIDInterface search,
992       int maxHops,
993       long timeout,
994       int maxResponses
995     ) {
996       this(
997         searchType,
998         crit,
999         target,
1000        search,
1001        maxHops,
1002        timeout,
1003        maxResponses
1004      );
1005      this.neighbor = neighbor;
1006    } 
1007
1008    public void run() {
1009      switch (searchType) {
1010        case LOCAL_SEARCH : {
1011          try {
1012            ServiceMatches sm = filterResults(
1013              registrar.lookup(
1014                crit.toServiceTemplate(),
1015                crit.getMaxMatches()
1016              )
1017            );
1018            Directory targetNode =
1019              ( Directory )contactNeighbor( target, false );
1020            if( targetNode != null ) {
1021              targetNode.addLookupResult( search, sm );
1022            }
1023          } catch ( RemoteException re ) {
1024          }
1025          break;
1026        }
1027        case NEIGHBOR_SEARCH : {
1028          try {
1029            if( this.neighbor != null ) {
1030              Directory neighborDir =
1031                ( Directory) contactNeighbor( this.neighbor, false );
1032              if( neighborDir != null ) {
1033                neighborDir.lookup(
1034                    crit,
1035                    search,
1036                    target,
1037                    url,
1038                    maxHops - 1,
1039                    timeout,
1040                    maxResponses
1041                );
1042              }
1043            }
1044          } catch ( RemoteException re ) {
1045
1046            // We were unable to contact the neighbor,
1047            // we should delete it
1048
1049            if( neighbors.contains( this.neighbor ) ) {
1050              neighbors.remove( this.neighbor );
1051            }
1052          }
1053          break;
1054        }
1055        default:
1056          break;
1057      }
1058    }
1059  }
1060
1061  /**
1062   * Starts and registers the FADA node.
1063   * The first argument is the url of the Jini Lookup Service that
1064   * will hold the FADA proxy.
1065   */
1066
1067  public static void main( String args[] ) throws Exception {
1068      if( System.getSecurityManager() == null ) {
1069      System.setSecurityManager(
1070        new java.rmi.RMISecurityManager()
1071      );
1072      }
1073    String url = null;
1074          if ( args.length == 0 ) {
1075            System.err.println( "You must specify the url" );
1076            System.exit( 1 );
1077          } else {
1078             url = args[0];
1079          }
1080          try {
1081            URL myUrl = new URL( "http://" + url );
1082            int port = myUrl.getPort();
1083            if( port == -1 ) {
1084              url = myUrl.getHost() + ":4160";
1085            }
1086          } catch( MalformedURLException mue ) {
1087            System.err.println( url + " is not a valid FADA url" );
1088            System.err.println( "Abandoning..." );
1089            System.exit( 1 );
1090          }
1091          System.out.println( "The URL is " + url );
1092        LookupLocator locator = null;
1093        try {
1094          locator = new LookupLocator( "jini://" + url );
1095        } catch( Exception ex1 ) {
1096        }
1097        if( locator == null ) {
1098          System.err.println( "You must set up a Jini Lookup Server" );
1099          System.exit( 1 );
1100        }
1101
1102        System.out.println( "Getting registrar ..." );
1103        ServiceRegistrar registrar = null;
1104        try {
1105          registrar = locator.getRegistrar();
1106        } catch( Exception e ) {
1107        }
1108        if( registrar == null ) {
1109          System.err.println( "Unable to get ServiceRegistrar" );
1110          System.exit( 1 );
1111        }
1112
1113        System.out.println( "Creating Fada..." );
1114        Fada fada = new Fada( registrar, url );
1115        FadaProxy fp = new FadaProxy( fada.toStub( fada ), url );
1116        System.out.println( "Fada created" );
1117
1118        ServiceID sid = null;
1119        ServiceItem si = new ServiceItem(
1120            sid,
1121            fp,
1122            null
1123        );
1124        ServiceRegistration sr = null;
1125        try {
1126            registrar = locator.getRegistrar();
1127            sr = registrar.register( si, Lease.ANY );
1128            Lease lease = sr.getLease();
1129            sid = sr.getServiceID();
1130            fada.setLease( lease );
1131            fada.setServiceID( sid );
1132            System.out.println( "FADA service registered" );
1133        } catch( Exception ex3 ) {
1134            System.err.println( "Unable to register service.  Abandoning..." );
1135            System.exit( 1 );
1136        }
1137        // Service registered, don't die
1138        for( ; true; ) {
1139          System.gc();
1140          try {
1141            Thread.sleep( 3600000 ); // Sleep an hour between gc
1142          } catch( Exception e ) {
1143          }
1144        }
1145  }
1146}