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 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