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 static jgroup.util.log.ReplicaEvent.Type.Initialized;
22
23 import java.rmi.RemoteException;
24 import java.util.HashMap;
25 import java.util.Map;
26
27 import jgroup.arm.GroupTable.MergeRecord;
28 import jgroup.core.ConfigManager;
29 import jgroup.core.GroupManager;
30 import jgroup.core.JgroupException;
31 import jgroup.core.MemberId;
32 import jgroup.core.MembershipListener;
33 import jgroup.core.MergingListener;
34 import jgroup.core.View;
35 import jgroup.core.arm.ARMEvent;
36 import jgroup.core.arm.DistributionScheme;
37 import jgroup.core.arm.GroupExistsException;
38 import jgroup.core.arm.RedundancyException;
39 import jgroup.core.arm.ReplicationManager;
40 import jgroup.core.arm.UnknownGroupException;
41 import jgroup.core.protocols.Multicast;
42 import jgroup.relacs.config.AppConfig;
43 import jgroup.relacs.config.DistributedSystemConfig;
44 import jgroup.relacs.config.Host;
45 import jgroup.relacs.config.HostSet;
46 import jgroup.relacs.registry.RegistryImpl;
47 import jgroup.upgrade.UpgradeManagerImpl;
48 import jgroup.util.Abort;
49 import jgroup.util.Util;
50 import jgroup.util.log.Eventlogger;
51 import jgroup.util.log.ReplicaEvent;
52
53 import org.apache.log4j.Logger;
54
55 /**
56 * This implementation of the replication manager interfaces make use
57 * of a variant of the semi-active replication scheme, in which only
58 * the <i>leader</i> RM replica receive and process the requests,
59 * while the <i>follower</i> replicas receive state update messages
60 * through the <code>StateListener</code> interface. This permits
61 * that only the leader perform creation and removal of replicas at
62 * hosts in the distributed system, ensuring that only one group is
63 * created in response to a <code>createGroup()</code> request. <p>
64 *
65 * This is done to prevent problems of non-determinism due to
66 * multithreading in the <code>Correlator</code>. <p>
67 *
68 * The RM leader replica communicate with <code>ExecDaemon</code>
69 * (factory) objects at the hosts that are supposed to server replicas
70 * of any type. The operations that can be performed on the
71 * <code>ExecDaemon</code>s are typically idempotent. <p>
72 *
73 * Other application-level layers (or even servers) may also
74 * communicate arbitrary events to the replication manager. For instance,
75 * the <code>RecoveryLayer</code> will provide <code>ViewChangeEvent</code>
76 * notifications to the RM leader when the group assoicated with a
77 * <code>RecoveryLayer</code> instance has installed a new view.
78 * Furthermore, the <code>RecoveryLayer</code> also implements a lease
79 * based liveness timer in case multiple replicas fail simultaneously. <p>
80 *
81 * The events that can be communicated to the replication manager must
82 * implement the <code>ARMEvent</code> interface, and must handle its
83 * own event when received at the replication manager.
84 *
85 * The replication manager supports several different implementations of
86 * the <code>RecoveryStrategy</code> interface, allowing different
87 * applications to provide specific recovery needs. For instance, one
88 * such recovery strategy is the <code>KeepMinimalInPartition</code>,
89 * which does just that.
90 *
91 * The replication manager also provide self-recovery, by reusing the
92 * same infrastructure used by the application-level recovery. However,
93 * there is no recovery in case all replication manager replicas has been
94 * exhausted.
95 *
96 * @author Hein Meling
97 * @since Jgroup 1.2
98 */
99 public class ReplicaManagerImpl
100 implements ReplicationManager, MembershipListener, MergingListener
101 {
102
103 ////////////////////////////////////////////////////////////////////////////////////////////
104 // Logger
105 ////////////////////////////////////////////////////////////////////////////////////////////
106
107 /** Obtain logger for this class */
108 private static final Logger log = Logger.getLogger(ReplicaManagerImpl.class);
109
110
111 ////////////////////////////////////////////////////////////////////////////////////////////
112 // Constants
113 ////////////////////////////////////////////////////////////////////////////////////////////
114
115 /**
116 * The default period to await view installations after replicas
117 * has been (re)assigned.
118 */
119 private static final int DEFAULT_VIEW_MONITOR_PERIOD = 5000;
120
121
122 ////////////////////////////////////////////////////////////////////////////////////////////
123 // Private Fields
124 ////////////////////////////////////////////////////////////////////////////////////////////
125
126 /*
127 * Note that these fields cannot be initialized here, since the
128 * constructor must ensure to parse the configuration file before
129 * creating an instance of the distribution scheme object.
130 */
131
132 /** The distribution scheme (interface) */
133 private final DistributionScheme distScheme;
134
135 /** The local instance of the dependable registry. */
136 private RegistryImpl dregistry;
137
138 /** Mapping: groupid --> ManagementCallback object */
139 private Map<Integer,ManagementCallback> callbackMap;
140
141 /** This application object for the RM group */
142 private final AppConfig thisApp;
143
144 /** My member identifier */
145 private MemberId myId;
146
147 /** True if I'm the leader (if single member, it is always leader) */
148 private boolean iamLeader = true;
149
150 /** The table of GroupData objects */
151 private final GroupTable grouptable;
152
153
154 ////////////////////////////////////////////////////////////////////////////////////////////
155 // Constructors
156 ////////////////////////////////////////////////////////////////////////////////////////////
157
158 /**
159 * Bootstrap or create local <code>ReplicaManagerImpl</code>
160 * instance. Bootstrapping involves some initialization of some
161 * replication manager components that is required for creating
162 * remote instances of the repliation manager. Creating a local
163 * instance of the <code>ReplicaManagerImpl</code> involves the same
164 * initialization procedure and in addition creating a local
165 * dependable registry instance and obtaining a group manager and
166 * related service references.
167 *
168 * @param bootstrap
169 * True indicates that this is <code>ReplicaManagerImpl</code> is
170 * to be used for bootstrapping only, while false indicate that it
171 * will be a local replication manager instance.
172 *
173 * @exception JgroupException
174 * Raised if the replication manager could not be created for
175 * numerous reasons.
176 * @exception RemoteException
177 * Raised if there was a problem with the dependable registry.
178 */
179 private ReplicaManagerImpl(boolean bootstrap)
180 throws JgroupException, RemoteException
181 {
182 /* Initialize the application name for the Replication Mananger */
183 ConfigManager.init();
184 /* Update reachability status for all hosts in the distributed system */
185 boolean someSuspected = DistributedSystemConfig.updateAll();
186 /* Read configuration parameters, and construct tables */
187 thisApp = AppConfig.getApplication(this);
188 AppConfig dregApp = AppConfig.getApplication(RegistryImpl.class);
189 dregApp.setRedundancy(thisApp.getMinimalRedundancy(), thisApp.getInitialRedundancy());
190 int viewMonitorPeriod = thisApp.getIntParam("ViewMonitorPeriod", DEFAULT_VIEW_MONITOR_PERIOD);
191 grouptable = new GroupTable(viewMonitorPeriod, thisApp.getGroupId());
192 DistributedSystemConfig dsc = ConfigManager.getDistributedSystem();
193 distScheme = new ReplicaCount(grouptable, dsc.getDomainSet());
194 /*
195 * Export the distribution scheme interface for the purpose of
196 * recovery strategy initialization in the AppConfig class.
197 */
198 AppConfig.setDistributionScheme(distScheme);
199 /*
200 * Ensure that the RM application properties are set appropriately for both
201 * the RM and DR applications. That is, check the system properties for minimal
202 * and initial redundancy configuration parameters and initialize accordingly.
203 * Otherwise, the applications.xml defaults are used.
204 */
205 thisApp.setProperties(null);
206 dregApp.setProperties(null);
207
208 boolean createLocalRegistry = thisApp.getBooleanParam("CreateLocalRegistry");
209 if (bootstrap) {
210 if (!createLocalRegistry) {
211 /* Create the DependableRegistry group on hosts in the distributed system */
212 createGroup(dregApp);
213 // Wait for the dregistry to get up and running.
214 Util.sleep(2000);
215 }
216 createGroup(thisApp);
217 log.info("Replication Manager(s) started and ready.");
218 } else {
219 if (createLocalRegistry) {
220 /* Create a local DependableRegistry in the same JVM as the ReplicaManager */
221 dregistry = new RegistryImpl();
222 } else {
223 // Since we have installed dregistry external to the ReplicaManager, initialize state appropriately
224 distScheme.assignReplicas(dregApp);
225 // Since the dregistry does not support RecoveryLayer, we remove it from the grouptable
226 grouptable.remove(dregApp);
227 }
228 //FIXME this initialization does not give correct host assignment when
229 // creating new RM replicas that is exposed to network partitions
230 /* Initialize the replica assignments for the RM group (initialize state) */
231 distScheme.assignReplicas(thisApp);
232
233 /* Obtain a group manager for this application */
234 GroupManager gm = GroupManager.getGroupManager(this);
235 myId = gm.getLocalMember();
236 if (thisApp.getBooleanParam("UpgradeManager"))
237 new UpgradeManagerImpl(dregistry);
238 if (Eventlogger.ENABLED)
239 Eventlogger.logEventFlush(new ReplicaEvent(Initialized, gm.getGroupId()));
240 log.info("Replication Manager ready...");
241 }
242 }
243
244
245 ////////////////////////////////////////////////////////////////////////////////////////////
246 // ReplicationManager interface methods (event notifications for the Replication Manager)
247 ////////////////////////////////////////////////////////////////////////////////////////////
248
249 @Multicast public synchronized void notifyEvent(ARMEvent event)
250 throws RemoteException, Exception
251 {
252 event.handle(distScheme);
253 int groupId = event.getGroupId();
254 if (callbackMap != null && groupId >= 0) {
255 ManagementCallback callback = callbackMap.get(groupId);
256 if (callback != null) {
257 try {
258 callback.notifyEvent(event);
259 } catch (RemoteException e) {
260 log.warn("Management callback client is unavailable");
261 callbackMap.remove(groupId);
262 }
263 }
264 }
265 }
266
267
268 ////////////////////////////////////////////////////////////////////////////////////////////
269 // ReplicationManager interface methods (methods for group management)
270 ////////////////////////////////////////////////////////////////////////////////////////////
271
272 @Multicast public synchronized int createGroup(AppConfig app)
273 throws RemoteException, RedundancyException, GroupExistsException
274 {
275 /*
276 * Assign replicas to a set of available hosts in the distributed
277 * system. Throws an exception if the requested redundancy could
278 * not be satisfied, or if the group was already installed through
279 * this method.
280 */
281 HostSet assignedHosts = distScheme.assignReplicas(app);
282 if (!iamLeader)
283 return app.getGroupId();
284 // if (log.isDebugEnabled())
285 // log.debug(app.getClassData().getShortName() + " assigned to " + assignedHosts);
286
287 /* Cycle through the host list and create replicas using the ExecDaemon */
288 for (Host host : assignedHosts) {
289 if (!host.createReplica(app))
290 host.suspect();
291 }
292 // if (log.isDebugEnabled())
293 // log.debug("Created group: " + app.getGroupId());
294 return app.getGroupId();
295 }
296
297
298 @Multicast public synchronized void removeGroup(AppConfig app)
299 throws RemoteException, UnknownGroupException
300 {
301 HostSet hosts = distScheme.removeReplicas(app);
302 if (!iamLeader)
303 return;
304
305 /* Cycle through the host list and remove replicas using the ExecDaemon */
306 for (Host host : hosts) {
307 if (!host.removeReplica(app))
308 host.suspect();
309 }
310 }
311
312
313 /* (non-Javadoc)
314 * @see jgroup.core.arm.ReplicationManager#updateGroup(jgroup.relacs.config.AppConfig)
315 */
316 @Multicast public synchronized void updateGroup(AppConfig app)
317 {
318 // method to facilitate runtime changes to the replication policy
319 }
320
321
322 /* (non-Javadoc)
323 * @see jgroup.core.arm.ReplicationManager#subscribe(jgroup.core.arm.ReplicationManager.ManagementCallback, int)
324 */
325 @Multicast public synchronized void subscribe(ManagementCallback callback, int gid)
326 throws RemoteException, UnknownGroupException
327 {
328 if (!grouptable.contains(gid)) {
329 throw new UnknownGroupException(gid);
330 }
331 if (callback != null) {
332 if (callbackMap == null)
333 callbackMap = new HashMap<Integer,ManagementCallback>(5);
334 callbackMap.put(Integer.valueOf(gid), callback);
335 }
336 }
337
338
339 /* (non-Javadoc)
340 * @see jgroup.core.arm.ReplicationManager#unsubscribe(int)
341 */
342 @Multicast public synchronized void unsubscribe(int gid)
343 throws RemoteException, UnknownGroupException
344 {
345 if (!grouptable.contains(gid) || callbackMap == null) {
346 throw new UnknownGroupException(gid);
347 }
348 callbackMap.remove(Integer.valueOf(gid));
349 if (callbackMap.isEmpty())
350 callbackMap = null;
351 }
352
353
354 ////////////////////////////////////////////////////////////////////////////////////////////
355 // MembershipListener interface methods
356 ////////////////////////////////////////////////////////////////////////////////////////////
357
358 /**
359 * View change for the replication manager group; set the leader
360 * replica, and ensure that the grouptable is correctly updated
361 * with respect to this view change from the RM group.
362 */
363 public void viewChange(View view)
364 {
365 /*
366 * Note that in the first view, myId will be null.
367 * However, this is ok, since the isLeader() returns true
368 * whenever there is only a single member in the view.
369 */
370 iamLeader = view.isLeader(myId);
371 grouptable.viewChange(view, iamLeader);
372 /* This is needed for activating self-recovery */
373 grouptable.viewChangeEvent(view);
374 }
375
376
377 public void hasLeft()
378 {
379 }
380
381
382 public void prepareChange()
383 {
384 }
385
386
387 ////////////////////////////////////////////////////////////////////////////////////////////
388 // MergingListener interface methods
389 ////////////////////////////////////////////////////////////////////////////////////////////
390
391 /* (non-Javadoc)
392 * @see jgroup.core.MergingListener#getState(jgroup.core.MemberId[])
393 */
394 public Object getState(MemberId[] dests)
395 {
396 // if (log.isDebugEnabled())
397 // log.debug("RM.getState");
398 return grouptable.getState(dests, myId);
399 }
400
401
402 /* (non-Javadoc)
403 * @see jgroup.core.MergingListener#putState(java.lang.Object, jgroup.core.MemberId[])
404 */
405 public void putState(Object state, MemberId[] sources)
406 {
407 // if (log.isDebugEnabled())
408 // log.debug("RM.putState");
409 grouptable.putState((MergeRecord) state);
410 /*
411 * For each application in the stored grouptable, check if we need
412 * to schedule the view monitor after the state of the replication
413 * manager has been updated.
414 */
415 for (GroupData gd : grouptable) {
416 gd.scheduleViewMonitor();
417 }
418 }
419
420
421 ////////////////////////////////////////////////////////////////////////////////////////////
422 // Main method
423 ////////////////////////////////////////////////////////////////////////////////////////////
424
425 public static void main(String[] argv)
426 throws Exception
427 {
428 /* By default value, we don't bootstrap */
429 boolean bootstrap = false;
430
431 /* Parse the command line arguments */
432 String usageString = "replicamanager [--bootstrap]";
433 for (int i = 0; i < argv.length; i++) {
434 if (argv[i].equals("--bootstrap")) {
435 bootstrap = true;
436 } else {
437 System.err.println("Unknown option " + argv[i]);
438 Abort.usage(usageString);
439 }
440 }
441
442 try {
443 new ReplicaManagerImpl(bootstrap);
444 } catch (Exception e) {
445 Abort.exit("Failed to start the replication manager", e, 1);
446 }
447 if (bootstrap) {
448 System.exit(0);
449 }
450 }
451
452 } // END ReplicaManagerImpl