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.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