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.Externalizable; 22 import java.io.IOException; 23 import java.io.ObjectInput; 24 import java.io.ObjectOutput; 25 import java.net.InetAddress; 26 import java.net.UnknownHostException; 27 import java.util.ArrayList; 28 import java.util.Collections; 29 import java.util.HashMap; 30 import java.util.Iterator; 31 import java.util.Map; 32 import java.util.NoSuchElementException; 33 34 import jgroup.core.MemberId; 35 import jgroup.util.Abort; 36 import jgroup.util.Network; 37 38 /** 39 * Collection class for storing information about a set of hosts. 40 * 41 * @author Hein Meling 42 * @since Jgroup 1.2 43 */ 44 public final class HostSet 45 implements Cloneable, Externalizable, Iterable<Host> 46 { 47 48 //////////////////////////////////////////////////////////////////////////////////////////// 49 // Constants 50 //////////////////////////////////////////////////////////////////////////////////////////// 51 52 private static final long serialVersionUID = 2517425024237000760L; 53 54 /** 55 * Default initial capacity. If we manage to stay below this value 56 * we avoid to rehash the set. @See java.util.HashMap for details. 57 */ 58 private static final int HOST_HASH_SIZE = 50; 59 60 /** 61 * Default load factor. 62 */ 63 private static final float LOAD_FACTOR = (float) 0.75; 64 65 66 //////////////////////////////////////////////////////////////////////////////////////////// 67 // Fields 68 //////////////////////////////////////////////////////////////////////////////////////////// 69 70 /** 71 * The unsyncronized map of <code>Host</code> objects. 72 * This is needed for cloning purposes. 73 */ 74 private Map<InetAddress, Host> unsyncHosts; 75 76 /** Synchronized map of <code>Host</code> objects (InetAddress -> Host object)*/ 77 private Map<InetAddress, Host> hosts; 78 79 /** 80 * Dynamic array of host listeners. Note that this field cannot be 81 * declared as just the List interface due to this class implementing 82 * the Cloneable interface. 83 */ 84 private ArrayList<HostListener> hostListeners = new ArrayList<HostListener>(); 85 86 87 //////////////////////////////////////////////////////////////////////////////////////////// 88 // Constructors 89 //////////////////////////////////////////////////////////////////////////////////////////// 90 91 /** 92 * Constructs an empty <code>HostSet</code> with default capacity and 93 * load factor. Also the default constructor for externalization. 94 */ 95 public HostSet() 96 { 97 this(HOST_HASH_SIZE, LOAD_FACTOR); 98 } 99 100 101 /** 102 * Constructs an empty <code>HostSet</code> with default load factor, 103 * with space for <code>maxEntries</code> elements. 104 */ 105 public HostSet(int maxEntries) 106 { 107 this(maxEntries, LOAD_FACTOR); 108 } 109 110 111 /** 112 * Constructs an empty <code>HostSet</code> with space for 113 * <code>maxEntries</code> elements, using the specified load factor. 114 */ 115 public HostSet(int maxEntries, float loadFactor) 116 { 117 int initialCapacity = Math.round(maxEntries / loadFactor) + 1; 118 unsyncHosts = new HashMap<InetAddress, Host>(initialCapacity, loadFactor); 119 hosts = Collections.synchronizedMap(unsyncHosts); 120 } 121 122 123 //////////////////////////////////////////////////////////////////////////////////////////// 124 // Listener interface 125 //////////////////////////////////////////////////////////////////////////////////////////// 126 127 public void addHostListener(HostListener listener) 128 { 129 hostListeners.add(listener); 130 } 131 132 133 //////////////////////////////////////////////////////////////////////////////////////////// 134 // HostSet interface methods 135 //////////////////////////////////////////////////////////////////////////////////////////// 136 137 /** 138 * Returns the <code>Host</code> object associated with the specified 139 * <code>InetAddress</code>. 140 * 141 * @param inetAddress 142 * The <code>InetAddress</code> of the <code>Host</code> object to get. 143 * @return 144 * The <code>Host</code> object associated with the given 145 * <code>InetAddress</code>. <code>null</code> is returned if the given 146 * IP address host is not in this host set. 147 */ 148 public Host getHost(InetAddress inetAddress) 149 { 150 return (Host) hosts.get(inetAddress); 151 } 152 153 154 /** 155 * Returns the <code>Host</code> object associated with the specified 156 * <code>MemberId</code>. 157 * 158 * @param member 159 * The <code>MemberId</code> of the <code>Host</code> object to get. 160 * @return 161 * The <code>Host</code> object associated with the given 162 * <code>MemberId</code>. <code>null</code> is returned if the given 163 * member host is not in this host set. 164 */ 165 public Host getHost(MemberId member) 166 { 167 return (Host) hosts.get(member.getEndPoint().getAddress()); 168 } 169 170 171 /** 172 * Add a new host to this <code>HostSet</code>. 173 * 174 * @param hostName 175 * The host name. 176 * @param domain 177 * The <code>Domain</code> associated with this host. 178 * @param port 179 * The port number for this host. 180 * @return 181 * True if the <code>HostSet</code> did not already contain this host. 182 * @throws UnknownHostException 183 * Is raised if the host was not recognized. 184 */ 185 public boolean addHost(String hostName, Domain domain, int port) 186 throws UnknownHostException 187 { 188 return addHost(new Host(hostName, domain, port)); 189 } 190 191 192 /** 193 * Add a new host to this <code>HostSet</code>. 194 * 195 * @param host 196 * A host object. 197 * @return 198 * True if the <code>HostSet</code> did not already contain this host. 199 */ 200 public boolean addHost(Host host) 201 { 202 Host oldHost = (Host) hosts.put(host.getAddress(), host); 203 boolean added = !host.equals(oldHost); 204 if (added) 205 for (int i = 0, size = hostListeners.size(); i < size; i++) 206 ((HostListener) hostListeners.get(i)).addHost(host); 207 return added; 208 } 209 210 211 /** 212 * Add the given host to the this host set. The given host 213 * is represented as an integer, and must already exist as a 214 * <code>Host</code> object in the <code>DistributedSystemConfig</code> 215 * object. 216 * 217 * @param hostIntAdr 218 * An <code>int</code> representation of the host to add to this 219 * host set. 220 * @return 221 * True if the <code>HostSet</code> did not already contain this host. 222 * @throws UnknownHostException 223 * If the <code>int</code> representation of the host does not 224 * correspond to a valid host address, or if it could not 225 * be found. 226 */ 227 public boolean addHost(int hostIntAdr) 228 throws UnknownHostException 229 { 230 InetAddress inetAdr = Network.translate(hostIntAdr); 231 Host host = DistributedSystemConfig.getHost(inetAdr); 232 return addHost(host); 233 } 234 235 236 /** 237 * Add all hosts in the given <code>HostSet</code> to this 238 * <code>HostSet</code>. 239 * 240 * @param hostSet 241 * The set of hosts to add to this host set. 242 */ 243 public void addHosts(HostSet hostSet) 244 { 245 for (Iterator iter = hostSet.iterator(); iter.hasNext();) { 246 Host host = (Host) iter.next(); 247 addHost(host); 248 } 249 } 250 251 252 /** 253 * Remove a host (member) from this <code>HostSet</code>. 254 * 255 * @param member 256 * A member object representing a host object. 257 * @return 258 * True if the <code>Host</code> was removed from the 259 * <code>HostSet</code>, that is if it was present in the 260 * <code>HostSet</code>; false otherwise. 261 */ 262 public boolean removeHost(MemberId member) 263 { 264 Host h = getHost(member); 265 if (h != null) 266 return removeHost(h); 267 else 268 return false; 269 } 270 271 272 /** 273 * Remove a host from this <code>HostSet</code>. 274 * 275 * @param host 276 * A host object. 277 * @return 278 * True if the <code>Host</code> was removed from the 279 * <code>HostSet</code>, that is if it was present in the 280 * <code>HostSet</code>; false otherwise. 281 */ 282 public boolean removeHost(Host host) 283 { 284 Host oldHost = (Host) hosts.remove(host.getAddress()); 285 boolean removed = (oldHost != null); 286 if (removed) 287 for (int i = 0, size = hostListeners.size(); i < size; i++) 288 ((HostListener) hostListeners.get(i)).removeHost(host); 289 return removed; 290 } 291 292 293 /** 294 * Remove all hosts in the given <code>HostSet</code> from this 295 * <code>HostSet</code>. 296 * 297 * @param hostSet 298 * The set of hosts to remove from this host set. 299 */ 300 public void removeHosts(HostSet hostSet) 301 { 302 if (hostSet == this) { 303 removeAllHosts(); 304 return; 305 } 306 for (Iterator iter = hostSet.iterator(); iter.hasNext();) { 307 Host host = (Host) iter.next(); 308 removeHost(host); 309 } 310 } 311 312 313 /** 314 * Remove all hosts in this <code>HostSet</code>. 315 */ 316 public void removeAllHosts() 317 { 318 hosts.clear(); 319 hostListeners.clear(); 320 } 321 322 323 /** 324 * Check if the given host set is fully contained in 325 * this <code>HostSet</code>. 326 * 327 * @param hostSet 328 * A host set. 329 * @return 330 * True if the given <code>HostSet</code> is contained in 331 * this host set. 332 */ 333 public boolean containsAll(HostSet hostSet) 334 { 335 for (Iterator iter = hostSet.iterator(); iter.hasNext();) { 336 Host host = (Host) iter.next(); 337 if (!hosts.containsKey(host.getAddress())) { 338 return false; 339 } 340 } 341 return true; 342 } 343 344 345 /** 346 * Check if the host is in this <code>HostSet</code>. 347 * 348 * @param host 349 * A host object. 350 * @return 351 * True if the <code>HostSet</code> contained the specified host. 352 */ 353 public boolean containsHost(Host host) 354 { 355 return hosts.containsKey(host.getAddress()); 356 } 357 358 359 /** 360 * Check if the host represented by the given InetAddress is in this 361 * <code>HostSet</code>. 362 * 363 * @param inetAddress 364 * The <code>InetAddress</code> representation of the host to 365 * test for presence in the <code>HostSet</code>. 366 * @return 367 * True if the <code>HostSet</code> contained the specified host. 368 */ 369 public boolean containsHost(InetAddress inetAddress) 370 { 371 return hosts.containsKey(inetAddress); 372 } 373 374 375 /** 376 * Returns the number of hosts stored in this <code>HostSet</code>. 377 */ 378 public int size() 379 { 380 return hosts.size(); 381 } 382 383 384 /** 385 * Returns true if this <code>HostSet</code> is empty. 386 */ 387 public boolean isEmpty() 388 { 389 return hosts.isEmpty(); 390 } 391 392 393 /** 394 * Returns an iterator over the values of the <code>HostSet</code>. 395 * The values returned by the <code>next</code> method call of the 396 * iterator should be cast to <code>Host</code>. 397 * 398 * @return 399 * An <code>Iterator</code> for the <code>HostSet</code>. 400 */ 401 public Iterator<Host> iterator() 402 { 403 return hosts.values().iterator(); 404 } 405 406 407 /** 408 * Returns the set of hosts that are considered available; i.e. running 409 * an <code>ExecService</code> instance. 410 */ 411 public HostSet getAvailHosts() 412 { 413 //FIXME since this method may block due to machines being unavail; 414 // it should be wrapped in a thread with a timeout mechanism. TimerTask stuff. 415 HostSet availableHost = (HostSet) this.clone(); 416 for (Iterator iter = availableHost.iterator(); iter.hasNext();) { 417 Host host = (Host) iter.next(); 418 if (!host.isAvailable()) { 419 iter.remove(); 420 } 421 } 422 return availableHost; 423 } 424 425 426 public Host[] toArray() 427 { 428 return (Host[]) hosts.values().toArray(new Host[hosts.size()]); 429 } 430 431 432 /** 433 * Returns an <code>int[]</code> array representing the content of 434 * this <code>HostSet</code>. 435 */ 436 public int[] toIntArray() 437 { 438 int i = 0; 439 int[] inetAdr = new int[hosts.size()]; 440 synchronized (hosts) { 441 for (Iterator assignedIter = hosts.values().iterator(); assignedIter.hasNext();) { 442 Host host = (Host) assignedIter.next(); 443 inetAdr[i++] = Network.translate(host.getAddress()); 444 } 445 } 446 return inetAdr; 447 } 448 449 450 /** 451 * Returns the first <code>Host</code> returned by the iterators next 452 * method. There is no guarantees that this method returns the same 453 * host for multiple invocations, nor does it promise to return a 454 * different host on the next invocation. This method is useful to 455 * obtain just one host in the <code>HostSet</code>, when it does not 456 * matter which host to get. 457 * 458 * @return 459 * The <code>Host</code> object found. 460 * @throws NoSuchElementException 461 * Raised if there are no hosts in this <code>HostSet</code>. 462 */ 463 public Host getFirst() 464 { 465 Iterator<Host> iter = iterator(); 466 if (!iter.hasNext()) 467 throw new NoSuchElementException("No hosts in the HostSet"); 468 return iter.next(); 469 } 470 471 472 /** 473 * Removes the first <code>Host</code> returned by the iterators next 474 * method. There is no guarantees that this method returns the same 475 * host for multiple invocations. This method is useful to 476 * obtain just one host in the <code>HostSet</code>, when it does not 477 * matter which host to get. 478 * 479 * @return 480 * The <code>Host</code> object found. 481 * @throws NoSuchElementException 482 * Raised if there are no hosts in this <code>HostSet</code>. 483 */ 484 public synchronized Host removeFirst() 485 { 486 Iterator<Host> iter = iterator(); 487 if (!iter.hasNext()) 488 throw new NoSuchElementException("No hosts in the HostSet"); 489 Host host = iter.next(); 490 iter.remove(); 491 return host; 492 } 493 494 495 //////////////////////////////////////////////////////////////////////////////////////////// 496 // Cloneable interface 497 //////////////////////////////////////////////////////////////////////////////////////////// 498 499 /** 500 * Returns a shallow copy of this <code>HostSet</code> instance. 501 * (The elements themselves are not copied.) 502 * 503 * @return 504 * a clone of this <code>HostSet</code> instance. 505 */ 506 @SuppressWarnings("unchecked") 507 public Object clone() 508 { 509 try { 510 HostSet newHostSet = (HostSet) super.clone(); 511 newHostSet.unsyncHosts = (HashMap) ((HashMap) unsyncHosts).clone(); 512 newHostSet.hosts = Collections.synchronizedMap(newHostSet.unsyncHosts); 513 newHostSet.hostListeners = (ArrayList) hostListeners.clone(); 514 return newHostSet; 515 } catch(CloneNotSupportedException e) { 516 /* Cannot happen since HashMap supports clone. */ 517 throw new Abort("Internal error", e); 518 } 519 } 520 521 522 //////////////////////////////////////////////////////////////////////////////////////////// 523 // Override methods in Object 524 //////////////////////////////////////////////////////////////////////////////////////////// 525 526 public boolean equals(Object o) 527 { 528 if (o != null && o instanceof HostSet) { 529 HostSet hostSet = (HostSet) o; 530 boolean eq = (size() == hostSet.size()); 531 // iterator over this host set and compare with the provided host set 532 for (Iterator iter = iterator(); eq && iter.hasNext();) { 533 Host host = (Host) iter.next(); 534 eq = hostSet.containsHost(host); 535 } 536 return eq; 537 } else { 538 return false; 539 } 540 } 541 542 543 /** 544 * Return a hash code for this host set. 545 */ 546 public int hashCode() 547 { 548 return hosts.hashCode(); 549 } 550 551 552 /** 553 * Returns a string representation of this object 554 */ 555 public String toString() 556 { 557 StringBuilder buf = new StringBuilder(); 558 buf.append("[HostSet: "); 559 boolean first = true; 560 synchronized (hosts) { 561 for (Iterator i = iterator(); i.hasNext(); ) { 562 if (first) 563 first = false; 564 else 565 buf.append(", "); 566 buf.append(((Host)i.next()).toString()); 567 } 568 } 569 buf.append("]"); 570 return buf.toString(); 571 } 572 573 574 //////////////////////////////////////////////////////////////////////////////////////////// 575 // Marshaling/unmarshaling methods 576 //////////////////////////////////////////////////////////////////////////////////////////// 577 578 /* (non-Javadoc) 579 * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput) 580 */ 581 public void writeExternal(ObjectOutput out) 582 throws IOException 583 { 584 // int[] hosts = toIntArray(); 585 // out.writeInt(hosts.length); 586 // for (int i = 0; i < hosts.length; i++) { 587 // out.writeInt(hosts[i]); 588 // } 589 590 out.writeInt(size()); 591 for (Iterator iter = this.hosts.values().iterator(); iter.hasNext();) { 592 Host host = (Host) iter.next(); 593 out.writeInt(host.getIntAddress()); 594 out.writeObject(host.getContentMap()); 595 } 596 } 597 598 599 /* (non-Javadoc) 600 * @see java.io.Externalizable#readExternal(java.io.ObjectInput) 601 */ 602 public void readExternal(ObjectInput in) 603 throws IOException, ClassNotFoundException 604 { 605 int len = in.readInt(); 606 for (int k = 0; k < len; k++) { 607 int hostadr = in.readInt(); 608 try { 609 addHost(hostadr); 610 } catch (UnknownHostException e) { 611 e.printStackTrace(); 612 } 613 Host host = getHost(Network.translate(hostadr)); 614 host.setContentMap((Map) in.readObject()); 615 } 616 } 617 618 } // END HostSet