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