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.arm;
20  
21  import java.io.Serializable;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.Map;
26  
27  import jgroup.arm.GroupData.GroupMergeRecord;
28  import jgroup.core.MemberId;
29  import jgroup.core.View;
30  import jgroup.core.arm.GroupExistsException;
31  import jgroup.core.arm.UnknownGroupException;
32  import jgroup.relacs.config.AppConfig;
33  import jgroup.relacs.config.Host;
34  import jgroup.relacs.config.HostSet;
35  import jgroup.relacs.types.ViewImpl;
36  
37  import org.apache.log4j.Logger;
38  
39  
40  /**
41   *  The <code>GroupTable</code> class keeps track of object groups
42   *  installed by the replication manager, and the application
43   *  information associated with these groups.
44   *
45   *  @author Hein Meling
46   *  @since Jgroup 1.2
47   */
48  final class GroupTable
49    implements Iterable<GroupData>
50  {
51  
52    ////////////////////////////////////////////////////////////////////////////////////////////
53    // Logger
54    ////////////////////////////////////////////////////////////////////////////////////////////
55  
56    /** Obtain logger for this class */
57    private static final Logger log = Logger.getLogger(GroupTable.class);
58  
59  
60    ////////////////////////////////////////////////////////////////////////////////////////////
61    // Fields (not shared with other RM replicas)
62    ////////////////////////////////////////////////////////////////////////////////////////////
63  
64    /** Table of all applications in the system (group id -> GroupData) */
65    private final Map<Integer,GroupData> apptable =
66      Collections.synchronizedMap(new HashMap<Integer,GroupData>());
67  
68    /** Field indicating if this RM replica is the leader or not */
69    private boolean iamLeader;
70  
71    /** The period to wait for a view change */
72    private int awaitViewPeriod;
73  
74    /** The most recently installed view of the RM group */
75    private View view;
76  
77  
78    ////////////////////////////////////////////////////////////////////////////////////////////
79    // Constructor
80    ////////////////////////////////////////////////////////////////////////////////////////////
81  
82    /**
83     * The <code>GroupTable</code> constructor will initialize the
84     * database maintained by the replication manager over application
85     * groups installed/deployed through the replication manager.
86     * 
87     * @param awaitViewPeriod
88     *   The delay to wait for a view before triggering the view
89     *   monitor timer.
90     * @param gid
91     *   The group identifier for the application controling and
92     *   interacting with this group table, typically this will be
93     *   the identifier of the replication manager group.
94     */
95    public GroupTable(int awaitViewPeriod, int gid)
96    {
97      this.awaitViewPeriod = awaitViewPeriod;
98      /* Initialize the view and leader fields */
99      viewChange(new ViewImpl(gid), true);
100   }
101 
102 
103   ////////////////////////////////////////////////////////////////////////////////////////////
104   // GroupTable methods
105   ////////////////////////////////////////////////////////////////////////////////////////////
106 
107   /**
108    *  Add the given application group to the table, and set the
109    *  assigned hosts set.
110    */
111   public void add(AppConfig app, HostSet hosts)
112     throws GroupExistsException
113   {
114     if (view == null)
115       throw new IllegalStateException("The view has not been initialized");
116     int groupId = app.getGroupId();
117     if (apptable.containsKey(groupId))
118       throw new GroupExistsException("Cannot add application", groupId);
119     // This will create a new GroupData object since it didn't exist
120     GroupData gd = get(groupId);
121     gd.addHosts(hosts);
122   }
123 
124   /**
125    *  Add the given host to an existing application group in the table.
126    */
127   public void addHost(AppConfig app, Host newHost)
128     throws UnknownGroupException
129   {
130     GroupData gd = getGroupData(app);
131     gd.addHost(newHost);
132   }
133 
134   /**
135    *  Remove the given host from an existing application group in the table.
136    */
137   public void removeHost(AppConfig app, Host oldHost)
138     throws UnknownGroupException
139   {
140     GroupData gd = getGroupData(app);
141     gd.removeHost(oldHost);
142   }
143 
144   /**
145    *  Remove the given application group from the system.
146    */
147   public void remove(AppConfig app)
148   {
149     GroupData gd = apptable.remove(app.getGroupId());
150     if (gd != null)
151       gd.cancelViewMonitor();
152   }
153 
154 
155   /**
156    *  Returns true if the given group is contained in this
157    *  grouptable object; otherwise false is returned.
158    */
159   public boolean contains(int gid)
160   {
161     return apptable.containsKey(gid);
162   }
163 
164 
165   /**
166    *  Returns true if the given application is contained in this
167    *  grouptable object; otherwise false is returned.
168    */
169   public boolean contains(AppConfig app)
170   {
171     return contains(app.getGroupId());
172   }
173 
174 
175   /**
176    *  Returns the set of assigned hosts for the given application.
177    */
178   public HostSet getAssignedHosts(AppConfig app)
179     throws UnknownGroupException
180   {
181     GroupData gd = apptable.get(app.getGroupId());
182     if (gd == null)
183       throw new UnknownGroupException(app.getGroupId());
184     return gd.getAssignedHosts();
185   }
186 
187 
188   /**
189    *  Returns an iterator over the application <code>GroupData</code>
190    *  objects stored in the table.
191    */
192   public Iterator<GroupData> iterator()
193   {
194     return apptable.values().iterator();
195   }
196 
197 
198   ////////////////////////////////////////////////////////////////////////////////////////////
199   // Methods for handling view changes and state merging
200   ////////////////////////////////////////////////////////////////////////////////////////////
201 
202   /**
203    * Invoked by the replication manager upon a local view change
204    * notification of the RM group.
205    *
206    * @param view
207    *   The new view of the RM group
208    * @param iamLeader
209    *   True if the this member is the leader of the RM group.
210    */
211   public void viewChange(View view, boolean iamLeader)
212   {
213     this.view = view;
214     this.iamLeader = iamLeader;
215     for (GroupData gd : apptable.values()) {
216       gd.viewChange(view, iamLeader);
217     }
218   }
219 
220   /**
221    *  Invoked by the replication manager upon a view change notification
222    *  to any of the groups it manages; i.e., kept in this grouptable.
223    *  All replication manager replicas will invoke this method, that is
224    *  correlation will be performed locally on all RM replicas, but
225    *  recovery will only be performed by the leader RM replica. <p>
226    *
227    *  This method should never be invoked with a view of a group which
228    *  is not already registered in this group table using the
229    *  <code>add()</code> method.
230    *
231    *  @param view
232    *    The view of a group for which the group table should be updated.
233    *  @exception IllegalStateException
234    *    Raised if the specified view's group does not correspond to any
235    *    registered groups in the system.
236    */
237   public void viewChangeEvent(View view)
238   {
239     /* Obtain the group data object for the associated view */
240     GroupData gd = get(view.getGid());
241     gd.viewChangeEvent(view);
242   }
243 
244   /**
245    *  Returns a <CODE>MergeRecord</CODE> object containing the 
246    *  information needed by the replication manager replicas contained in
247    *  <CODE>dests</CODE> set to merge their databases.
248    *  
249    *  @param dests the set of replicas to which the merging information
250    *    is addressed.
251    *  @param me identifier of the local replica, needed to identify
252    *    the origin of the data.
253    */
254   MergeRecord getState(MemberId[] dests, MemberId me)
255   {
256     MergeRecord merging = new MergeRecord(apptable.size());
257 
258     int i = 0;
259     for (Iterator<GroupData> iter = iterator(); iter.hasNext(); i++) {
260       GroupData gd = iter.next();
261       merging.groupMerge[i] = gd.getMergeRecord(dests, me);
262     }
263     return merging;
264   }
265 
266   /**
267    *  Takes a <code>MergeRecord</code> object and inserts the data
268    *  contained in it.
269    *
270    *  @param merging
271    *    The merging data sent by another replica
272    */
273   void putState(MergeRecord merging)
274   {
275     for (int i = 0; i < merging.groupMerge.length; i++) {
276       int gid = merging.groupMerge[i].getGroupId();
277       GroupData gd = get(gid);
278       gd.putMergeRecord(merging.groupMerge[i]);
279     }
280   }
281 
282 
283   ////////////////////////////////////////////////////////////////////////////////////////////
284   // Private Methods
285   ////////////////////////////////////////////////////////////////////////////////////////////
286 
287   /**
288    *  Returns the <code>GroupData</code> object associated with the 
289    *  given <code>groupId</code>, or creates a new one if not present
290    *  in this <code>GroupTable</code> instance.
291    */
292   private GroupData get(int groupId)
293   {
294     GroupData gd = apptable.get(groupId);
295     if (gd == null) {
296       if (log.isDebugEnabled()) {
297         log.debug("Group: " + groupId + " not in apptable");
298       }
299       AppConfig app = AppConfig.getApplication(groupId);
300       gd = new GroupData(app, awaitViewPeriod);
301       gd.viewChange(view, iamLeader);
302       apptable.put(groupId, gd);
303     }
304     return gd;
305   }
306 
307   /**
308    * Returns the <code>GroupData</code> object associated with the given
309    * <code>app</code> object; if no such app has been registered an
310    * <code>UnknownGroupException</code> is thrown.
311    * 
312    * @param app
313    * @return
314    * @throws UnknownGroupException
315    */
316   private GroupData getGroupData(AppConfig app)
317     throws UnknownGroupException
318   {
319     int gid = app.getGroupId();
320     GroupData gd = apptable.get(gid);
321     if (gd == null)
322       throw new UnknownGroupException(gid);
323     return gd;
324   }
325 
326 
327   ////////////////////////////////////////////////////////////////////////////////////////////
328   // Methods from Object
329   ////////////////////////////////////////////////////////////////////////////////////////////
330 
331   /**
332    *  Returns a string representation of this object
333    */
334   public String toString()
335   {
336     StringBuilder buf = new StringBuilder();
337     buf.append("[GroupTable: size=");
338     buf.append(apptable.size());
339     buf.append(":\n");
340     for (GroupData gd : apptable.values()) {
341       buf.append(gd);
342       buf.append("\n");
343     }
344     buf.append("]");
345     return buf.toString();
346   }
347 
348 
349   ////////////////////////////////////////////////////////////////////////////////////////////
350   // Inner class for state merge
351   ////////////////////////////////////////////////////////////////////////////////////////////
352 
353   public static class MergeRecord
354     implements Serializable
355   {
356 
357     private static final long serialVersionUID = -441736911665721212L;
358 
359     GroupMergeRecord[] groupMerge;
360 
361     MergeRecord(int size)
362     {
363       groupMerge = new GroupMergeRecord[size];
364     }
365 
366   } // END MergeRecord
367 
368 } // END GroupTable