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