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 <host>[:<port>]. 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}