001//
002// Name
003//  $RCSfile: Partition.java,v $
004// 
005// Copyright
006//  Copyright 2010-2014 Cloud Software Group, Inc. ALL RIGHTS RESERVED. 
007//  Cloud Software Group, Inc. Confidential Information
008//
009// History
010//  $Revision: 1.1.2.30 $ $Date: 2015/01/27 03:10:25 $
011//
012package com.kabira.platform.highavailability;
013
014import com.kabira.platform.ResourceUnavailableException;
015import com.kabira.platform.disteng.DEPartitionManager;
016import com.kabira.platform.disteng.DENodeMismatch;
017import com.kabira.platform.disteng.DEReplicationType;
018import com.kabira.platform.disteng.DEProperties;
019import com.kabira.platform.disteng.DEPartitionStats;
020import com.kabira.platform.disteng.DESparsePartitionAudit;
021import com.kabira.platform.disteng.DERemoteEnableAction;
022import com.kabira.platform.disteng.DEPartitionBroadcast;
023import java.util.Date;
024
025/**
026 * The Partition class. This non-managed class contains a snapshot of a
027 * shared memory partition. Direct access to the Partition instance in
028 * shared memory is not allowed to avoid transaction locking issues.
029 * <p>
030 * Note that partitions are only visible on their active and replica
031 * nodes, not all nodes in the cluster.
032 */
033public final class Partition
034{
035    /**
036     * The Partition states
037     */
038    public enum State
039    {
040        /** The partition has been defined, but not enabled */
041        INITIAL,
042        /** The partition is active */
043        ACTIVE,
044        /**
045         * The partition definition is being updated. Entered when 
046         * partition membership (i.e. the objects associated with the
047         * partition) is being updated to split or merge partitions.
048         */
049        UPDATING,
050        /**
051         * The partition owner is being updated. Each object in the
052         * partition is sent to the new owner and the location definition
053         * of all instances are updated.
054         */
055        MIGRATING,
056        /**
057         * The partition replicas are being updated. Each object in the
058         * partition is sent to the new replica list.
059         */
060        REPLICATING,
061        /** 
062         * The partition is not active because all associated nodes have
063         * failed, the node was taken off-line because it is in a
064         * non-quorum state, or the node was restarted.
065         */
066        UNAVAILABLE
067    }
068
069    /**
070     * The Partition status
071     */
072    public enum Status
073    {
074        /** The partition has been disabled */
075        LOCAL_DISABLED,
076        /** The partition has been defined, but not enabled */
077        LOCAL_DEFINED,
078        /** The partition has been defined and enabled. */
079        LOCAL_ENABLED,
080        /** The partition has been defined remotely. */
081        REMOTE_DEFINED,
082        /**
083         * The partition has been defined and enabled on a remote node
084         * but not defined on this node.
085         */
086        REMOTE_ENABLED
087    }
088
089    /**
090     * The Partition properties used when defining a partition
091     */
092    public final static class Properties
093    {
094        /**
095         * An enumeration of the possible audits that can be taken when
096         * enabling a sparse Partition.
097         * <p>
098         * A sparse Partition is a partition that doesn't have the local
099         * node as the active node, or in the replica node list. When
100         * enabled, no data migration takes place; instead the node list
101         * is verified against the node list that is present on the
102         * active node to verify they match.
103         * <p>
104         * When restoring multiple nodes, it may be necessary to disable
105         * the auditing of node lists, since the active node may have a
106         * node list that is the result of a previous failover.
107         * <p>
108         * The default audit is defined by {@link #DefaultSparseAudit},
109         */
110        public enum SparseAudit
111        {
112            /**
113             * Verify that the defined node list matches the active node's
114             * node list.
115             */
116            VERIFY_NODE_LIST,
117            /**
118             * Do not perform any verification of node lists.
119             */
120            IGNORE_NODE_LIST
121        }
122
123        /**
124         * An enumeration of the possible actions to be taken when a remote
125         * node enables a Partition.
126         * <p>
127         * When a remote node enables a partition, it will enable the
128         * partition on all nodes in the node list, and update the partition
129         * status and state. This is done even on nodes that have explicitly
130         * disabled the partition. This behaviour can be changed by setting
131         * the RemoteEnableAction to LEAVE_DISABLED. Note that this only
132         * controls the behaviour on the local node, it does not affect
133         * partitions defined on remote nodes.
134         * <p>
135         * The default action is defined by {@link #DefaultRemoteEnableAction},
136         */
137        public enum RemoteEnableAction
138        {
139            /**
140             * Allow remote nodes to enable the partition on the local node.
141             */
142            ENABLE_PARTITION,
143            /**
144             * Do not allow remote nodes to enable the partition on the
145             * local node if it has been explicitly disabled.
146             */
147            LEAVE_DISABLED
148        }
149
150        /**
151         * Default constructor.
152         */
153        public Properties()
154        {
155            m_restoreFromNode = null;
156            m_forceReplication = false;
157            m_objectsLockedPerTransaction = DefaultObjectsLockedPerTransaction;
158            m_numberOfThreads = DefaultNumberOfThreads;
159            m_sparseAudit = DefaultSparseAudit;
160            m_remoteEnableAction = DefaultRemoteEnableAction;
161            m_broadcastUpdates = DEPartitionBroadcast.BroadcastDefault;
162        }
163
164        /**
165         * Define the node that the partition should be restored from.
166         * <p>
167         * When this property is set, the partition defined on the given
168         * remote node is loaded to the local node. This should be done
169         * when restoring a node from a split-brain situation, where
170         * <b>nodeName</b> is the node in the cluster where all objects
171         * should be preserved, and the local node is the node being
172         * restored. Any conflicts during restore will preserve the
173         * objects on <b>nodeName</b>, and remove the conflicting objects
174         * on the local node.
175         * <p>
176         * A restore is needed when multiple nodes are currently the active
177         * node for a partition in a cluster due to a split-brain scenario.
178         * In this case, the application needs to decide which active node
179         * will be the node where the objects are preserved during a
180         * restore. Note that the <b>nodeName</b> does not necessarily have
181         * to be the node which becomes the partition's active node after the
182         * restore completes.
183         * <p>
184         * The actual restore of the partition is done in the
185         * enable() or enablePartitions() method when the JOIN_CLUSTER_RESTORE
186         * EnableAction is used. If any other EnableAction is used,
187         * object data isn't preserved, and no restoration of partition
188         * objects is done.
189         * <p>
190         * If restoreFromNode isn't set after a split-brain scenario, the
191         * runtime will perform a cluster wide broadcast to find the
192         * current active node, and use that node to restore instances in
193         * the partition. If multiple active nodes are found, the first
194         * responder is chosen.
195         * <p>
196         * @param nodeName The remote node to use when restoring the
197         *  partition's objects.
198         *
199         * @exception IllegalArgumentException
200         * The nodeName was empty.
201         * @see PartitionManager#definePartition(String,
202         *              Partition.Properties, String, ReplicaNode [])
203         * @see PartitionManager.EnableAction
204         */
205        public void restoreFromNode(String nodeName)
206            throws IllegalArgumentException
207        {
208            if (nodeName.isEmpty())
209            {
210                throw new IllegalArgumentException(
211                    "nodeName cannot be empty");
212            }
213            m_restoreFromNode = nodeName;
214        }
215
216        /**
217         * Get the current restoreFromNode property value.
218         * @return String containing value.
219         */
220        public final String getRestoreFromNode()
221        {
222            return m_restoreFromNode;
223        }
224
225        /**
226         * Determine how objects are replicated during a migrate of
227         * an object partition.
228         * <p>
229         * When set to true, a migrate() or
230         * definePartition()/enablePartitions() will force the copy of
231         * partitioned objects to all pre-existing replica nodes. The
232         * default value for this property is false, objects are only
233         * copied to new replicas as they are added since the objects
234         * should already exist on the pre-existing replica nodes.
235         * <p>
236         * Normally, a migrate will skip the replication of objects to
237         * pre-existing nodes in the partition's replica node list. This
238         * allows applications to incrementally add replica nodes without
239         * having to copy the objects to replicas that already exist in
240         * the partition. However, if one or more replicas have gone
241         * offline, or were not discovered when the partition was first
242         * enabled, this property can be set to insure that objects are
243         * pushed to all replicas in the cluster.
244         * <p>
245         * Warning: This is performance hostile, and should only be done
246         * if the replica can't be manually taken offline and restored.
247         * <p>
248         * The value passed into definePartition() is stored and used in
249         * failover. The value passed to migrate() overrides
250         * the value passed to definePartition().
251         *
252         * @param enabled If true, force the copy of objects to all replicas
253         *  when a migrate() or enablePartition() is executed.
254         *
255         */
256        public void forceReplication(boolean enabled)
257        {
258            m_forceReplication = enabled;
259        }
260
261        /**
262         * Get the current forceReplication property value.
263         * @return boolean containing value.
264         */
265        public final boolean getForceReplication()
266        {
267            return m_forceReplication;
268        }
269
270        /**
271         * Define the number of objects locked in a transaction when performing
272         * a migrate() or update().
273         * <p>
274         * When distribution performs a migrate() or update(), or when it
275         * performs a migrate() as part of failover, the work is done in
276         * units of work defined by objectsLockedPerTransaction. This
277         * allows applications to concurrently run while the work is in
278         * progress, otherwise all partitioned instances would be locked
279         * in the calling transaction, preventing application code from
280         * establishing a write lock on instances on the active node, or
281         * establishing a read lock on instances on replica nodes.
282         * <p>
283         * The default is defined by {@link #DefaultObjectsLockedPerTransaction}
284         * <p>
285         * If objectsLockedPerTransaction is set to zero, all instances will
286         * be processed in the caller's transaction.
287         * <p>
288         * If the calling transaction has one or more partitioned
289         * instances locked in the transaction,
290         * objectsLockedPerTransaction is ignored, and all work is done
291         * in the caller's transaction. To insure that migrate() or update()
292         * minimizes the number of locks taken, they should be run in separate
293         * transactions that have no partitioned instances locked. 
294         * <p>
295         * The value passed into definePartition() is stored and used in
296         * failover. The value passed to migrate() and update() override
297         * the value passed to definePartition().
298         * 
299         * @param objectsLockedPerTransaction Number of objects locked
300         *  per transaction when sending data to remote nodes.
301         * @exception IllegalArgumentException The objectsLockedPerTransaction
302         *  value was negative.
303         */
304        public void setObjectsLockedPerTransaction(
305            long objectsLockedPerTransaction)
306        {
307            if (objectsLockedPerTransaction < 0)
308            {
309                throw new IllegalArgumentException(
310                    "invalid objectsLockedPerTransaction: " +
311                    objectsLockedPerTransaction);
312            }
313            m_objectsLockedPerTransaction = objectsLockedPerTransaction;
314        }
315
316        /**
317         * Get the current objectsLockedPerTransaction property value.
318         * @return long containing value.
319         */
320        public final long getObjectsLockedPerTransaction()
321        {
322            return m_objectsLockedPerTransaction;
323        }
324
325        /**
326         * Define the number of threads used when performing a migrate().
327         * <p>
328         * When distribution performs a migrate(), either explicitly or
329         * as part of the enablePartitions() call, the work is done in
330         * multiple threads in order to scale to the number of CPUs
331         * available in the system. 
332         * <p>
333         * The default is defined by {@link #DefaultNumberOfThreads}
334         * <p>
335         * If the number of partitioned instances is less that
336         * objectsLockedPerTransaction, only one thread will be used.
337         * <p>
338         * If objectsLockedPerTransaction is zero, or the calling
339         * transaction has one or more partitioned instances locked in
340         * the transaction, numberOfThreads is ignored, and all work is
341         * done in the caller's transaction.
342         * <p>
343         * @param numberOfThreads Number of threads to use
344         *  when sending data to remote nodes.
345         * @exception IllegalArgumentException The numberOfThreads
346         *  value was less than one.
347         */
348        public void setNumberOfThreads(long numberOfThreads)
349        {
350            if (numberOfThreads < 1)
351            {
352                throw new IllegalArgumentException(
353                    "invalid numberOfThreads: " + numberOfThreads);
354            }
355            m_numberOfThreads = numberOfThreads;
356        }
357
358        /**
359         * Get the current numberOfThreads property value.
360         * @return long containing value.
361         */
362        public final long getNumberOfThreads()
363        {
364            return m_numberOfThreads;
365        }
366
367        /**
368         * Set the sparseAudit property value.
369         * <p>
370         * The default is defined by {@link #DefaultSparseAudit}
371         *
372         * @param sparseAudit What auditing should be done.
373         *
374         */
375        public void setSparseAudit(final SparseAudit sparseAudit)
376        {  
377            m_sparseAudit = sparseAudit;
378        }
379
380        /**
381         * Get the current sparseAudit property value.
382         * @return SparseAudit enum.
383         */
384        public final SparseAudit getSparseAudit()
385        {
386            return m_sparseAudit;
387        }
388
389        /**
390         * Set the remoteEnableAction property value.
391         * <p>
392         * The default is defined by {@link #DefaultRemoteEnableAction}
393         *
394         * @param remoteEnableAction The action to take when remote nodes enable
395         * a partition.
396         *
397         */
398        public void setRemoteEnableAction(final RemoteEnableAction remoteEnableAction)
399        {  
400            m_remoteEnableAction = remoteEnableAction;
401        }
402
403        /**
404         * Get the current remoteEnableAction property value.
405         * @return RemoteEnableAction enum.
406         */
407        public final RemoteEnableAction getRemoteEnableAction()
408        {
409            return m_remoteEnableAction;
410        }
411
412        /**
413         * Set the broadcastUpdates property value.
414         * <p>
415         * By default, this property is enabled. Updates to the partition's 
416         * definition are broadcast to all nodes in the cluster so that
417         * the partition definition is consistent across the cluster.
418         * <p>
419         * Warning: The ability to disable this property is only provided
420         * to support the testing of multi-master scenarios.
421         * Disabling this property for any other reason is not recommended.
422         *
423         * @param enabled If true, broadcast partition definition updates
424         *  to all nodes in the cluster.
425         */
426        public void broadcastUpdates(boolean enabled)
427        {
428            m_broadcastUpdates = (enabled == true)
429                ? DEPartitionBroadcast.BroadcastEnabled 
430                : DEPartitionBroadcast.BroadcastDisabled;
431        }
432
433        /**
434         * Get the current broadcastUpdates property value.
435         * @return boolean containing value.
436         */
437        public final boolean getBroadcastUpdates()
438        {
439            // Map both BroadcastDefault and BroadcastEnabled to true.
440            return (m_broadcastUpdates == DEPartitionBroadcast.BroadcastDisabled)
441                ? false : true;
442        }
443
444        //
445        // package private constructor
446        //
447        Properties(
448            boolean forceReplication,
449            long objectChunks,
450            long numThreads,
451            SparseAudit sparseAudit,
452            RemoteEnableAction remoteEnableAction,
453            DEPartitionBroadcast broadcastUpdates)
454        {
455            m_restoreFromNode = null;
456            m_forceReplication = forceReplication;
457            m_objectsLockedPerTransaction = objectChunks;
458            m_numberOfThreads = numThreads;
459            m_sparseAudit = sparseAudit;
460            m_remoteEnableAction = remoteEnableAction;
461            m_broadcastUpdates = broadcastUpdates;
462        }
463
464        String m_restoreFromNode;
465        boolean m_forceReplication;
466        long m_objectsLockedPerTransaction;
467        long m_numberOfThreads;
468        SparseAudit m_sparseAudit;
469        RemoteEnableAction m_remoteEnableAction;
470        DEPartitionBroadcast m_broadcastUpdates;
471    }
472
473    /**
474     * Partition statistics
475     * <p>
476     * Partition statistics are captured for the local node only, any
477     * operations done on remote nodes are not reflected in the stats. As
478     * an example, if the local node is a replica, and the node executes
479     * 10 object creates, the createCount will be set to 10. If objects
480     * are created on the active node, or on other replica nodes, the
481     * createCount will not be updated.
482     * <p>
483     * All partition statistics are reset each time an engine containing
484     * the highavailability component is restarted. They do not persist
485     * across engine restarts.
486     */
487    public final static class Statistics
488    {
489        /**
490         * The number of instances created in this partition.
491         */
492        public long createCount;
493        /**
494         * The number of updates that have been applied to instance in this
495         * partition.
496         */
497        public long updateCount;
498        /**
499         * The number of instances deleted in this partition.
500         */
501        public long deleteCount;
502        /**
503         * The number of synchronous create failures that occurred in
504         * this partition.
505         */
506        public long createFailures;
507        /**
508         * The number of synchronous update failures that occurred in
509         * this partition.
510         */
511        public long updateFailures;
512        /**
513         * The number of synchronous delete failures that occurred in
514         * this partition.
515         */
516        public long deleteFailures;
517        /**
518         * The number of asynchronous create failures that occurred in
519         * this partition.
520         */
521        public long asyncCreateFailures;
522        /**
523         * The number of asynchronous update failures that occurred in
524         * this partition.
525         */
526        public long asyncUpdateFailures;
527        /**
528         * The number of asynchronous delete failures that occurred in
529         * this partition.
530         */
531        public long asyncDeleteFailures;
532
533        // package private constructor
534        Statistics(DEPartitionStats stats)
535        {
536            this.createCount = stats.createCount;
537            this.updateCount = stats.updateCount;
538            this.deleteCount = stats.deleteCount;
539            this.createFailures = stats.createFailures;
540            this.updateFailures = stats.updateFailures;
541            this.deleteFailures = stats.deleteFailures;
542            this.asyncCreateFailures = stats.asyncCreateFailures;
543            this.asyncUpdateFailures = stats.asyncUpdateFailures;
544            this.asyncDeleteFailures = stats.asyncDeleteFailures;
545        }
546        // private constructor
547        private Statistics() { }
548    }
549
550    /** Index to the active node in the nodes array */
551    public final static int     ActiveNodeIndex = 0;
552
553    /**
554     * The default number of objects locked per transaction when
555     * performing a migrate or update.
556     */
557    public final static long    DefaultObjectsLockedPerTransaction = 1000;
558
559    /**
560     * The default number of threads to use when performing a migrate.
561     */
562    public final static long    DefaultNumberOfThreads = 1;
563
564    /**
565     * The default audit used for sparse partitions.
566     * The value is {@link Properties.SparseAudit#VERIFY_NODE_LIST}
567     */
568    public final static Properties.SparseAudit DefaultSparseAudit =
569                                    Properties.SparseAudit.VERIFY_NODE_LIST;
570
571    /**
572     * The default action used for remote partition enables.
573     * The value is {@link Properties.RemoteEnableAction#ENABLE_PARTITION}
574     */
575    public final static Properties.RemoteEnableAction DefaultRemoteEnableAction =
576                                    Properties.RemoteEnableAction.ENABLE_PARTITION;
577
578    /**
579     * Get the partition name.
580     * @return Partition name.
581     */
582    public final String getName()
583    {
584        return m_name;
585    }
586
587    /**
588     * Get the active node for the partition.
589     * @return Active node name.
590     */
591    public final String getActiveNode()
592    {
593        assert( m_nodes.length >= 1 );
594        return m_nodes[ActiveNodeIndex];
595    }
596
597    /**
598     * Get the replica node list for the partition.
599     * @return Array of replica node names.
600     */
601    public final String [] getReplicaNodes()
602    {
603        assert( m_nodes.length >= 1 );
604        String [] replicas = new String[m_nodes.length - 1];
605        for (int i = 0; i < m_nodes.length - 1; i++)
606        {
607            replicas[i] = m_nodes[i + 1];
608        }
609        return replicas;
610    }
611
612    /**
613     * Get the complete node list for the partition.
614     * <p>
615     * The node list is a String array of node names, with the active
616     * node being at ActiveNodeIndex, followed by a prioritized list of
617     * replica nodes.
618     * @return Array of node names.
619     */
620    public final String [] getNodeList()
621    {
622        return m_nodes;
623    }
624
625    /**
626     * Get the current state for the partition.
627     * @return Current state of partition.
628     */
629    public final State getCurrentState()
630    {
631        return m_currentState;
632    }
633
634    /**
635     * Get the current status for the partition.
636     * @return Current status of partition.
637     */
638    public final Status getCurrentStatus()
639    {
640        return m_currentStatus;
641    }
642
643    /**
644     * Get the ReplicaNode instance for the given replica node index.
645     * @param idx Index into the ReplicaNode array.
646     * @return ReplicaNode instance.
647     * @exception IndexOutOfBoundsException
648     *  The idx isn't valid.
649     */
650    public final ReplicaNode getReplicaNode(int idx)
651        throws IndexOutOfBoundsException
652    {
653        // m_nodes[0] is active node, so we need to disallow -1
654        if (idx < 0) throw new IndexOutOfBoundsException(Integer.toString(idx));
655        return new ReplicaNode(m_nodes[idx + 1],
656            PartitionManager.mapDEReplicationType(
657                m_replicationTypes[idx + 1]));
658    }
659
660    /**
661     * Get the ReplicaNode instance for the given replica node name.
662     * @param nodeName Name of replica node.
663     * @return ReplicaNode instance, or null if not found.
664     * @exception IllegalArgumentException The nodeName was invalid.
665     */
666    public final ReplicaNode getReplicaNode(final String nodeName)
667        throws IllegalArgumentException
668    {
669        if (nodeName.isEmpty())
670        {
671            throw new IllegalArgumentException("nodeName cannot be empty");
672        }
673        for (int idx = 1; idx < m_nodes.length; idx++)
674        {
675            if (m_nodes[idx].equals(nodeName))
676            {
677                return getReplicaNode(idx - 1);
678            }
679        }
680        return null;
681    }
682
683    /**
684     * Get the last time the state for the partition was updated
685     * @return Date of last state change for partition.
686     */
687    public final Date getLastStateChangeTime()
688    {
689        return m_lastUpdated;
690    }
691
692    /**
693     * Get the properties currently defined for the partition.
694     * <p>
695     * The restoreFromNode property is not stored in the runtime since it
696     * has no meaning outside of the initial definePartition(). So this method
697     * will return a Properties instance with a null restoreFromNode value.
698     *
699     * @return Properties instance for partition.
700     */
701    public final Properties getProperties()
702    {
703        return new Properties(m_forceReplication, m_objectChunks,
704                m_numThreads, m_sparseAudit, m_remoteEnableAction, m_broadcastUpdates);
705    }
706
707    /**
708     * Get the statistics for the partition.
709     *
710     * @return A Statistics instance for the partition.
711     */
712    public final Statistics getStatistics()
713    {
714        DEPartitionManager pm = new DEPartitionManager();
715        return new Statistics(pm.getPartitionStats(m_name));
716    }
717
718    /**
719     * Clear the statistics for the partition.
720     */
721    public final void clearStatistics()
722    {
723        DEPartitionManager pm = new DEPartitionManager();
724        pm.clearPartitionStats(m_name);
725    }
726
727    /**
728     * Use {@link #update(Properties)} instead.
729     */
730    @Deprecated
731    public final void update() throws NodeMismatch, NotActiveNode
732    {
733        update(null);
734    }
735
736    /**
737     * Update the partition.
738     * <p>
739     * This method is used to re-partition all instances that exist in a
740     * partition. For each instance in the partition, the {@link
741     * PartitionMapper} defined for that type is accessed, and the
742     * instance re-assigned to the partition that the {@link
743     * PartitionMapper#getPartition} method returns. The update() method
744     * must be called on the current active node for the partition.
745     * <p>
746     * To split a partition, the applications should create the new
747     * partition, install a new PartitionMapper for all types managed by
748     * the partition, and call the update() method for the existing
749     * partition.
750     * <p>
751     * To merge two or more partitions, the applications should install a
752     * new PartitionMapper for all types managed by the partition(s), and
753     * call the update() method for all the partitions that need to
754     * be merged.
755     * <p>
756     * It is important that all partitions returned when executing the
757     * PartitionMapper's getPartition() call for a type all contain
758     * identical node lists. If the partition returned contains a
759     * different node list than this partition, a NodeMismatch exception
760     * is thrown, and the update() terminates. To fix this, perform a
761     * migrate() on all partitions that will be split or merged to insure
762     * that they have identical node lists before performing an update().
763     *
764     * @param partitionProperties Optional properties for the partition.
765     *  If null, the default property values are used.
766     * 
767     * @exception NodeMismatch
768     *  The re-partitioning of instances was done using partitions that
769     *  have different node lists.
770     * @exception NotActiveNode
771     *  The current node is not the active node for the partition.
772     */
773    public final void update(final Properties partitionProperties)
774            throws NodeMismatch, NotActiveNode
775    {
776        DEProperties props = copyDEProperties(partitionProperties);
777
778        PartitionManager.updatePartition(m_name, props);
779    }
780
781    /**
782     * Use {@link #migrate(Properties, String, ReplicaNode [])} instead.
783     * @param nodes An ordered list of nodes for the partition.
784     */
785    @Deprecated
786    public final void migrate(String [] nodes)
787        throws NotActiveNode, IllegalArgumentException
788    {
789        PartitionManager.validateNodeList(nodes);
790        DEReplicationType [] drl = new DEReplicationType[nodes.length];
791        for (int i = 0; i < drl.length; i++)
792        {
793            drl[i] = DEReplicationType.DataSynchronous;
794        }
795
796        DEProperties props = defaultDEProperties();
797
798        PartitionManager.migratePartition(m_name, props, nodes, drl);
799    }
800
801    /**
802     * Migrate the partition.
803     * <p>
804     * This method is used to migrate all instances that exist in a
805     * partition. For each instance in the partition, the object is
806     * migrated as needed to all nodes in the replicas array, and to the
807     * new activeNode if it is different from the current active node. This
808     * method must be called on the currently active node for the partition.
809     * <p>
810     * Any properties passed in only apply to the current migrate() command,
811     * properties passed into definePartition() are saved and used during
812     * failover. 
813     * 
814     * @param partitionProperties Optional properties for the partition.
815     *  If null, the default property values are used.
816     * @param activeNode Active node after migrate completes.
817     * @param replicas An ordered list of replica nodes for the partition.
818     *  Should be passed in as a null instance or a zero length array if
819     *  no replicas exists.
820     *
821     * @exception NotActiveNode
822     *  The current node is not the active node for the partition.
823     * @exception IllegalArgumentException
824     * The activeNode or replica array was invalid.
825     */
826    public final void migrate(
827        final Properties partitionProperties,
828        String activeNode,
829        ReplicaNode [] replicas)
830            throws NotActiveNode, IllegalArgumentException
831    {
832        PartitionManager.validateReplicaList(replicas);
833
834        int nodeLength = (replicas == null) ? 1 : replicas.length + 1;
835        String [] nodes = new String[nodeLength];
836
837        nodes[0] = activeNode;
838        if (replicas != null )
839        {
840            for (int i = 0; i < replicas.length; i++)
841            {
842                assert( i + 1 < nodes.length );
843                nodes[i + 1] = replicas[i].nodeName;
844            }
845        }
846        PartitionManager.validateNodeList(nodes);
847
848        DEReplicationType [] drl = new DEReplicationType[nodes.length];
849        drl[0] = DEReplicationType.DataSynchronous;
850        if (replicas != null)
851        {
852            for (int i = 0; i < replicas.length; i++)
853            {
854                assert( i + 1 < nodes.length );
855                drl[i + 1] = PartitionManager.mapReplicationType(
856                    replicas[i].replicationType);
857            }
858        }
859        boolean forceReplication = (partitionProperties != null)
860            ? partitionProperties.m_forceReplication : false;
861
862        DEProperties props = copyDEProperties(partitionProperties);
863
864        PartitionManager.migratePartition(m_name, props, nodes, drl);
865    }
866
867    /**
868     * Enable high availability for this partition.
869     * <p>
870     * This method is used to enable this partition. If the partition is
871     * already in the <b>ACTIVE</b> state, no action is taken.
872     * This method blocks until all required object migration
873     * is complete.
874     *
875     * @param enableAction The action to take when enabling partitions.
876     *
877     * @exception ResourceUnavailableException
878     *  The minimum number of nodes needed to establish a quorum has not been
879     *  seen, or the partition has not been defined.
880     * @exception NodeMismatch
881     *  The partition does not have the local node in the node list, and the
882     *  active node for the partition has a different node list.
883     * @see PartitionManager#enablePartitions(PartitionManager.EnableAction)
884     */
885    public final void enable(
886        final PartitionManager.EnableAction enableAction)
887            throws NodeMismatch, ResourceUnavailableException
888    {
889        DEPartitionManager pm = new DEPartitionManager();
890        try
891        {
892            pm.enablePartition(m_name,
893                PartitionManager.mapEnable(enableAction));
894        }
895        catch (DENodeMismatch ex)
896        {
897            throw new NodeMismatch(ex.errMsg);
898        }
899    }
900
901    /**
902     * Disable high availability for this partition.
903     * <p>
904     * This method is used to remove the local node from the node list 
905     * of this partition, and move the partition state to <b>UNAVAILABLE</b>.
906     * If called multiple times, the additional calls have no effect.
907     * <p>
908     * If the local node does not exist in the partition, no action is taken.
909     *
910     * @param disableAction  The action to take when disabling partitions.
911     *
912     * @exception ResourceUnavailableException
913     *  The partition would be in the <b>UNAVAILABLE</b> state after the
914     *  disable executes, not thrown if <b>LEAVE_CLUSTER_FORCE</b> is used.
915     * @see PartitionManager#disablePartitions(PartitionManager.DisableAction)
916     */
917    public final void disable(
918        final PartitionManager.DisableAction disableAction)
919            throws ResourceUnavailableException
920    {
921        DEPartitionManager pm = new DEPartitionManager();
922        pm.disablePartition(m_name, PartitionManager.mapDisable(disableAction));
923    }
924
925    /**
926     * Associated a notifier with the partition.
927     * <p>
928     * This method is used to associate a user defined {@link PartitionNotifier}
929     * instance with a partition.
930     * <p>
931     * PartitionNotifier instances are local to a node, this method
932     * should be executed on all nodes which need to determine if a
933     * partition state change occurs.
934     * <p>
935     * The same PartitionNotifier instance can be associated with multiple
936     * partitions. Multiple PartitionNotifier instances can be installed for
937     * a given Partition. When a state change occurs, all instances are
938     * executed, no order is guaranteed when executing the notifiers.
939     * To remove a notifier, the PartitionNotifier instance should be deleted.
940     * 
941     * @param partitionNotifier User defined notifier instance.
942     *
943     */
944    public final void setNotifier(PartitionNotifier partitionNotifier)
945    {
946        PartitionManager.setNotifier(m_name, partitionNotifier);
947    }
948
949    /**
950     * Returns the number of instances in this partition.
951     * <p>
952     * This method does not establish transaction locks on the
953     * partition or any of the individual objects in the partition.
954     * This means the number returned by cardinality() may 
955     * change even as observed within a single transaction.
956     * <p>
957     * Warning: This method requires a scan of all objects for each class in 
958     * the partition to determine the count, which is computationally
959     * expensive.
960     *
961     * @return Number of instances found.
962     */
963    public final long cardinality()
964    {
965        DEPartitionManager pm = new DEPartitionManager();
966        return pm.getCardinality(m_name);
967    }
968
969    /** The name of the partition */
970    private final String         m_name;
971
972    /** The list of nodes for a partition */
973    private final String []      m_nodes;
974
975    /** The list of nodes for a partition */
976    private final DEReplicationType [] m_replicationTypes;
977
978    /** The current state of the partition */
979    private final State          m_currentState;
980
981    /** The current status of the partition */
982    private final Status        m_currentStatus;
983
984    /** The time it was updated */
985    private final Date          m_lastUpdated;
986
987    /** stored property */
988    private final boolean       m_forceReplication;
989
990    /** stored property */
991    private final long          m_objectChunks;
992
993    /** stored property */
994    private final long          m_numThreads;
995
996    /** stored property */
997    private final Properties.SparseAudit m_sparseAudit;
998
999    /** stored property */
1000    private final Properties.RemoteEnableAction m_remoteEnableAction;
1001
1002    /** stored property */
1003    private final DEPartitionBroadcast   m_broadcastUpdates;
1004
1005    //
1006    // FIX THIS: Add stats attributes...
1007    //
1008
1009    //
1010    // Package private constructor
1011    //
1012    Partition(
1013        String name,
1014        String [] nodes,
1015        DEReplicationType [] replicationTypes,
1016        State currentState,
1017        Status currentStatus,
1018        Date lastUpdated,
1019        boolean forceReplication,
1020        long objectChunks,
1021        long numThreads,
1022        Properties.SparseAudit sparseAudit,
1023        Properties.RemoteEnableAction remoteEnableAction,
1024        DEPartitionBroadcast broadcastUpdates)
1025    {
1026        this.m_name = name;
1027        this.m_nodes = nodes;
1028        this.m_replicationTypes = replicationTypes;
1029        this.m_currentState = currentState;
1030        this.m_currentStatus = currentStatus;
1031        this.m_lastUpdated = lastUpdated;
1032        this.m_forceReplication = forceReplication;
1033        this.m_objectChunks = objectChunks;
1034        this.m_numThreads = numThreads;
1035        this.m_sparseAudit = sparseAudit;
1036        this.m_remoteEnableAction = remoteEnableAction;
1037        this.m_broadcastUpdates = broadcastUpdates;
1038    }
1039    //
1040    // Never should be called
1041    //
1042    private Partition()
1043    {
1044        this.m_name = null;
1045        this.m_nodes = null;
1046        this.m_replicationTypes = null;
1047        this.m_currentState = null;
1048        this.m_currentStatus = null;
1049        this.m_lastUpdated = null;
1050        this.m_forceReplication = false;
1051        this.m_objectChunks = 0;
1052        this.m_numThreads = 0;
1053        this.m_sparseAudit = null;
1054        this.m_remoteEnableAction = null;
1055        this.m_broadcastUpdates = DEPartitionBroadcast.BroadcastDefault;
1056    }
1057
1058    //
1059    // Copy properties to DEProperties.
1060    //
1061    static DEProperties copyDEProperties(Properties partitionProperties)
1062    {
1063        if (partitionProperties == null)
1064        {
1065            return defaultDEProperties();
1066        }
1067
1068        DEProperties props = new DEProperties();
1069
1070        props.forceReplication = partitionProperties.m_forceReplication;
1071        props.objectChunks =
1072            partitionProperties.m_objectsLockedPerTransaction;
1073        props.numThreads = partitionProperties.m_numberOfThreads;
1074        props.sparseAudit =
1075            PartitionManager.mapSparseAudit(partitionProperties.m_sparseAudit);
1076        props.remoteEnableAction =
1077            PartitionManager.mapRemoteEnableAction(partitionProperties.m_remoteEnableAction);
1078        props.restoreFromNode = partitionProperties.m_restoreFromNode;
1079        props.broadcastUpdates = partitionProperties.m_broadcastUpdates;
1080
1081        return props;
1082    }
1083
1084    //
1085    // Allocate and initialize default DEProperties.
1086    //
1087    static DEProperties defaultDEProperties()
1088    {
1089        DEProperties props = new DEProperties();
1090
1091        //
1092        // Note: This needs to match the Properties() constructor.
1093        //
1094        props.forceReplication = false;
1095        props.objectChunks = DefaultObjectsLockedPerTransaction;
1096        props.numThreads = DefaultNumberOfThreads;
1097        props.sparseAudit = DESparsePartitionAudit.VerifyNodeList;
1098        props.remoteEnableAction = DERemoteEnableAction.EnablePartition;
1099        props.restoreFromNode = null;
1100        props.broadcastUpdates = DEPartitionBroadcast.BroadcastDefault;
1101
1102        return props;
1103    }
1104}