View Javadoc

1   /*
2    * Copyright (c) 1998-2005 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  package jgroup.util.log;
19  
20  import static jgroup.util.log.ConnectionPatternEvent.ConnectionPatternType.ConnectionPattern;
21  
22  import java.io.IOException;
23  import java.io.ObjectInputStream;
24  import java.io.Serializable;
25  import java.net.InetAddress;
26  import java.util.ArrayList;
27  import java.util.Collections;
28  import java.util.HashMap;
29  import java.util.HashSet;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.NoSuchElementException;
34  import java.util.Set;
35  
36  import jgroup.core.ConfigManager;
37  import jgroup.relacs.config.Domain;
38  import jgroup.relacs.config.DomainSet;
39  
40  /**
41   * A connection pattern event to be logged.
42   * 
43   * @author Hein Meling
44   */
45  public class ConnectionPatternEvent
46    extends Event
47    implements Iterable<Domain>
48  {
49  
50    ////////////////////////////////////////////////////////////////////////////////////////////
51    // Constants
52    ////////////////////////////////////////////////////////////////////////////////////////////
53  
54    private static final long serialVersionUID = 227099539664502309L;
55  
56    /**
57     * Supported connection event types.
58     */
59    public enum Type implements EventType { Partition, Merge }
60  
61    public enum ConnectionPatternType implements EventType { ConnectionPattern }
62  
63  
64    ////////////////////////////////////////////////////////////////////////////////////////////
65    // Fields
66    ////////////////////////////////////////////////////////////////////////////////////////////
67  
68    /** The list of connection events comprising this event pattern */
69    private List<ConnectionEvent> events = new ArrayList<ConnectionEvent>(3);
70  
71    /** The connection pattern string */
72    private String pattern;
73  
74    /** Set of partitions computed from the events list */
75    private transient Set<DomainSet> partitions;
76  
77    /** Mapping between InetAddress and domain set (used to lookup the partition of a host) */
78    private transient Map<InetAddress, DomainSet> hostPartitions;
79  
80  
81    ////////////////////////////////////////////////////////////////////////////////////////////
82    // Constructor
83    ////////////////////////////////////////////////////////////////////////////////////////////
84  
85    public ConnectionPatternEvent()
86    {
87      super();
88    }
89  
90    public ConnectionPatternEvent(String description, String pattern, List<ConnectionEvent> events)
91    {
92      super(ConnectionPattern, description);
93      this.pattern = pattern;
94      this.events = events;
95    }
96  
97    public void addEvent(Type type, Domain fromDomain, Domain toDomain)
98    {
99      events.add(new ConnectionEvent(type, fromDomain, toDomain));
100   }
101 
102 
103   ////////////////////////////////////////////////////////////////////////////////////////////
104   // Inner class for connection event
105   ////////////////////////////////////////////////////////////////////////////////////////////
106 
107   /**
108    * Connection event describing the connection between to domains.
109    * 
110    * @author Hein Meling
111    */
112   private class ConnectionEvent
113     implements Serializable, Comparable<ConnectionEvent>
114   {
115 
116     private static final long serialVersionUID = -5851464130156734196L;
117 
118     /**
119      * The connection type for this event
120      */
121     private final Type type;
122 
123     /**
124      * The two domains involved in this connection event.
125      */
126     private Domain fromDomain, toDomain;
127 
128     /**
129      * The connection pattern for this ConnectionEvent
130      */
131     private String connectPattern;
132 
133     public ConnectionEvent(Type type, Domain fromDomain, Domain toDomain)
134     {
135       this.type = type;
136       this.fromDomain = fromDomain;
137       this.toDomain = toDomain;
138       switch (type) {
139         case Partition:
140           this.connectPattern = fromDomain.getName() + " | " + toDomain.getName();
141           break;
142 
143         case Merge:
144           this.connectPattern = fromDomain.getName() + " - " + toDomain.getName();
145           break;
146       }
147     }
148 
149     public String toString()
150     {
151       return connectPattern;
152     }
153 
154     /**
155      * Comparing two events for the purpose of sorting so that merge events are
156      * first in the list.
157      * 
158      * @param o
159      * @return
160      */
161     public int compareTo(ConnectionEvent o)
162     {
163       if (o.type == type) {
164         return 0;
165       }
166       if (o.type == Type.Partition && type == Type.Merge) {
167         return -1;
168       } else {
169         return 1;
170       }
171     }
172 
173     ////////////////////////////////////////////////////////////////////////////////////////////
174     // Serialization Methods
175     ////////////////////////////////////////////////////////////////////////////////////////////
176 
177     /**
178      * Override readObject to update the domain objects to contain all hosts;
179      * this is required since the Domain class is not serialize; only the EndPointImpl
180      * class is serialized, which contains the InetAddress.
181      */
182     private void readObject(ObjectInputStream in)
183       throws IOException, ClassNotFoundException
184     {
185       in.defaultReadObject();
186       DomainSet domains = ConfigManager.getDistributedSystem().getDomainSet();
187       // Replace the unmarshaled domains with real domains 
188       fromDomain = domains.getDomain(fromDomain);
189       toDomain = domains.getDomain(toDomain);
190     }
191 
192   } // END ConnectionEvent
193 
194 
195   ////////////////////////////////////////////////////////////////////////////////////////////
196   // Access Methods
197   ////////////////////////////////////////////////////////////////////////////////////////////
198 
199   /**
200    * @return Returns the list of connection events.
201    */
202   public List<ConnectionEvent> getConnectionEvents()
203   {
204     return events;
205   }
206 
207   /**
208    * @return True if at least one of the events is a partition event.
209    */
210   public boolean containsPartition()
211   {
212     return partitionCount() > 1;
213   }
214 
215   /**
216    * @return The number of partitions that will result from this connection pattern.
217    */
218   public int partitionCount()
219   {
220     int pCount = 0;
221     for (ConnectionEvent ev : events) {
222       if (ev.type == Type.Partition)
223         pCount++;
224     }
225     if (pCount == 0)
226       pCount = 1;
227     return pCount;
228   }
229 
230   /**
231    * Returns the domain set (the partition) of the given host.
232    * 
233    * @param inetAdr the host for which to obtain the domain set
234    * @return Returns the domain set (the partition) of the given host.
235    * @throws NoSuchElementException Raised if the given host is not
236    *   in any of the partitions.
237    */
238   public DomainSet getPartition(InetAddress inetAdr)
239   {
240     if (hostPartitions == null)
241       hostPartitions = new HashMap<InetAddress, DomainSet>();
242     DomainSet domainSet = hostPartitions.get(inetAdr);
243     if (domainSet == null) {
244       domainSet = searchForHost(inetAdr);
245       hostPartitions.put(inetAdr, domainSet);
246     }
247     return domainSet;
248   }
249 
250   private DomainSet searchForHost(InetAddress inetAdr)
251   {
252     Set<DomainSet> parts = getPartitions();
253     for (DomainSet domainSet : parts) {
254       if (domainSet.getHostDomain(inetAdr) != null)
255         return domainSet;
256     }
257     throw new NoSuchElementException(inetAdr.toString());
258   }
259 
260   /**
261    * Returns a set of domain sets representing the partitions of this connection
262    * pattern event.
263    * 
264    * Implementation note: this method requires that merge events are stored first
265    * in the events list.  Currently this is accomplished by implementing the
266    * <code>Comparable</code> interface and sorting this list before constructing
267    * the set of domain sets.
268    * 
269    * @return Returns a set of domain sets representing the partitions of this
270    *   connection pattern event.
271    */
272   public Set<DomainSet> getPartitions()
273   {
274     if (partitions == null)
275       partitions = new HashSet<DomainSet>();
276     if (!partitions.isEmpty())
277       return partitions;
278 
279     Collections.sort(events);
280     for (ConnectionEvent ev : events) {
281       DomainSet xPartition = findPartition(ev.fromDomain);
282       DomainSet yPartition = findPartition(ev.toDomain);
283 
284       switch (ev.type) {
285         case Partition:
286           if (xPartition == null) {
287             xPartition = new DomainSet(DomainSet.domainComparator);
288             partitions.add(xPartition);
289           }
290           if (yPartition == null) {
291             yPartition = new DomainSet(DomainSet.domainComparator);
292             partitions.add(yPartition);
293           }
294           xPartition.addDomain(ev.fromDomain);
295           yPartition.addDomain(ev.toDomain);
296           break;
297 
298         case Merge:
299           if (xPartition == null && yPartition == null) {
300             xPartition = new DomainSet(DomainSet.domainComparator);
301             partitions.add(xPartition);
302           } else if (xPartition == null && yPartition != null) {
303             xPartition = yPartition;
304           } else {
305             // Should not happen
306             assert false;
307           }
308           xPartition.addDomain(ev.fromDomain);
309           xPartition.addDomain(ev.toDomain);
310           break;
311       }
312     }
313     return partitions;
314   }
315 
316   private DomainSet findPartition(Domain domain)
317   {
318     for (DomainSet domains : partitions) {
319       if (domains.containsDomain(domain)) {
320         return domains;
321       }
322     }
323     return null;
324   }
325 
326 
327   ////////////////////////////////////////////////////////////////////////////////////////////
328   // Methods from Object
329   ////////////////////////////////////////////////////////////////////////////////////////////
330 
331   public String toString()
332   {
333     StringBuilder buf = commonToString(true);
334     buf.append(" [");
335     buf.append(pattern);
336     buf.append("]");
337     return buf.toString();
338   }
339 
340 
341   ////////////////////////////////////////////////////////////////////////////////////////////
342   // Methods from Iterable<Domain>
343   ////////////////////////////////////////////////////////////////////////////////////////////
344 
345   public Iterator<Domain> iterator()
346   {
347     Set<Domain> domains = new HashSet<Domain>(5);
348     for (ConnectionEvent ev : events) {
349       domains.add(ev.fromDomain);
350       domains.add(ev.toDomain);
351     }
352     return domains.iterator();
353   }
354 
355 } // END ConnectionPatternEvent