View Javadoc

1   /*
2    * Copyright (c) 1998-2002 The Jgroup Team.
3    *
4    * This program is free software; you can redistribute it and/or modify
5    * it under the terms of the GNU Lesser General Public License version 2 as
6    * published by the Free Software Foundation.
7    *
8    * This program is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   * GNU Lesser General Public License for more details.
12   *
13   * You should have received a copy of the GNU Lesser General Public License
14   * along with this program; if not, write to the Free Software
15   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16   *
17   */
18  
19  package jgroup.relacs.config;
20  
21  import java.io.Serializable;
22  import java.net.InetAddress;
23  import java.net.UnknownHostException;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.Comparator;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.NoSuchElementException;
30  
31  import jgroup.core.MemberId;
32  
33  /**
34   *  Collection class for storing information about a set of domains.
35   *
36   *  @author Hein Meling
37   *  @since Jgroup 1.2
38   */
39  public final class DomainSet
40    implements Iterable<Domain>, Serializable
41  {
42  
43    ////////////////////////////////////////////////////////////////////////////////////////////
44    // Static comparator for domain sets
45    ////////////////////////////////////////////////////////////////////////////////////////////
46  
47    /**
48     * Comparator to be provided to domain sets in order to order domains correctly,
49     * ignoring the number of replicas in each domain.  That is override the default
50     * ordering for domain sets which is based on the number of replicas in each domain.
51     */
52    public static final Comparator<Domain> domainComparator = new Comparator<Domain>() {
53      public int compare(Domain o1, Domain o2) {
54        int o1Adr = o1.getIntAddress();
55        int o2Adr = o2.getIntAddress();
56        if (o1Adr < o2Adr)
57          return -1;
58        else if (o1Adr == o2Adr)
59          return 0;
60        else
61          return 1;
62      }
63    };
64  
65  
66    ////////////////////////////////////////////////////////////////////////////////////////////
67    // Fields
68    ////////////////////////////////////////////////////////////////////////////////////////////
69  
70    /**
71     * List of <code>Domain</code> objects; the list will be sorted before each access
72     * to its iterator.  This sorting is required since the domain objects may change
73     * their individual position in the list with time (as more replicas and/or hosts
74     * are added to them).  Hence, we cannot use a sorted set, because that will only
75     * give correct order at insertion time.
76     */
77    private final List<Domain> domains;
78  
79    /** Dynamic array of domain listeners */
80    private final List<DomainListener> domainListeners = new ArrayList<DomainListener>();
81  
82    /** Comparator for the domain set iteration ordering (may be undefined) */
83    private Comparator<Domain> comparator;
84  
85  
86    ////////////////////////////////////////////////////////////////////////////////////////////
87    // Constructors
88    ////////////////////////////////////////////////////////////////////////////////////////////
89  
90    /**
91     *  Constructs an empty <code>DomainSet</code>.
92     */
93    public DomainSet()
94    {
95      this(null, null);
96    }
97  
98    /**
99     * Constructs an empty <code>DomainSet</code> whose iteration order is
100    * defined by the given <code>comparator</code>.
101    * 
102    * @param comparator
103    */
104   public DomainSet(Comparator<Domain> comparator)
105   {
106     this(null, comparator);
107   }
108 
109   /**
110    * Constructs a new <code>DomainSet</code> containg the given domain set, 
111    * whose iteration order is defined by the given <code>comparator</code>.
112    * 
113    * @param domains
114    * @param comparator
115    */
116   public DomainSet(DomainSet domains, Comparator<Domain> comparator)
117   {
118     if (domains == null) {
119       this.domains = new ArrayList<Domain>();
120     } else {
121       this.domains = new ArrayList<Domain>(domains.domains);
122     }
123     this.comparator = comparator;
124   }
125 
126 
127   ////////////////////////////////////////////////////////////////////////////////////////////
128   // Listener interface
129   ////////////////////////////////////////////////////////////////////////////////////////////
130 
131   public void addListener(DomainListener listener)
132   {
133     domainListeners.add(listener);
134   }
135 
136 
137   ////////////////////////////////////////////////////////////////////////////////////////////
138   // DomainSet interface methods
139   ////////////////////////////////////////////////////////////////////////////////////////////
140 
141   /**
142    *  Add a new domain to this <code>DomainSet</code>.
143    *
144    *  @param domainName
145    *    The domain name.
146    *  @param mcastAdr
147    *    The multicast address for this domain.
148    *  @param port
149    *    The multicast port for this domain.
150    *  @param jdaemons
151    *    The number of Jgroup daemons that should exist in the new domain.
152    *  @return 
153    *    True if the <code>DomainSet</code> did not already contain this domain.
154    *  @throws UnknownHostException
155    *    Is raised if the multicast address is not recognized.
156    */
157   public boolean addDomain(String domainName, String mcastAdr, int port, int jdaemons)
158     throws UnknownHostException
159   {
160     return addDomain(new Domain(domainName, mcastAdr, port, jdaemons));
161   }
162 
163 
164   /**
165    *  Add a new domain to this <code>DomainSet</code>.
166    *
167    *  @param domain
168    *    A domain object.
169    *  @return 
170    *    True if the <code>DomainSet</code> did not already contain this domain.
171    */
172   public boolean addDomain(Domain domain)
173   {
174     boolean added = !domains.contains(domain);
175     if (added) {
176       domains.add(domain);
177       for (int i = 0, size = domainListeners.size(); i < size; i++) {
178         ((DomainListener) domainListeners.get(i)).addDomain(domain);
179       }
180     }
181     return added;
182   }
183 
184 
185   /**
186    *  Remove the specified domain from this <code>DomainSet</code>.
187    *
188    *  @param domain
189    *    A domain object.
190    *  @return 
191    *    True if the specified domain was removed from this
192    *    <code>DomainSet</code>; false otherwise.
193    */
194   public boolean removeDomain(Domain domain)
195   {
196     boolean removed = domains.remove(domain);
197     if (removed) {
198       for (int i = 0, size = domainListeners.size(); i < size; i++) {
199         ((DomainListener) domainListeners.get(i)).removeDomain(domain);
200       }
201     }
202     return removed;
203   }
204 
205 
206   /**
207    *  Check if the specified domain is in this <code>DomainSet</code>.
208    *
209    *  @param domain
210    *    A domain object.
211    *  @return 
212    *    True if the specified domain is in this <code>DomainSet</code>;
213    *    false otherwise.
214    */
215   public boolean containsDomain(Domain domain)
216   {
217     return domains.contains(domain);
218   }
219 
220 
221   /**
222    * Returns true if this domain set contains the given <code>InetAddress</code>.
223    * 
224    * @param inetAdr
225    * @return
226    */
227   public boolean contains(InetAddress inetAdr)
228   {
229     for (Domain domain : domains) {
230       for (Host host : domain.getHostSet()) {
231         if (host.getAddress().equals(inetAdr))
232           return true;
233       }
234     }
235     return false;
236   }
237 
238 
239   /**
240    * Returns the <code>Domain</code> object stored in this domain set
241    * corresponding to the provided domain.  If no domain matching the provided
242    * domain is available in this domain set, <code>null</code> is returned.
243    */
244   public Domain getDomain(Domain theDomain)
245   {
246     for (Domain domain : domains) {
247       if (domain.equals(theDomain)) {
248         return domain;
249       }
250     }
251     return null;
252   }
253 
254 
255   /**
256    * Returns the <code>Domain</code> object stored in this domain set
257    * corresponding to the provided multicast <code>InetAddress</code>.
258    * If no domain matching the provided domain is available in this
259    * domain set, <code>null</code> is returned.
260    * 
261    * @throws IllegalArgumentException if the provided <code>InetAddress</code>
262    *   is not a multicast address.
263    */
264   public Domain getDomain(InetAddress mcastAdr)
265   {
266     if (!mcastAdr.isMulticastAddress())
267       throw new IllegalArgumentException(mcastAdr + " is not a multicast address");
268     for (Domain domain : domains) {
269       if (domain.getAddress().equals(mcastAdr)) {
270         return domain;
271       }
272     }
273     return null;
274   }
275 
276   /**
277    * Returns the <code>Domain</code> object stored in this domain set
278    * corresponding to the provided host <code>InetAddress</code>.
279    * If no domain contains the provided host address in this domain set,
280    * <code>null</code> is returned.
281    * 
282    * @param hostAdr the host address whose domain object to return.
283    * @return the domain in which the given host resides.
284    * @throws IllegalArgumentException if the provided <code>InetAddress</code>
285    *   is a multicast address instead of a regular host address.
286    */
287   public Domain getHostDomain(InetAddress hostAdr)
288   {
289     if (hostAdr.isMulticastAddress())
290       throw new IllegalArgumentException(hostAdr + " is a multicast address");
291     for (Domain domain : domains) {
292       if (domain.getHostSet().containsHost(hostAdr)) {
293         return domain;
294       }
295     }
296     return null;
297   }
298 
299   /**
300    * @param comparator The comparator to set.
301    */
302   public void setComparator(Comparator<Domain> comparator)
303   {
304     this.comparator = comparator;
305   }
306 
307   /**
308    *  Returns the number of domains stored in this <code>DomainSet</code>.
309    */
310   public int size()
311   {
312     return domains.size();
313   }
314 
315 
316   /**
317    *  Returns true if this <code>DomainSet</code> is empty.
318    */
319   public boolean isEmpty()
320   {
321     return domains.isEmpty();
322   }
323 
324 
325   /**
326    *  Returns an iterator over the values of the <code>DomainSet</code>.
327    *  The values returned by the <code>next</code> method call of the
328    *  iterator should be cast to <code>Domain</code>.
329    *  
330    *  This iterator returns the sorted order of the domains in this set.
331    *
332    *  @return 
333    *    An <code>Iterator</code> for the <code>DomainSet</code>.
334    */
335   @SuppressWarnings("unchecked")
336   public Iterator<Domain> iterator()
337   {
338     /*
339      * Note that we need to sort here because the domain objects in this set
340      * may change their individual positions with time, depending on the number
341      * of replicas and number of hosts associated with the domain.  Hence, we
342      * cannot use a sorted set, because that will only give correct order at
343      * insertion time.
344      */
345     Collections.sort(domains, comparator);
346     return domains.iterator();
347   }
348 
349 
350   /**
351    * Returns an array of domain objects, in sorted order.
352    * 
353    * @return
354    */
355   @SuppressWarnings("unchecked")
356   public Domain[] toArray()
357   {
358     Collections.sort(domains, comparator);
359     return (Domain[]) domains.toArray(new Domain[domains.size()]);
360   }
361 
362 
363   /**
364    *  Returns the first <code>Domain</code> in the domain set; the domain
365    *  returned is the one with the lowest replica count.
366    *
367    *  @return 
368    *    The <code>Domain</code> object found.
369    *  @throws NoSuchElementException
370    *    Raised if there are no domains in this <code>DomainSet</code>.
371    */
372   @SuppressWarnings("unchecked")
373   public Domain first()
374   {
375     Collections.sort(domains, comparator);
376     return domains.get(0);
377   }
378 
379 
380   /**
381    *  Returns the domain that contains the largest number of hosts.
382    */
383   public Domain getLargestDomain()
384   {
385     if (isEmpty()) 
386       throw new NoSuchElementException("No domains have been added to the domain set.");
387     Domain largestDomain = null;
388     for (Domain domain : domains) {
389       if (largestDomain == null || largestDomain.size() < domain.size()) {
390         largestDomain = domain; 
391       }
392     }
393     return largestDomain;
394   }
395 
396 
397   /**
398    *  Given a set of members, return the <code>Domain</code>
399    *  that contains the most members.  It is not specified which
400    *  domain is returned when more than one domain contains an
401    *  equal number of members.
402    * 
403    *  @param members The members from which to find the domain.
404    *  @return The domain with the largest number of members amongst
405    *     the provided set.
406    */
407   public static Domain getLargestReplicaDomain(MemberId[] members)
408   {
409     DomainSet domains = new DomainSet();
410     for (int i = 0; i < members.length; i++) {
411       Host host = DistributedSystemConfig.getHost(members[i]);
412       /*
413        * Obtain the domain of the host object, and check if this
414        * domain already exists within the domain set.
415        */
416       Domain hostDomain = host.getDomain();
417       Domain domain = domains.getDomain(hostDomain);
418       if (domain == null) {
419         /*
420          * Construct a new Domain without any hosts associated with it,
421          * and add it to the domain set, since it was not previously
422          * stored in the domain set.
423          */
424         domain = new Domain(hostDomain, false);
425         domains.addDomain(domain);
426       }
427       domain.addHost(host);
428     }
429     return domains.getLargestDomain();
430   }
431 
432 
433   ////////////////////////////////////////////////////////////////////////////////////////////
434   // Override methods in Object
435   ////////////////////////////////////////////////////////////////////////////////////////////
436 
437   public int hashCode()
438   {
439     return domains.hashCode();
440   }
441 
442 
443   /**
444    *  Returns a string representation of this object
445    */
446   public String toString()
447   {
448     return toString(false);
449   }
450 
451   /**
452    *  Returns a string representation of this object
453    */
454   public String toString(boolean full)
455   {
456     StringBuilder buf = new StringBuilder();
457     buf.append("[DomainSet: ");
458     for (Domain domain : domains) {
459       /* One domain per line */
460 //      buf.append("\n ");
461       buf.append(domain.toString(full));
462     }
463     buf.append("]");
464     return buf.toString();
465   }
466 
467 } // END DomainSet