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