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