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