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