]> sjero.net Git - linphone/blob - p2pproxy/dependencies-src/jxse-src-2.5/api/src/net/jxta/protocol/RouteAdvertisement.java
6748e78cdc82e918a1f2f00bc84fc741ab21fa95
[linphone] / p2pproxy / dependencies-src / jxse-src-2.5 / api / src / net / jxta / protocol / RouteAdvertisement.java
1 /*
2 Copyright (c) 2001-2007 Sun Microsystems, Inc.  All rights reserved.
3  *  
4  *  The Sun Project JXTA(TM) Software License
5  *  
6  *  Redistribution and use in source and binary forms, with or without 
7  *  modification, are permitted provided that the following conditions are met:
8  *  
9  *  1. Redistributions of source code must retain the above copyright notice,
10  *     this list of conditions and the following disclaimer.
11  *  
12  *  2. Redistributions in binary form must reproduce the above copyright notice, 
13  *     this list of conditions and the following disclaimer in the documentation 
14  *     and/or other materials provided with the distribution.
15  *  
16  *  3. The end-user documentation included with the redistribution, if any, must 
17  *     include the following acknowledgment: "This product includes software 
18  *     developed by Sun Microsystems, Inc. for JXTA(TM) technology." 
19  *     Alternately, this acknowledgment may appear in the software itself, if 
20  *     and wherever such third-party acknowledgments normally appear.
21  *  
22  *  4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must 
23  *     not be used to endorse or promote products derived from this software 
24  *     without prior written permission. For written permission, please contact 
25  *     Project JXTA at http://www.jxta.org.
26  *  
27  *  5. Products derived from this software may not be called "JXTA", nor may 
28  *     "JXTA" appear in their name, without prior written permission of Sun.
29  *  
30  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
31  *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
32  *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN 
33  *  MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
34  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
35  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
36  *  OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
37  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
38  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
39  *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40  *  
41  *  JXTA is a registered trademark of Sun Microsystems, Inc. in the United 
42  *  States and other countries.
43  *  
44  *  Please see the license information page at :
45  *  <http://www.jxta.org/project/www/license.html> for instructions on use of 
46  *  the license in source files.
47  *  
48  *  ====================================================================
49  *  
50  *  This software consists of voluntary contributions made by many individuals 
51  *  on behalf of Project JXTA. For more information on Project JXTA, please see 
52  *  http://www.jxta.org.
53  *  
54  *  This license is based on the BSD license adopted by the Apache Foundation. 
55  */
56 package net.jxta.protocol;
57
58 import net.jxta.document.AdvertisementFactory;
59 import net.jxta.document.ExtendableAdvertisement;
60 import net.jxta.endpoint.EndpointAddress;
61 import net.jxta.id.ID;
62 import net.jxta.id.IDFactory;
63 import net.jxta.peer.PeerID;
64 import net.jxta.peergroup.PeerGroupID;
65
66 import java.io.ByteArrayInputStream;
67 import java.io.InputStream;
68 import java.util.ArrayList;
69 import java.util.Collection;
70 import java.util.Enumeration;
71 import java.util.HashSet;
72 import java.util.Iterator;
73 import java.util.List;
74 import java.util.ListIterator;
75 import java.util.Set;
76 import java.util.Vector;
77
78 /**
79  * Advertisement used to represent a route to a peer. Routes are represented in
80  * a generic manner as a sequence of hops to reach the destination. Each hop
81  * represent a potential relay peer in the route:
82  * <p/>
83  * <pre> Dest
84  *       hop 1
85  *       hop 2
86  *       hop 3
87  *       ....
88  *       hop n
89  * </pre>
90  * <p/>
91  * A route can have as many hops as necessary. Hops are implicitly ordered
92  * starting from the hop with the shortest route to reach the destination. If a
93  * peer cannot reach directly the dest, it should try to reach in descending
94  * order one of the listed hops. Some hops may have the same physical distance
95  * to the destination. Some hops may define alternative routes.
96  * <p/>
97  * The destination and hops are defined using a AccessPointAdvertisements.
98  *
99  * @see net.jxta.protocol.PeerAdvertisement
100  * @see net.jxta.protocol.AccessPointAdvertisement
101  */
102 public abstract class RouteAdvertisement extends ExtendableAdvertisement implements Cloneable {
103
104     public static final String DEST_PID_TAG = "DstPID";
105
106     /**
107      * AccessPointAdvertisement of destination peer.
108      */
109     private transient AccessPointAdvertisement dest = (AccessPointAdvertisement)
110             AdvertisementFactory.newAdvertisement(AccessPointAdvertisement.getAdvertisementType());
111
112     /**
113      * Semi-ordered list of alternative hops to the destination.
114      */
115     private transient Vector<AccessPointAdvertisement> hops = new Vector<AccessPointAdvertisement>();
116
117     /**
118      * Cached value for {@link #getID()}
119      */
120     private transient ID hashID = null;
121
122     /**
123      * construct a new route
124      * <p/>
125      * <b>WARNING hops may be MODIFIED.</b>
126      *
127      * @param destPid  destination
128      * @param firsthop first hop node ID
129      * @param hops     routes
130      * @return the new route
131      */
132     public static RouteAdvertisement newRoute(PeerID destPid, PeerID firsthop, Vector<AccessPointAdvertisement> hops) {
133
134         if (destPid == null) {
135             throw new IllegalArgumentException("Missing destination peer id.");
136         }
137
138         for (AccessPointAdvertisement apa : hops) {
139             if (null == apa) {
140                 throw new IllegalArgumentException("Bad route. null APA.");
141             }
142
143             if (apa.getPeerID() == null) {
144                 throw new IllegalArgumentException("Bad route. Incomplete APA.");
145             }
146         }
147
148         RouteAdvertisement route = (RouteAdvertisement)
149                 AdvertisementFactory.newAdvertisement(RouteAdvertisement.getAdvertisementType());
150
151         route.setDestPeerID(destPid);
152
153         // set the route hops
154         route.setHops(hops);
155
156         // check if the given first hop is already in the route if not add it
157         // (note: we do not expect it to be there, but it is acceptable).
158         if (firsthop != null) {
159             AccessPointAdvertisement ap = route.getFirstHop();
160
161             if (ap == null || !ap.getPeerID().equals(firsthop)) {
162                 ap = (AccessPointAdvertisement)
163                         AdvertisementFactory.newAdvertisement(AccessPointAdvertisement.getAdvertisementType());
164                 ap.setPeerID(firsthop);
165                 route.setFirstHop(ap);
166             }
167         }
168
169         return route;
170     }
171
172     /**
173      * {@inheritDoc}
174      */
175     @Override
176     public RouteAdvertisement clone() {
177         try {
178             RouteAdvertisement a = (RouteAdvertisement) super.clone();
179
180             a.setDest(getDest());
181
182             // deep copy of the hops
183             Vector<AccessPointAdvertisement> clonehops = getVectorHops();
184
185             ListIterator<AccessPointAdvertisement> eachHop = clonehops.listIterator();
186
187             while (eachHop.hasNext()) {
188                 eachHop.set(eachHop.next().clone());
189             }
190
191             a.setHops(clonehops);
192
193             return a;
194         } catch (CloneNotSupportedException impossible) {
195             throw new Error("Object.clone() threw CloneNotSupportedException", impossible);
196         }
197     }
198
199     /**
200      * makes a copy of a route advertisement
201      * that only contains PID not endpoint addresses
202      *
203      * @return object clone route advertisement
204      */
205     public RouteAdvertisement cloneOnlyPIDs() {
206         RouteAdvertisement routeAdvertisement;
207
208         try {
209             routeAdvertisement = (RouteAdvertisement) super.clone();
210             routeAdvertisement.setDestEndpointAddresses(new Vector<String>());
211         } catch (CloneNotSupportedException impossible) {
212             throw new Error("Object.clone() threw CloneNotSupportedException", impossible);
213         }
214
215         // deep copy of the hops
216         Vector<AccessPointAdvertisement> clonehops = routeAdvertisement.getVectorHops();
217
218         ListIterator<AccessPointAdvertisement> eachHop = clonehops.listIterator();
219
220         while (eachHop.hasNext()) {
221             AccessPointAdvertisement aHop = eachHop.next();
222
223             eachHop.set(aHop.clone());
224         }
225
226         routeAdvertisement.setHops(clonehops);
227         return routeAdvertisement;
228     }
229
230     /**
231      * Compare if two routes are equals. Equals means same destination with the
232      * same endpoint addresses and thee same number of hops and the same
233      * endpoint addresses for each hop.
234      *
235      * @param target the route to compare against
236      * @return boolean true if the route is equal to this route otherwise false
237      */
238     @Override
239     public boolean equals(Object target) {
240
241         if (this == target) {
242             return true;
243         }
244
245         if (!(target instanceof RouteAdvertisement)) {
246             return false;
247         }
248
249         RouteAdvertisement route = (RouteAdvertisement) target;
250
251         // check the destination
252         if (!dest.equals(route.getDest())) {
253             return false;
254         }
255
256         // check each of the hops
257
258         // routes need to have the same size
259         if (hops.size() != route.size()) {
260             return false;
261         }
262
263         int index = 0;
264
265         for (AccessPointAdvertisement hop : route.hops) {
266             if (!hop.equals(hops.get(index++))) {
267                 return false;
268             }
269         }
270
271         return true;
272     }
273
274     /**
275      * {@inheritDoc}
276      */
277     @Override
278     public int hashCode() {
279         if (null != dest.getPeerID()) {
280             return dest.getPeerID().hashCode();
281         } else {
282             // force all incomplete advertisements to hash to the same place.
283             return 1;
284         }
285     }
286
287     /**
288      * Returns the identifying type of this Advertisement.
289      *
290      * @return String the type of advertisement
291      */
292     public static String getAdvertisementType() {
293         return "jxta:RA";
294     }
295
296     /**
297      * {@inheritDoc}
298      */
299     @Override
300     public final String getBaseAdvType() {
301         return getAdvertisementType();
302     }
303
304     /**
305      * {@inheritDoc}
306      */
307     @Override
308     public synchronized ID getID() {
309         if (null == dest.getPeerID()) {
310             throw new IllegalStateException("Destination peerID not defined. Incomplete RouteAdvertisement");
311         }
312
313         if (hashID == null) {
314             try {
315                 // We have not yet built it. Do it now
316                 byte[] seed = getAdvertisementType().getBytes("UTF-8");
317                 InputStream in = new ByteArrayInputStream(dest.getPeerID().toString().getBytes("UTF-8"));
318
319                 hashID = IDFactory.newCodatID((PeerGroupID) dest.getPeerID().getPeerGroupID(), seed, in);
320             } catch (Exception ez) {
321                 return ID.nullID;
322             }
323         }
324         return hashID;
325     }
326
327     /**
328      * Returns the route destination Peer ID
329      *
330      * @return peerID of the destination of the route
331      */
332     public PeerID getDestPeerID() {
333         return dest.getPeerID();
334     }
335
336     /**
337      * Sets the route destination peer id.
338      *
339      * @param pid route destination peerID
340      */
341     public void setDestPeerID(PeerID pid) {
342         if ((null != pid) && (null != dest.getPeerID()) && (!pid.equals(dest.getPeerID()))) {
343             throw new IllegalStateException("Changing the peer id of the destination APA." + pid + " != " + dest.getPeerID());
344         }
345
346         dest.setPeerID(pid);
347
348         // recalculate hash.
349         synchronized (this) {
350             hashID = null;
351         }
352     }
353
354     /**
355      * Returns the destination access point. <b>This does <i>NOT</i> copy
356      * the AccessPointAdvertisement</b>.
357      *
358      * @return AccessPointAdvertisement of the destination peer.
359      * @deprecated Because this method unsafely exposes destination AccessPointAdvertisement it will be removed.
360      */
361     @Deprecated
362     public AccessPointAdvertisement getDest() {
363         return dest;
364     }
365
366     /**
367      * Sets the access point of the destination. <b>This does <i>NOT</i> copy
368      * the AccessPointAdvertisement</b>.
369      *
370      * @param ap AccessPointAdvertisement of the destination peer
371      */
372     public void setDest(AccessPointAdvertisement ap) {
373         PeerID destPid = dest.getPeerID();
374
375         this.dest = ap.clone();
376
377         if ((null != destPid) && (null != dest.getPeerID()) && (!destPid.equals(dest.getPeerID()))) {
378             throw new IllegalStateException("Changed the peer id of the destination APA." + destPid + " != " + dest.getPeerID());
379         }
380
381         if (null != destPid) {
382             dest.setPeerID(destPid);
383         }
384
385         // recalculate hash.
386         synchronized (this) {
387             hashID = null;
388         }
389     }
390
391     /**
392      * Add a new list of EndpointAddresses to the Route Destination access
393      * point
394      *
395      * @param addresses vector of endpoint addresses to add to the
396      *                  destination access point. Warning: The vector of endpoint addresses
397      *                  is specified as a vector of String. Each string representing
398      *                  one endpoint address.
399      * @deprecated Use {@link #addDestEndpointAddresses(List<EndpointAddress>)} instead.
400      */
401     @Deprecated
402     public void addDestEndpointAddresses(Vector<String> addresses) {
403         dest.addEndpointAddresses(addresses);
404     }
405
406     /**
407      * Clears all endpoint addresses associated with the destination peer.
408      */
409     public void clearDestEndpointAddresses() {
410         dest.clearEndpointAddresses();
411     }
412
413     /**
414      * Add the specified endpoint address to destination peer.
415      *
416      * @param addr EndpointAddress to add.
417      */
418     public void addDestEndpointAddress(EndpointAddress addr) {
419         dest.addEndpointAddress(addr);
420     }
421
422     /**
423      * Add all of the specified endpoint addresses to destination peer.
424      *
425      * @param addrs EndpointAddresses to add.
426      */
427     public void addDestEndpointAddresses(List<EndpointAddress> addrs) {
428         dest.addEndpointAddresses(addrs);
429     }
430
431     /**
432      * Remove the specified endpoint address to destination peer.
433      *
434      * @param addr EndpointAddress to add.
435      */
436     public void removeDestEndpointAddress(EndpointAddress addr) {
437         dest.removeEndpointAddress(addr);
438     }
439
440     /**
441      * Remove the specified endpoint addresses from destination peer.
442      *
443      * @param addrs EndpointAddress to add.
444      */
445     public void removeDestEndpointAddresses(Collection<EndpointAddress> addrs) {
446         dest.removeEndpointAddresses(addrs);
447     }
448
449     /**
450      * Remove a list of EndpointAddresses from the Route Destination
451      * access point
452      *
453      * @param addresses vector of endpoint addresses to remove from the
454      *                  destination access point.
455      * @deprecated Use {@link #removeDestEndpointAddresses(Collection)}.
456      */
457     @Deprecated
458     public void removeDestEndpointAddresses(Vector<String> addresses) {
459         dest.removeEndpointAddresses(addresses);
460     }
461
462     /**
463      * Returns the endpoint addresses of the destination peer in their
464      * preferred order.
465      *
466      * @return The {@code EndpointAddress}es of the destination peer.
467      */
468     public List<EndpointAddress> getDestEndpointAddresses() {
469         List<EndpointAddress> result = new ArrayList<EndpointAddress>();
470
471         Enumeration<String> eachEA = dest.getEndpointAddresses();
472
473         while (eachEA.hasMoreElements()) {
474             result.add(new EndpointAddress(eachEA.nextElement()));
475         }
476
477         return result;
478     }
479
480     /**
481      * Set the route destination endpoint addresses
482      *
483      * @param ea vector of endpoint addresses. Warning: The vector is not copied
484      *           and is used directly.
485      * @deprecated Use {@link #addDestEndpointAddress(EndpointAddress)} instead.
486      */
487     @Deprecated
488     public void setDestEndpointAddresses(Vector<String> ea) {
489         dest.setEndpointAddresses(ea);
490     }
491
492     /**
493      * returns the list of hops
494      *
495      * @return Enumeration list of hops as AccessPointAdvertisement
496      */
497     public Enumeration<AccessPointAdvertisement> getHops() {
498         return hops.elements();
499     }
500
501     /**
502      * returns the list of hops
503      *
504      * @return Vector list of hops as AccessPointAdvertisement
505      */
506     public Vector<AccessPointAdvertisement> getVectorHops() {
507         return hops;
508     }
509
510     /**
511      * Sets the list of hops associated with this route.
512      *
513      * @param newHops AccessPointAdvertisements which form the hops. The
514      *                Vector is <b>NOT</b> copied.
515      */
516     public void setHops(Vector<AccessPointAdvertisement> newHops) {
517         // It is legal to set it to null but it is automatically converted
518         // to an empty vector. The member hops is NEVER null.
519         if (null == newHops) {
520             hops = new Vector<AccessPointAdvertisement>();
521         } else {
522             for (AccessPointAdvertisement hop : newHops) {
523                 if (null == hop.getPeerID()) {
524                     throw new IllegalArgumentException("Bad hop");
525                 }
526             }
527
528             hops = newHops;
529         }
530     }
531
532     /**
533      * Check if the route contains the following hop
534      *
535      * @param pid peer id of the hop
536      * @return boolean true or false if the hop is found in the route
537      */
538     public boolean containsHop(PeerID pid) {
539         for (AccessPointAdvertisement hop : hops) {
540             PeerID hid = hop.getPeerID();
541
542             if (pid.equals(hid)) {
543                 return true;
544             }
545         }
546         return false;
547     }
548
549     /**
550      * Returns the AccessPointAdvertisement of first hop. <b>The
551      * AccessPointAdvertisement is <i>not</i> cloned.</b>
552      *
553      * @return AccessPointAdvertisement of first hop.
554      */
555     public AccessPointAdvertisement getFirstHop() {
556         return hops.isEmpty() ? null : hops.firstElement();
557     }
558
559     /**
560      * Sets the AccessPointAdvertisement for the first hop. <b>The
561      * AccessPointAdvertisement is <i>not</i> cloned.</b>
562      *
563      * @param ap AccessPointAdvertisement of the first hop.
564      */
565     public void setFirstHop(AccessPointAdvertisement ap) {
566         if (null == ap.getPeerID()) {
567             throw new IllegalArgumentException("Bad hop");
568         }
569
570         hops.add(0, ap);
571     }
572
573     /**
574      * Returns the access point for the last hop. <b>The
575      * AccessPointAdvertisement is <i>not</i> cloned.</b>
576      *
577      * @return AccessPointAdvertisement last hop.
578      */
579     public AccessPointAdvertisement getLastHop() {
580         return hops.isEmpty() ? null : hops.lastElement();
581     }
582
583     /**
584      * Sets the AccessPointAdvertisement of the last hop. <b>The
585      * AccessPointAdvertisement is <i>not</i> cloned.</b>
586      *
587      * @param ap AccessPointAdvertisement of the last hop.
588      */
589     public void setLastHop(AccessPointAdvertisement ap) {
590         if (null == ap.getPeerID()) {
591             throw new IllegalArgumentException("Bad hop");
592         }
593
594         hops.add(ap);
595     }
596
597     /**
598      * check if the route has a loop
599      *
600      * @return boolean true or false if the route has a loop
601      */
602     public boolean hasALoop() {
603         // Now check for any other potential loops.
604
605         Set<PeerID> seenPeers = new HashSet<PeerID>(hops.size());
606
607         for (AccessPointAdvertisement anAPA : hops) {
608             PeerID pid = anAPA.getPeerID();
609
610             if (seenPeers.contains(pid)) {
611                 return true; // There is a loop.
612             }
613
614             seenPeers.add(pid);
615         }
616         return false;
617     }
618
619     /**
620      * return the length of the route
621      *
622      * @return int size of the route
623      */
624     public int size() {
625         return hops.size();
626     }
627
628     /**
629      * Return the hop that follows the specified currentHop. <b>The
630      * AccessPointAdvertisement is <i>not</i> cloned.</b>
631      *
632      * @param currentHop PeerID of the current hop
633      * @return ap AccessPointAdvertisement of the next Hop
634      */
635     public AccessPointAdvertisement nextHop(PeerID currentHop) {
636
637         // check if we have a real route
638         if (hops.isEmpty()) {
639             // Empty vector.
640             return null;
641         }
642
643         // find the index of the route
644         int index = 0;
645         boolean found = false;
646
647         for (AccessPointAdvertisement ap : hops) {
648             if (currentHop.equals(ap.getPeerID())) {
649                 found = true;
650                 break;
651             }
652             index++;
653         }
654
655         AccessPointAdvertisement nextHop = null;
656
657         if (!found) {
658             // The peer is not into the list. Since we have got that message,
659             // the best we can do is to send it to the first gateway in the
660             // forward path.
661             nextHop = hops.get(0);
662         } else {
663             // Found the peer within the vector of hops. Get the next hop.
664             if (index < hops.size()) {
665                 nextHop = hops.get(index);
666             }
667         }
668
669         return nextHop;
670     }
671
672     /**
673      * Generate a string that displays the route
674      * information for logging or debugging purpose
675      *
676      * @return String return a string containing the route info
677      */
678     public String display() {
679         StringBuilder routeBuf = new StringBuilder();
680
681         routeBuf.append("Dest APA : ");
682         AccessPointAdvertisement dest = getDest();
683
684         routeBuf.append(dest.display());
685         routeBuf.append("\n");
686
687         int i = 1;
688         Enumeration<AccessPointAdvertisement> e = getHops();
689
690         while (e.hasMoreElements()) {
691             AccessPointAdvertisement hop = e.nextElement();
692
693             if (i == 1) {
694                 routeBuf.append("HOPS = ");
695             }
696             routeBuf.append("\n\t[").append(i++).append("] ");
697
698             routeBuf.append(hop.display());
699         }
700         return routeBuf.toString();
701     }
702
703     /**
704      * Remove a hop from the list of hops.
705      *
706      * @param pid peer id of the hop
707      * @return boolean true or false if the hop is found in the route
708      */
709     public boolean removeHop(PeerID pid) {
710         Iterator<AccessPointAdvertisement> eachHop = hops.iterator();
711
712         while (eachHop.hasNext()) {
713             AccessPointAdvertisement hop = eachHop.next();
714             PeerID hid = hop.getPeerID();
715
716             if (pid.equals(hid)) {
717                 eachHop.remove();
718                 return true;
719             }
720         }
721
722         return false;
723     }
724
725     /**
726      * Return a hop from the list of hops.
727      *
728      * @param pid peer id of the hop
729      * @return AccessPointAdvertisement of the corresponding hop
730      */
731     public AccessPointAdvertisement getHop(PeerID pid) {
732         for (AccessPointAdvertisement hop : hops) {
733             PeerID hid = hop.getPeerID();
734
735             if (pid.equals(hid)) {
736                 return hop.clone();
737             }
738         }
739         return null;
740     }
741
742     /**
743      * Alter the given newRoute (which does not start from here) by using firstLeg, a known route to whence
744      * it starts from. So that the complete route goes from here to the end-destination via firstLeg.
745      * public static boolean stichRoute(RouteAdvertisement newRoute,
746      *
747      * @param newRoute the new route
748      * @param firstLeg the first route
749      * @return true if successful
750      */
751     public static boolean stichRoute(RouteAdvertisement newRoute, RouteAdvertisement firstLeg) {
752         return stichRoute(newRoute, firstLeg, null);
753     }
754
755     /**
756      * Alter the given newRoute (which does not start from here) by using firstLeg, a known route to whence
757      * it starts from. So that the complete route goes from here to the end-destination via firstLeg
758      * also shortcut the route by removing the local peer.
759      *
760      * @param newRoute  the new route
761      * @param firstLeg  first hop
762      * @param localPeer local PeerID
763      * @return true if successful
764      */
765     public static boolean stichRoute(RouteAdvertisement newRoute, RouteAdvertisement firstLeg, PeerID localPeer) {
766
767         if (newRoute.hasALoop()) {
768             return false;
769         }
770
771         Vector<AccessPointAdvertisement> hops = newRoute.getVectorHops();
772
773         // Make room
774         hops.ensureCapacity(firstLeg.getVectorHops().size() + 1 + hops.size());
775
776         // prepend the routing peer unless the routing peer happens to be
777         // in the route already. That happens if the routing peer is the relay.
778         // or if the route does not have a first leg
779         PeerID routerPid = firstLeg.getDest().getPeerID();
780
781         if (newRoute.size() == 0 || (!newRoute.getFirstHop().getPeerID().equals(routerPid))) {
782             AccessPointAdvertisement ap = (AccessPointAdvertisement)
783                     AdvertisementFactory.newAdvertisement(AccessPointAdvertisement.getAdvertisementType());
784
785             // prepend the route with the routing peer.
786             ap.setPeerID(routerPid);
787             hops.add(0, ap);
788         }
789
790         // prepend the rest of the route
791         hops.addAll(0, firstLeg.getVectorHops());
792
793         // remove any loop from the root
794         cleanupLoop(newRoute, localPeer);
795         return true;
796     }
797
798     /**
799      * Remove loops from the route advertisement
800      * by shortcutting cycle from the route
801      *
802      * @param route     the route advertisement
803      * @param localPeer local PeerID
804      */
805     public static void cleanupLoop(RouteAdvertisement route, PeerID localPeer) {
806
807         // Note: we cleanup all enp addresses except for the last hop (which we
808         // use to shorten routes often enough).
809         // If we end-up removing the last hop, it means that it is the local
810         // peer and thus the route ends up with a size 0.
811
812         Vector<AccessPointAdvertisement> hops = route.getVectorHops();
813         Vector<AccessPointAdvertisement> newHops = new Vector<AccessPointAdvertisement>(hops.size());
814         AccessPointAdvertisement lastHop = null;
815
816         // Replace all by PID-only entries, but keep the last hop on the side.
817         if (!hops.isEmpty()) {
818             lastHop = hops.get(hops.size() - 1);
819         }
820         hops = (route.cloneOnlyPIDs()).getVectorHops();
821
822         // remove cycle from the route
823         for (int i = 0; i < hops.size(); i++) {
824             int loopAt = newHops.indexOf(hops.elementAt(i));
825
826             if (loopAt != -1) { // we found a cycle
827
828                 // remove all entries after loopAt
829                 for (int j = newHops.size(); --j > loopAt;) {
830                     newHops.remove(j);
831                 }
832             } else { // did not find it so we add it
833                 newHops.add(hops.get(i));
834             }
835         }
836
837         // Remove the local peer in the route if we were given one
838         if (localPeer != null) {
839             for (int i = newHops.size(); --i >= 0;) {
840                 if (localPeer.equals(newHops.elementAt(i).getPeerID())) {
841                     // remove all the entries up to that point we
842                     // need to keep the remaining of the route from that
843                     // point
844                     for (int j = 0; j <= i; j++) {
845                         newHops.remove(0);
846                     }
847                     break;
848                 }
849             }
850         }
851
852         if (lastHop != null && newHops.size() > 0) {
853             newHops.setElementAt(lastHop, newHops.size() - 1);
854         }
855
856         // update the new hops in the route
857         route.setHops(newHops);
858     }
859 }