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