001// 002// Name 003// $RCSfile: PartitionManager.java,v $ 004// 005// Copyright 006// Copyright 2010-2015 Cloud Software Group, Inc. ALL RIGHTS RESERVED. 007// Cloud Software Group, Inc. Confidential Information 008// 009// History 010// $Revision: 1.1.2.57.2.2 $ $Date: 2015/08/05 20:33:26 $ 011// 012package com.kabira.platform.highavailability; 013 014import com.kabira.platform.ManagedObject; 015import com.kabira.platform.ManagedClassError; 016import com.kabira.platform.ResourceUnavailableException; 017import com.kabira.platform.Transaction; 018import com.kabira.platform.Transaction.Rollback; 019import com.kabira.platform.disteng.DEPartitionManager; 020import com.kabira.platform.disteng.DEPartitionNotifier; 021import com.kabira.platform.disteng.DEPartition; 022import com.kabira.platform.disteng.DEProperties; 023import com.kabira.platform.disteng.DEPartitionNotFound; 024import com.kabira.platform.disteng.DENodeMismatch; 025import com.kabira.platform.disteng.DENotActiveNode; 026import com.kabira.platform.disteng.DEEnableAction; 027import com.kabira.platform.disteng.DEDisableAction; 028import com.kabira.platform.disteng.DEMapperAudit; 029import com.kabira.platform.disteng.PartitionState; 030import com.kabira.platform.disteng.PartitionStatus; 031import com.kabira.platform.disteng.DEReplicationType; 032import com.kabira.platform.disteng.DESparsePartitionAudit; 033import com.kabira.platform.disteng.DEReplicaAudit; 034import com.kabira.platform.disteng.DERemoteEnableAction; 035 036import java.lang.reflect.Field; 037import java.util.HashSet; 038import java.util.logging.LogManager; 039import java.util.logging.Logger; 040import java.util.logging.Level; 041 042/** 043 * The PartitionManager class. This is the main interface for managing 044 * partitions. 045 */ 046public final class PartitionManager 047{ 048 /** 049 * An enumeration of the possible actions that can be taken when 050 * enabling high availability for a node. 051 */ 052 public enum EnableAction 053 { 054 /** 055 * Perform any object migrations needed to activate the 056 * partitions defined for this node. This option should be used 057 * for newly added nodes. 058 */ 059 JOIN_CLUSTER, 060 /** 061 * Before migrating the partition data, remove any instances that 062 * exist on the local node. This option should be used for nodes 063 * where the local data doesn't need to be preserved. 064 */ 065 JOIN_CLUSTER_PURGE, 066 /** 067 * For all partitions where the node joining the cluster is the 068 * current active node, access the remote node where the 069 * partition should be restored from, and compare the objects 070 * between the local node and remote node. If an object on the 071 * local node has been updated, or a new object created, push the 072 * update to the remote node. If the remote node has been 073 * updated, replicate the change to the local node. If a state 074 * conflict is detected, or a duplicate key is found, remove the 075 * local object and copy the remote node object to the local 076 * node. 077 * <p> 078 * For all partitions where the node joining the cluster is 079 * acting as a replica, the replica objects are deleted and 080 * instances migrated from the remote node. 081 * <p> 082 * Warning: This is performance hostile, and should only be done 083 * when resolving a split-brain scenario in a cluster. 084 * 085 * @see Partition.Properties#restoreFromNode(String) 086 */ 087 JOIN_CLUSTER_RESTORE 088 } 089 090 /** 091 * An enumeration of the possible actions that can be taken when 092 * disabling high availability for a node. 093 */ 094 public enum DisableAction 095 { 096 /** 097 * Perform any object migrations needed to migrate 098 * partitions owned by this node to the first order 099 * replica node, and remove this node from all replica 100 * lists. Before the disable is executed, an audit is 101 * done to insure that no partitions will be moved to the 102 * UNAVAILABLE state. 103 */ 104 LEAVE_CLUSTER, 105 /** 106 * Same as LEAVE_CLUSTER, but with audits disabled. 107 */ 108 LEAVE_CLUSTER_FORCE, 109 } 110 111 /** 112 * Find all partitions on the local node. 113 * 114 * @return Array of Partition instances. 115 */ 116 public static final Partition [] getPartitions() 117 { 118 DEPartitionManager pm = new DEPartitionManager(); 119 DEPartition [] dp = pm.getPartitions(); 120 121 Partition [] pa = new Partition[dp.length]; 122 123 for (int i = 0; i < dp.length; i++) 124 { 125 pa[i] = new Partition( 126 dp[i].name, 127 dp[i].nodes, 128 dp[i].replicationTypeList, 129 mapState(dp[i].state), 130 mapStatus(dp[i].status), 131 dp[i].lastUpdated, 132 dp[i].forceReplication, 133 dp[i].objectChunks, 134 dp[i].numThreads, 135 mapDESparseAudit(dp[i].sparseAudit), 136 mapDEReplicaAudit(dp[i].replicaAudit), 137 mapDERemoteEnableAction(dp[i].remoteEnableAction), 138 dp[i].broadcastUpdates); 139 } 140 return pa; 141 } 142 143 /** 144 * Find a partition by name. 145 * 146 * @param partitionName Name of partition to find. 147 * 148 * @return Partition instance. 149 * 150 * @exception PartitionNotFound 151 * The given partition doesn't exist. 152 * @exception IllegalArgumentException 153 * The partitionName was empty. 154 */ 155 public static final Partition getPartition( 156 final String partitionName) throws 157 IllegalArgumentException, PartitionNotFound 158 { 159 if (partitionName.isEmpty()) 160 { 161 throw new IllegalArgumentException("partitionName cannot be empty"); 162 } 163 164 return _getPartition(partitionName); 165 } 166 167 /** 168 * Get the partition an object is in. 169 * 170 * @param obj Object to examine. 171 * 172 * @return Partition instance. 173 * 174 * @exception ManagedClassError 175 * The object is not a Managed object. 176 * @exception PartitionNotFound 177 * The given object isn't in a partition. This can happen if 178 * object instances were created before a {@link PartitionMapper} was 179 * installed for a managed class, or no mapper was ever defined. 180 */ 181 public static final Partition getObjectPartition( 182 final Object obj) throws ManagedClassError, PartitionNotFound 183 { 184 checkManagedObject(obj); 185 186 DEPartitionManager pm = new DEPartitionManager(); 187 188 String partitionName = pm.getObjectPartition(obj); 189 if (partitionName.length() == 0) 190 { 191 throw new PartitionNotFound( 192 "No Partition exists for object '" + obj + "'"); 193 } 194 195 return _getPartition(partitionName); 196 } 197 198 /** 199 * Move the instance from the current partition (if any) to 200 * the partition defined by the installed mapper for the object class. 201 * The interactions between installed mappers and partitions when an 202 * object is repartitioned is enumerated below: 203 * <ul> 204 * <li> 205 * If the object was not associated with a partition, and a mapper 206 * maps it to a new partition, the instance is replicated to all 207 * nodes defined for the new partition. 208 * </li> 209 * <li> 210 * If the object was mapped to a pre-existing partition, and a mapper 211 * maps it to a new partition, the instance is replicated to all 212 * nodes defined for the new partition. The instance is removed from 213 * all nodes that were defined in the old partition but are not 214 * defined with the new partition. 215 * </li> 216 * <li> 217 * If the object was associated with a partition, and a mapper no 218 * longer exists, the instance is removed from all nodes that that 219 * were defined in the old partition, and the instance is left in 220 * shared memory on the local node as un-partitioned. 221 * </li> 222 * <li> 223 * If the object was not associated with a partition, and a mapper 224 * does not exist, this operation does nothing. 225 * </li> 226 * </ul> 227 * 228 * @param obj Object to repartition. 229 * 230 * @exception ManagedClassError 231 * The object is not a Managed object. 232 * @exception ResourceUnavailableException 233 * An illegal partition name was returned from the mapper, or the 234 * partition that is returned in not in the <b>ACTIVE</b> state. 235 */ 236 public static final void repartitionInstance(final Object obj) 237 throws ManagedClassError 238 { 239 checkManagedObject(obj); 240 241 // First, clear the cached location in the Java proxy for this object. 242 // 243 // FIX THIS, DS: make this an undocumented native entry point 244 // on ManagedObject? 245 try 246 { 247 Field localityField = obj.getClass().getField("$m_locality"); 248 localityField.setByte(obj, (byte)0); 249 } 250 catch (NoSuchFieldException ex) 251 { 252 throw new ManagedClassError( 253 "Clearing locality failed: " + ex.getMessage()); 254 } 255 catch (SecurityException ex) 256 { 257 throw new ManagedClassError( 258 "Clearing locality failed: " + ex.getMessage()); 259 } 260 catch (IllegalArgumentException ex) 261 { 262 throw new ManagedClassError( 263 "Clearing locality failed: " + ex.getMessage()); 264 } 265 catch (IllegalAccessException ex) 266 { 267 throw new ManagedClassError( 268 "Clearing locality failed: " + ex.getMessage()); 269 } 270 271 DEPartitionManager pm = new DEPartitionManager(); 272 pm.repartitionObject(obj); 273 } 274 275 /** 276 * Use {@link #definePartition(String, Partition.Properties, String, ReplicaNode [])} 277 * @param partitionName Name of partition to create. 278 * @param nodes An ordered list of nodes for the partition. 279 */ 280 @Deprecated 281 public static final void definePartition( 282 final String partitionName, 283 final String [] nodes) throws IllegalArgumentException 284 { 285 if (partitionName.isEmpty()) 286 { 287 throw new IllegalArgumentException("partitionName cannot be empty"); 288 } 289 validateNodeList(nodes); 290 291 DEReplicationType [] rtl = new DEReplicationType[nodes.length]; 292 for (int i = 0; i < rtl.length; i++) 293 { 294 rtl[i] = DEReplicationType.DataSynchronous; 295 } 296 297 DEProperties props = Partition.defaultDEProperties(); 298 299 definePartition(partitionName, props, nodes, rtl); 300 } 301 302 /** 303 * Use {@link #definePartition(String, Partition.Properties, String, ReplicaNode [])} 304 * instead. 305 * @param partitionName Name of partition to create. 306 * @param restoreFromNode The remote node to use when restoring the 307 * partition's objects. 308 * @param nodes An ordered list of nodes for the partition. 309 */ 310 @Deprecated 311 public static final void definePartition( 312 final String partitionName, 313 final String restoreFromNode, 314 final String [] nodes) throws IllegalArgumentException, NotActiveNode 315 { 316 if (restoreFromNode.isEmpty()) 317 { 318 throw new IllegalArgumentException( 319 "restoreFromNode cannot be empty"); 320 } 321 322 if (partitionName.isEmpty()) 323 { 324 throw new IllegalArgumentException("partitionName cannot be empty"); 325 } 326 validateNodeList(nodes); 327 328 DEReplicationType [] rtl = new DEReplicationType[nodes.length]; 329 for (int i = 0; i < rtl.length; i++) 330 { 331 rtl[i] = DEReplicationType.DataSynchronous; 332 } 333 334 DEProperties props = Partition.defaultDEProperties(); 335 props.restoreFromNode = restoreFromNode; 336 337 definePartition(partitionName, props, nodes, rtl); 338 } 339 340 /** 341 * Define a partition. 342 * <p> 343 * This method defines a partition. This method can be called on any 344 * node in the cluster to define a new partition, or change the 345 * definition of a pre-existing partition. Once all partitions have 346 * been defined for a node, the enablePartitions() method should be 347 * called to move the partitions to the active state. It is a runtime 348 * error to attempt to create or modify instances in a newly defined 349 * partition until the enablePartitions() method is called. 350 * <p> 351 * The activeNode is the node that will be the active node for the 352 * partition after it is enabled. 353 * <p> 354 * Optional partition properties can be used by passing in an 355 * instance of the {@link Partition.Properties} class. User defined 356 * properties such as forceReplication are saved and subsequently 357 * used during failover processing. 358 * <p> 359 * The replicas array contains an ordered list of replica nodes for 360 * the partition. A failure of the active node results in the 361 * partition automatically migrating to the the next entry in the 362 * replicas array. Objects are sent to each replica node based on 363 * the configuration in the ReplicaNode entry. 364 * 365 * @param partitionName Name of partition to create. 366 * @param partitionProperties Optional properties for the partition. 367 * If null, the default property values are used. 368 * @param activeNode The active node for the partition 369 * @param replicas An ordered list of replica nodes for the partition. 370 * Should be passed in as a null instance or a zero length array if 371 * no replicas are desired. 372 * 373 * @exception IllegalArgumentException 374 * The partitionName or activeNode was empty, or the replicas array 375 * was invalid. 376 * @exception NotActiveNode 377 * The current node is not the active node for the partition and 378 * restoreFromNode was defined in partitionProperties. 379 * @exception ResourceUnavailableException 380 * The operation timed out waiting for the active node to become 381 * available. 382 */ 383 public static final void definePartition( 384 final String partitionName, 385 final Partition.Properties partitionProperties, 386 final String activeNode, 387 final ReplicaNode [] replicas) 388 throws IllegalArgumentException, NotActiveNode, ResourceUnavailableException 389 { 390 if (partitionName.isEmpty()) 391 { 392 throw new IllegalArgumentException("partitionName cannot be empty"); 393 } 394 395 if (activeNode.isEmpty()) 396 { 397 throw new IllegalArgumentException("activeNode cannot be empty"); 398 } 399 400 validateReplicaList(replicas); 401 402 int nodeLength = (replicas == null) ? 1 : replicas.length + 1; 403 404 String [] nodes = new String[nodeLength]; 405 406 nodes[0] = activeNode; 407 if (replicas != null) 408 { 409 for (int i = 0; i < replicas.length; i++) 410 { 411 assert( i + 1 < nodeLength ); 412 nodes[i + 1] = replicas[i].nodeName; 413 } 414 } 415 validateNodeList(nodes); 416 417 DEReplicationType [] rtl = new DEReplicationType[nodes.length]; 418 rtl[0] = DEReplicationType.DataSynchronous; 419 if (replicas != null) 420 { 421 for (int i = 0; i < replicas.length; i++) 422 { 423 rtl[i + 1] = mapReplicationType(replicas[i].replicationType); 424 } 425 } 426 427 DEProperties props = Partition.copyDEProperties(partitionProperties); 428 429 definePartition(partitionName, props, nodes, rtl); 430 } 431 432 /** 433 * Associate a mapper to a given managed class. 434 * 435 * This method uses {@link PartitionMapper.Properties.Audit#IGNORE_PARTITIONING} 436 * for audit. 437 * 438 * @param managedClass Name of a Managed object. 439 * @param partitionMapper User defined PartitionMapper instance. 440 * 441 * @exception ManagedClassError 442 * The managedClass is not a Managed object, or has a non-abstract 443 * parent class with a key defined but no mapper installed. 444 * 445 * @see PartitionManager#setMapper(Class, PartitionMapper, PartitionMapper.Properties) 446 */ 447 public static final void setMapper( 448 final Class<?> managedClass, 449 final PartitionMapper partitionMapper) throws ManagedClassError 450 { 451 setMapper(managedClass, partitionMapper, null); 452 } 453 454 /** 455 * Associate a mapper to a given managed class. 456 * <p> 457 * This method is used to associate a user defined PartitionMapper 458 * instance with a managed class. All managed types that are replicated 459 * must have a PartitionMapper associated with the class. This method must 460 * be called on all nodes that has the partition definition. 461 * <p> 462 * The same PartitionMapper instance can be associated with multiple 463 * classes. If a PartitionMapper is already associated with a class, 464 * the new value overwrites the previous one. 465 * <p> 466 * Applications should only delete a PartitionMapper instance after a 467 * new instance is installed. If a PartitionMapper instance is 468 * deleted, any subsequent object creates for the class will throw a 469 * runtime exception. 470 * <p> 471 * Mapper instances are inherited by all classes that extend the 472 * managed class. If the managed class extends another class, that 473 * class must not have any keys defined. 474 * <p> 475 * If auditing is enabled in the mapperProperties instance, this routine 476 * will verify that no unpartitioned instances of the given class 477 * already exist in shared memory. This can happen if instances are 478 * created before a mapper is installed. 479 * 480 * @param managedClass Managed class. 481 * @param partitionMapper User defined PartitionMapper instance. 482 * @param mapperProperties Optional mapper properties. If null, 483 * {@link PartitionMapper.Properties.Audit#IGNORE_PARTITIONING} is used. 484 * 485 * @return Old mapper instance associated with managedClass, or null if 486 * no mapper was set. 487 * 488 * @exception ManagedClassError 489 * The managedClass is not a Managed object, or has a non-abstract 490 * parent class with a key defined but no mapper installed. 491 */ 492 public static final PartitionMapper setMapper( 493 final Class<?> managedClass, 494 final PartitionMapper partitionMapper, 495 final PartitionMapper.Properties mapperProperties) 496 throws ManagedClassError 497 { 498 try 499 { 500 checkManagedClass(managedClass); 501 return setMapper(managedClass.getName(), 502 partitionMapper, mapperProperties); 503 } 504 catch (ResourceUnavailableException ex) 505 { 506 // 507 // FIX THIS: Probably untestable because of the checkManagedClass() 508 // 509 throw new ManagedClassError(ex.getMessage()); 510 } 511 } 512 513 /** 514 * Associate a mapper to a given shared memory type. 515 * <p> 516 * This method is used to associate a user defined PartitionMapper 517 * instance with shared memory type. All types that are replicated 518 * must have a PartitionMapper associated with the class. This method must 519 * be called on all nodes that has the partition definition. 520 * <p> 521 * The same PartitionMapper instance can be associated with multiple 522 * type. If a PartitionMapper is already associated with a type, 523 * the new value overwrites the previous one. 524 * <p> 525 * Applications should only delete a PartitionMapper instance after a 526 * new instance is installed. If a PartitionMapper instance is 527 * deleted, any subsequent object creates for the type will throw a 528 * runtime exception. 529 * <p> 530 * Mapper instances are inherited by all subtypes. If the type 531 * extends another type, that class must not have any keys defined. 532 * <p> 533 * If auditing is enabled in the mapperProperties instance, this routine 534 * will verify that no unpartitioned instances of the given type 535 * already exist in shared memory. This can happen if instances are 536 * created before a mapper is installed. 537 * 538 * @param typeName Name of a shared memory type. 539 * @param partitionMapper User defined PartitionMapper instance. 540 * @param mapperProperties Optional mapper properties. If null, 541 * {@link PartitionMapper.Properties.Audit#IGNORE_PARTITIONING} is used. 542 * 543 * @return Old mapper instance associated with managedClass, or null if 544 * no mapper was set. 545 * 546 * @exception ResourceUnavailableException 547 * The typeName is not a shared memory type, or has a non-abstract 548 * parent class with a key defined but no mapper installed. 549 */ 550 public static final PartitionMapper setMapper( 551 final String typeName, 552 final PartitionMapper partitionMapper, 553 final PartitionMapper.Properties mapperProperties) 554 throws ResourceUnavailableException 555 { 556 if (partitionMapper == null) 557 { 558 throw new NullPointerException("partitionMapper cannot be null"); 559 } 560 DEPartitionManager pm = new DEPartitionManager(); 561 DEMapperAudit audit = (mapperProperties == null) 562 ? DEMapperAudit.MapperIgnore 563 : mapAudit(mapperProperties.getAudit()); 564 565 return (PartitionMapper)pm.setMapperAudit( 566 typeName, partitionMapper, audit); 567 } 568 569 /** 570 * Clear a mapper for a given managed class. 571 * <p> 572 * This method is used to remove a user defined PartitionMapper 573 * instance for a managed class. This method must be called on all 574 * nodes where the mapper was previously defined. 575 * <p> 576 * If no PartitionMapper is associated with a class, this operation 577 * is a noop. 578 * 579 * @param managedClass Managed class. 580 * 581 * @exception ManagedClassError 582 * The managedClass is not a Managed object. 583 * 584 * @see PartitionManager#setMapper(Class, PartitionMapper, PartitionMapper.Properties) 585 */ 586 public static final void clearMapper( 587 final Class<?> managedClass) throws ManagedClassError 588 { 589 try 590 { 591 clearMapper(managedClass.getName()); 592 } 593 catch (ResourceUnavailableException ex) 594 { 595 throw new ManagedClassError(ex.getMessage()); 596 } 597 } 598 599 /** 600 * Clear a mapper for a given type. 601 * <p> 602 * This method is used to remove a user defined PartitionMapper 603 * instance for a shared memory type. This method must be called on all 604 * nodes where the mapper was previously defined. 605 * <p> 606 * If no PartitionMapper is associated with a class, this operation 607 * is a noop. 608 * 609 * @param typeName Name of a Managed object. 610 * 611 * @exception ResourceUnavailableException 612 * The typeName does not exist in shared memory. 613 * 614 * @see PartitionManager#setMapper(String, PartitionMapper, PartitionMapper.Properties) 615 */ 616 public static final void clearMapper( 617 final String typeName) throws ResourceUnavailableException 618 { 619 DEPartitionManager pm = new DEPartitionManager(); 620 pm.setMapperAudit(typeName, null, DEMapperAudit.MapperIgnore); 621 } 622 623 /** 624 * Use {@link com.kabira.platform.ManagedObject#isPartitioned} 625 * @param managedClass Class to check. 626 * @return true if class is partitioned. 627 */ 628 @Deprecated 629 public static final boolean isPartitioned(final Class<?> managedClass) 630 { 631 return ManagedObject.isPartitioned(managedClass); 632 } 633 634 /** 635 * Enable high availability for the all partitions defined on the node. 636 * <p> 637 * This method is used to enable all partitions that have been 638 * defined for this node. This method should be called each time new 639 * partitions are defined. 640 * This method blocks until all required object migration 641 * is complete. 642 * 643 * @param enableAction The action to take when enabling partitions. 644 * 645 * @exception NodeMismatch 646 * The partition does not have the local node in the node list, and the 647 * active node for the partition has a different node list. 648 * @exception ResourceUnavailableException 649 * The minimum number of nodes needed to establish a quorum has not been 650 * seen. 651 */ 652 public static final void enablePartitions( 653 final EnableAction enableAction) 654 throws NodeMismatch, ResourceUnavailableException 655 { 656 DEPartitionManager pm = new DEPartitionManager(); 657 try 658 { 659 pm.enablePartitions(mapEnable(enableAction)); 660 } 661 catch (DENodeMismatch ex) 662 { 663 throw new NodeMismatch(ex.errMsg); 664 } 665 } 666 667 /** 668 * Disable high availability for the all partitions defined on the node. 669 * <p> 670 * This method is used to remove the local node from the node list of 671 * all partitions that have been defined for this node. If called 672 * multiple times, the additional calls have no effect. 673 * 674 * @param disableAction The action to take when disabling partitions. 675 * 676 * @exception ResourceUnavailableException 677 * One or more partitions would be in the UNAVAILABLE state after the 678 * disable executes, not thrown if LEAVE_CLUSTER_FORCE is used. 679 */ 680 public static final void disablePartitions( 681 final DisableAction disableAction) throws ResourceUnavailableException 682 { 683 DEPartitionManager pm = new DEPartitionManager(); 684 pm.disablePartitions(mapDisable(disableAction)); 685 } 686 687 /** 688 * Wait for a remote node to become available. 689 * 690 * @param remoteNode name of remote node to wait for. 691 * 692 * @exception ResourceUnavailableException 693 * The operation timed out waiting for the remote node to become 694 * available. 695 */ 696 public static final void waitForNode( 697 final String remoteNode) throws ResourceUnavailableException 698 { 699 DEPartitionManager pm = new DEPartitionManager(); 700 pm.waitForNode(remoteNode); 701 } 702 703 /** 704 * Create local copies of all partition defined by the remote node. 705 * <p> 706 * After execution of this method completes, all partitions defined 707 * on the remote node will exist on the local node, and will be 708 * accessible using the {@link #getPartition(String)} method. 709 * 710 * @param remoteNode name of remote node to access. 711 * 712 * @exception ResourceUnavailableException 713 * The operation timed out waiting for the remote node to become 714 * available. 715 */ 716 public static final void getRemotePartitions( 717 final String remoteNode) throws ResourceUnavailableException 718 { 719 DEPartitionManager pm = new DEPartitionManager(); 720 pm.getRemotePartitions(remoteNode); 721 } 722 723 // 724 // This routine performs initialization of the DistributionLogger 725 // logger, and runtime resources used in the highavailability 726 // package. It normally is called from a static initializer defined 727 // in this class, but can also be called from ManagedObject via 728 // reflection. 729 // 730 // Note: We assume caller synchronizes this call. 731 // 732 private static final void initialize() 733 { 734 if (m_logger == null) 735 { 736 m_logger = new DistributionLogger(); 737 LogManager lm = LogManager.getLogManager(); 738 lm.addLogger(m_logger); 739 ManagedObject.setDistributedQuery(new DistributedQuery()); 740 } 741 } 742 743 // 744 // Package private methods. These don't bother with handling 745 // PartitionNotFound exceptions, since they can only be called after 746 // getPartition() was executed, and we never delete partitions. 747 // 748 749 static final void updatePartition( 750 final String existingPartition, 751 DEProperties props) throws NodeMismatch, NotActiveNode 752 { 753 DEPartitionManager pm = new DEPartitionManager(); 754 try 755 { 756 pm.updatePartition(existingPartition, props); 757 } 758 catch (DENodeMismatch ex) 759 { 760 throw new NodeMismatch(ex.errMsg); 761 } 762 catch (DENotActiveNode ex) 763 { 764 throw new NotActiveNode(ex.errMsg); 765 } 766 } 767 768 static final void migratePartition( 769 final String partitionName, 770 DEProperties props, 771 final String [] nodes, 772 DEReplicationType [] rtl) throws NotActiveNode 773 { 774 DEPartitionManager pm = new DEPartitionManager(); 775 try 776 { 777 pm.migratePartition(partitionName, props, nodes, rtl); 778 } 779 catch (DENotActiveNode ex) 780 { 781 throw new NotActiveNode(ex.errMsg); 782 } 783 } 784 785 static final void setNotifier( 786 final String partitionName, 787 final PartitionNotifier partitionNotifier) throws PartitionNotFound 788 { 789 Notifier notifier = new Notifier(partitionNotifier); 790 DEPartitionManager pm = new DEPartitionManager(); 791 pm.setNotifier(partitionName, notifier); 792 } 793 794 // 795 // Validate the replica list. We just check for null replicas here, the 796 // rest of the checks are done in validateNodeList() 797 // 798 static void validateReplicaList( 799 final ReplicaNode [] replicas) throws IllegalArgumentException 800 { 801 if (replicas != null) 802 { 803 for (int i = 0; i < replicas.length; i++) 804 { 805 ReplicaNode r = replicas[i]; 806 if (r == null) 807 { 808 throw new IllegalArgumentException( 809 "Replica entry at index " + i + " cannot be empty"); 810 } 811 } 812 } 813 } 814 815 // 816 // Validate the node list 817 // 818 static void validateNodeList( 819 final String [] nodes) throws IllegalArgumentException 820 { 821 HashSet<String> nodeSet = new HashSet<String>(); 822 if (nodes.length == 0) 823 { 824 throw new IllegalArgumentException( 825 "The node array must have at least one entry"); 826 } 827 for (int i = 0; i < nodes.length; i++) 828 { 829 String node = nodes[i]; 830 if (node == null || node.isEmpty()) 831 { 832 throw new IllegalArgumentException("Node entry at index " + 833 i + " cannot be empty"); 834 } 835 if (nodeSet.contains(node)) 836 { 837 throw new IllegalArgumentException("Node entry " + node + 838 " at index " + i + " is a duplicate. " + 839 "All nodes must be unique"); 840 } 841 nodeSet.add(node); 842 } 843 } 844 845 // 846 // Internal getPartition() method 847 // 848 private static Partition _getPartition( 849 final String partitionName) throws PartitionNotFound 850 { 851 assert( !partitionName.isEmpty() ); 852 853 DEPartitionManager pm = new DEPartitionManager(); 854 855 try 856 { 857 DEPartition p = pm.getPartition(partitionName); 858 return new Partition( 859 p.name, 860 p.nodes, 861 p.replicationTypeList, 862 mapState(p.state), 863 mapStatus(p.status), 864 p.lastUpdated, 865 p.forceReplication, 866 p.objectChunks, 867 p.numThreads, 868 mapDESparseAudit(p.sparseAudit), 869 mapDEReplicaAudit(p.replicaAudit), 870 mapDERemoteEnableAction(p.remoteEnableAction), 871 p.broadcastUpdates); 872 } 873 catch (DEPartitionNotFound ex) 874 { 875 throw new PartitionNotFound(ex.errMsg); 876 } 877 } 878 879 // 880 // Wrapper class to map notifiers. 881 // 882 private static class Notifier extends DEPartitionNotifier 883 { 884 public void stateChange( 885 final String partitionName, 886 final PartitionState oldState, 887 final PartitionState newState) 888 { 889 if (ManagedObject.isEmpty(m_partitionNotifier)) 890 { 891 ManagedObject.delete(this); 892 } 893 else 894 { 895 m_partitionNotifier.stateChange( 896 partitionName, 897 mapState(oldState), 898 mapState(newState)); 899 } 900 } 901 902 Notifier(PartitionNotifier partitionNotifier) 903 { 904 m_partitionNotifier = partitionNotifier; 905 } 906 907 PartitionNotifier m_partitionNotifier; 908 } 909 910 // 911 // Wrapper method to map definePartition exceptions. 912 // 913 private static void definePartition( 914 final String partitionName, 915 final DEProperties props, 916 final String [] nodes, 917 DEReplicationType [] rtl) 918 throws NotActiveNode 919 { 920 DEPartitionManager pm = new DEPartitionManager(); 921 922 try 923 { 924 pm.definePartition(partitionName, props, nodes, rtl); 925 } 926 catch (DENotActiveNode ex) 927 { 928 throw new NotActiveNode(ex.errMsg); 929 } 930 } 931 932 // 933 // Utility function to map PartitionState to Partition.State 934 // 935 private static Partition.State mapState(PartitionState state) 936 { 937 Partition.State s; 938 939 switch (state) 940 { 941 case PartitionInitial: 942 s = Partition.State.INITIAL; 943 break; 944 case PartitionActive: 945 s = Partition.State.ACTIVE; 946 break; 947 case PartitionUpdating: 948 s = Partition.State.UPDATING; 949 break; 950 case PartitionMigrating: 951 s = Partition.State.MIGRATING; 952 break; 953 case PartitionReplicating: 954 s = Partition.State.REPLICATING; 955 break; 956 default: 957 assert( state == PartitionState.PartitionUnavailable ); 958 s = Partition.State.UNAVAILABLE; 959 break; 960 } 961 return s; 962 } 963 964 // 965 // Utility function to map PartitionStatus to Partition.Status 966 // 967 private static Partition.Status mapStatus(PartitionStatus status) 968 { 969 Partition.Status s; 970 971 switch (status) 972 { 973 case LocalDisabled: 974 s = Partition.Status.LOCAL_DISABLED; 975 break; 976 case LocalDefined: 977 s = Partition.Status.LOCAL_DEFINED; 978 break; 979 case LocalEnabled: 980 s = Partition.Status.LOCAL_ENABLED; 981 break; 982 case RemoteDefined: 983 s = Partition.Status.REMOTE_DEFINED; 984 break; 985 default: 986 assert( status == PartitionStatus.RemoteEnabled ); 987 s = Partition.Status.REMOTE_ENABLED; 988 break; 989 } 990 return s; 991 } 992 993 // 994 // Utility function to map PartitionManager.EnableAction to EnableAction 995 // 996 static DEEnableAction mapEnable( 997 final PartitionManager.EnableAction enableAction) 998 { 999 DEEnableAction e; 1000 1001 switch (enableAction) 1002 { 1003 case JOIN_CLUSTER: 1004 e = DEEnableAction.JoinCluster; 1005 break; 1006 case JOIN_CLUSTER_PURGE: 1007 e = DEEnableAction.JoinClusterPurge; 1008 break; 1009 default: 1010 assert( enableAction == EnableAction.JOIN_CLUSTER_RESTORE ); 1011 e = DEEnableAction.JoinClusterRestore; 1012 break; 1013 } 1014 return e; 1015 } 1016 1017 // 1018 // Utility function to map PartitionManager.DisableAction to DisableAction 1019 // 1020 static DEDisableAction mapDisable( 1021 final PartitionManager.DisableAction disableAction) 1022 { 1023 DEDisableAction e; 1024 1025 switch (disableAction) 1026 { 1027 case LEAVE_CLUSTER: 1028 e = DEDisableAction.LeaveCluster; 1029 break; 1030 default: 1031 assert( disableAction == DisableAction.LEAVE_CLUSTER_FORCE ); 1032 e = DEDisableAction.LeaveClusterForce; 1033 break; 1034 } 1035 return e; 1036 } 1037 1038 // 1039 // Utility function to map ReplicaNode.ReplicationType to DEReplicationType 1040 // 1041 static DEReplicationType mapReplicationType( 1042 final ReplicaNode.ReplicationType replicationType) 1043 { 1044 DEReplicationType rt; 1045 1046 switch (replicationType) 1047 { 1048 case SYNCHRONOUS: 1049 rt = DEReplicationType.DataSynchronous; 1050 break; 1051 default: 1052 assert( replicationType == 1053 ReplicaNode.ReplicationType.ASYNCHRONOUS ); 1054 rt = DEReplicationType.DataAsynchronous; 1055 break; 1056 } 1057 return rt; 1058 } 1059 1060 // 1061 // Utility function to map DEReplicationType to ReplicaNode.ReplicationType 1062 // 1063 static ReplicaNode.ReplicationType mapDEReplicationType( 1064 final DEReplicationType rt) 1065 { 1066 ReplicaNode.ReplicationType replicationType; 1067 1068 switch (rt) 1069 { 1070 case DataSynchronous: 1071 replicationType = ReplicaNode.ReplicationType.SYNCHRONOUS; 1072 break; 1073 default: 1074 assert( rt == DEReplicationType.DataAsynchronous ); 1075 replicationType = ReplicaNode.ReplicationType.ASYNCHRONOUS; 1076 break; 1077 } 1078 return replicationType; 1079 } 1080 1081 // 1082 // Utility function to map SparseAudit to DESparsePartitionAudit 1083 // 1084 static DESparsePartitionAudit mapSparseAudit( 1085 final Partition.Properties.SparseAudit sparseAudit) 1086 { 1087 DESparsePartitionAudit spa; 1088 1089 switch (sparseAudit) 1090 { 1091 case VERIFY_NODE_LIST: 1092 spa = DESparsePartitionAudit.VerifyNodeList; 1093 break; 1094 default: 1095 assert( sparseAudit == 1096 Partition.Properties.SparseAudit.IGNORE_NODE_LIST ); 1097 spa = DESparsePartitionAudit.IgnoreNodeList; 1098 break; 1099 } 1100 return spa; 1101 } 1102 1103 // 1104 // Utility function to map DESparsePartitionAudit to SparseAudit 1105 // 1106 static Partition.Properties.SparseAudit mapDESparseAudit( 1107 final DESparsePartitionAudit sparseAudit) 1108 { 1109 Partition.Properties.SparseAudit spa; 1110 1111 switch (sparseAudit) 1112 { 1113 case VerifyNodeList: 1114 spa = Partition.Properties.SparseAudit.VERIFY_NODE_LIST; 1115 break; 1116 default: 1117 assert( sparseAudit == DESparsePartitionAudit.IgnoreNodeList ); 1118 spa = Partition.Properties.SparseAudit.IGNORE_NODE_LIST; 1119 break; 1120 } 1121 return spa; 1122 } 1123 1124 // 1125 // Utility function to map ReplicaAudit to DEReplicaAudit 1126 // 1127 static DEReplicaAudit mapReplicaAudit( 1128 final Partition.Properties.ReplicaAudit replicaAudit) 1129 { 1130 DEReplicaAudit ra; 1131 1132 switch (replicaAudit) 1133 { 1134 case IGNORE_REPLICA: 1135 ra = DEReplicaAudit.ReplicaIgnore; 1136 break; 1137 case DISCARD_REPLICA: 1138 ra = DEReplicaAudit.ReplicaDiscard; 1139 break; 1140 default: 1141 assert( replicaAudit == Partition.Properties.ReplicaAudit.WAIT_ACTIVE ); 1142 ra = DEReplicaAudit.ReplicaWaitActive; 1143 break; 1144 } 1145 return ra; 1146 } 1147 1148 // 1149 // Utility function to map DEReplicaAudit to ReplicaAudit 1150 // 1151 static Partition.Properties.ReplicaAudit mapDEReplicaAudit( 1152 final DEReplicaAudit replicaAudit) 1153 { 1154 Partition.Properties.ReplicaAudit ra; 1155 1156 switch (replicaAudit) 1157 { 1158 case ReplicaIgnore: 1159 ra = Partition.Properties.ReplicaAudit.IGNORE_REPLICA; 1160 break; 1161 case ReplicaDiscard: 1162 ra = Partition.Properties.ReplicaAudit.DISCARD_REPLICA; 1163 break; 1164 default: 1165 assert( replicaAudit == DEReplicaAudit.ReplicaWaitActive ); 1166 ra = Partition.Properties.ReplicaAudit.WAIT_ACTIVE; 1167 break; 1168 } 1169 return ra; 1170 } 1171 1172 // 1173 // Utility function to map RemoteEnableAction to DERemoteEnableAction 1174 // 1175 static DERemoteEnableAction mapRemoteEnableAction( 1176 final Partition.Properties.RemoteEnableAction remoteEnableAction) 1177 { 1178 DERemoteEnableAction rea; 1179 1180 switch (remoteEnableAction) 1181 { 1182 case ENABLE_PARTITION: 1183 rea = DERemoteEnableAction.EnablePartition; 1184 break; 1185 default: 1186 assert( remoteEnableAction == 1187 Partition.Properties.RemoteEnableAction.LEAVE_DISABLED ); 1188 rea = DERemoteEnableAction.LeaveDisabled; 1189 break; 1190 } 1191 return rea; 1192 } 1193 1194 // 1195 // Utility function to map DERemoteEnableAction to RemoteEnableAction 1196 // 1197 static Partition.Properties.RemoteEnableAction mapDERemoteEnableAction( 1198 final DERemoteEnableAction remoteEnableAction) 1199 { 1200 Partition.Properties.RemoteEnableAction rea; 1201 1202 switch (remoteEnableAction) 1203 { 1204 case EnablePartition: 1205 rea = Partition.Properties.RemoteEnableAction.ENABLE_PARTITION; 1206 break; 1207 default: 1208 assert( remoteEnableAction == DERemoteEnableAction.LeaveDisabled ); 1209 rea = Partition.Properties.RemoteEnableAction.LEAVE_DISABLED; 1210 break; 1211 } 1212 return rea; 1213 } 1214 1215 // 1216 // Utility function to map MapperProperties.Audit to DEMapperAudit 1217 // 1218 private static DEMapperAudit mapAudit( 1219 final PartitionMapper.Properties.Audit audit) 1220 { 1221 DEMapperAudit e; 1222 1223 switch (audit) 1224 { 1225 case VERIFY_PARTITIONING: 1226 case VERIFY_PARTIONING: 1227 e = DEMapperAudit.MapperVerify; 1228 break; 1229 case VERIFY_DEFER_INSTALL: 1230 e = DEMapperAudit.MapperDefer; 1231 break; 1232 default: 1233 assert( audit == PartitionMapper.Properties.Audit.IGNORE_PARTITIONING ); 1234 e = DEMapperAudit.MapperIgnore; 1235 break; 1236 } 1237 return e; 1238 } 1239 1240 // 1241 // Validates a given object is managed. 1242 // 1243 private static void checkManagedObject(Object obj) 1244 { 1245 if (!ManagedObject.isManaged(obj)) 1246 { 1247 throw new ManagedClassError("class " + obj.getClass().getName() + 1248 " is not a Managed class."); 1249 } 1250 } 1251 1252 // 1253 // Validates a given class is managed. 1254 // 1255 private static void checkManagedClass(Class<?> klass) 1256 { 1257 if (!ManagedObject.isManagedClass(klass)) 1258 { 1259 throw new ManagedClassError("class " + klass.getName() + 1260 " is not a Managed class."); 1261 } 1262 } 1263 1264 // 1265 // Never should be called 1266 // 1267 private PartitionManager() 1268 { 1269 } 1270 1271 // 1272 // Private logger class 1273 // 1274 private static class DistributionLogger extends Logger 1275 { 1276 DistributionLogger() 1277 { 1278 super("com.kabira.platform.highavailability", null); 1279 m_enabled = false; 1280 } 1281 1282 public void setLevel(Level level) 1283 { 1284 super.setLevel(level); 1285 if (level != null) 1286 { 1287 m_enabled = (level.intValue() <= Level.FINEST.intValue()) 1288 ? true : false; 1289 if (Transaction.isActive()) 1290 { 1291 debugTrace(); 1292 } 1293 else 1294 { 1295 new Transaction("DistributionLogger") 1296 { 1297 @Override 1298 protected void run() throws Rollback 1299 { 1300 debugTrace(); 1301 } 1302 }.execute(); 1303 } 1304 } 1305 } 1306 1307 private boolean m_enabled; 1308 1309 private void debugTrace() 1310 { 1311 DEPartitionManager pm = new DEPartitionManager(); 1312 pm.debugTrace(m_enabled); 1313 } 1314 1315 @Override 1316 public String toString() 1317 { 1318 StringBuilder sb = 1319 new StringBuilder("DistributionLogger: debug tracing is "); 1320 sb.append(m_enabled ? "enabled" : "disabled"); 1321 return sb.toString(); 1322 } 1323 } 1324 1325 private static DistributionLogger m_logger; 1326 1327 // 1328 // Create and register logger and DistributedQuery instance at 1329 // start of world. 1330 // 1331 static 1332 { 1333 initialize(); 1334 } 1335} 1336