helix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From zzh...@apache.org
Subject git commit: HELIX-43: Add support for error->dropped transition
Date Mon, 01 Apr 2013 18:07:49 GMT
Updated Branches:
  refs/heads/master 32a4e37d9 -> cd8272c95


HELIX-43: Add support for error->dropped transition


Project: http://git-wip-us.apache.org/repos/asf/incubator-helix/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-helix/commit/cd8272c9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-helix/tree/cd8272c9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-helix/diff/cd8272c9

Branch: refs/heads/master
Commit: cd8272c952377ef9bbb478356ea4a2a9f8e7d3fa
Parents: 32a4e37
Author: zzhang <zzhang5@uci.edu>
Authored: Mon Apr 1 11:07:34 2013 -0700
Committer: zzhang <zzhang5@uci.edu>
Committed: Mon Apr 1 11:07:34 2013 -0700

----------------------------------------------------------------------
 .../main/java/org/apache/helix/HelixConstants.java |    4 +-
 .../java/org/apache/helix/HelixDefinedState.java   |   43 +++
 .../stages/BestPossibleStateCalcStage.java         |   37 ++--
 .../controller/stages/BestPossibleStateOutput.java |    4 +
 .../stages/ExternalViewComputeStage.java           |    3 +-
 .../controller/stages/ReadClusterDataStage.java    |    6 +-
 .../controller/stages/TaskAssignmentStage.java     |    2 +-
 .../org/apache/helix/manager/zk/ZKHelixAdmin.java  |   37 ++--
 .../messaging/handling/BatchMessageHandler.java    |    2 +-
 .../messaging/handling/HelixBatchMessageTask.java  |    2 +-
 .../handling/HelixStateTransitionHandler.java      |  144 ++++++-----
 .../apache/helix/messaging/handling/HelixTask.java |   10 +-
 .../helix/messaging/handling/HelixTaskResult.java  |    2 +-
 .../org/apache/helix/model/InstanceConfig.java     |   19 +--
 .../apache/helix/model/StateModelDefinition.java   |   53 ++++-
 .../helix/monitoring/mbeans/ResourceMonitor.java   |    3 +-
 .../helix/participant/statemachine/StateModel.java |   15 +-
 .../participant/statemachine/StateModelParser.java |    2 +-
 .../apache/helix/tools/ClusterStateVerifier.java   |   26 ++-
 .../helix/tools/StateModelConfigGenerator.java     |    1 +
 .../org/apache/helix/integration/TestDrop.java     |  203 ++++++++++++++-
 .../integration/TestStateTransitionTimeout.java    |    2 +-
 .../helix/mock/participant/MockMSStateModel.java   |   25 ++-
 .../helix/mock/participant/MockTransition.java     |    2 +-
 .../helix/mock/participant/SleepTransition.java    |    9 +-
 .../participant/StoreAccessDiffNodeTransition.java |    2 +-
 .../participant/StoreAccessOneNodeTransition.java  |    2 +-
 .../statemachine/TestStateModelParser.java         |   93 +++++++
 28 files changed, 605 insertions(+), 148 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/HelixConstants.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/HelixConstants.java b/helix-core/src/main/java/org/apache/helix/HelixConstants.java
index 938b9bf..19bc740 100644
--- a/helix-core/src/main/java/org/apache/helix/HelixConstants.java
+++ b/helix-core/src/main/java/org/apache/helix/HelixConstants.java
@@ -21,7 +21,7 @@ package org.apache.helix;
 
 public interface HelixConstants
 {
-  // ChangeType and PropertyType are the same; remove this
+  // TODO: ChangeType and PropertyType are duplicated, consider unifying
   enum ChangeType
   {
     // @formatter:off
@@ -47,6 +47,6 @@ public interface HelixConstants
   {
     HELIX_DISABLE_PIPELINE_TRIGGERS
   }
-
+  
   static final String DEFAULT_STATE_MODEL_FACTORY = "DEFAULT";
 }

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/HelixDefinedState.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/HelixDefinedState.java b/helix-core/src/main/java/org/apache/helix/HelixDefinedState.java
new file mode 100644
index 0000000..88cf068
--- /dev/null
+++ b/helix-core/src/main/java/org/apache/helix/HelixDefinedState.java
@@ -0,0 +1,43 @@
+package org.apache.helix;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+/**
+* helix defined states
+*
+* ERROR : when errors happen during state transitions, transit to ERROR state
+*            participant will also invoke state-model.on-err(), ignore errors in state-model.on-err()
+*         when drop resource in ERROR state and not disabled, controller sends ERROR->DROPPED transition
+*            if errors happen in ERROR->DROPPED transition, participant will disable resource/partition
+*         when disable resource/partition in ERROR state, resource/partition will be marked disabled
+*            but controller not send any transitions
+*         when reset resource/partition in ERROR state and not disabled
+*            controller send ERROR->initial-state transition
+*            if errors happen in ERROR->initial-state transition, remain in ERROR state
+* DROPPED : when drop resource in a non-ERROR state and not disabled
+*            controller sends all the transitions from current-state to initial-state
+*            then sends initial-state->DROPPED transition
+* @see HELIX-43: add support for dropping partitions in error state
+*/
+public enum HelixDefinedState {
+  ERROR,
+  DROPPED
+}

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateCalcStage.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateCalcStage.java b/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateCalcStage.java
index edce407..aca0e74 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateCalcStage.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateCalcStage.java
@@ -29,6 +29,8 @@ import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
 
+import org.apache.helix.HelixConstants;
+import org.apache.helix.HelixDefinedState;
 import org.apache.helix.HelixManager;
 import org.apache.helix.ZNRecord;
 import org.apache.helix.HelixConstants.StateModelToken;
@@ -410,15 +412,16 @@ public class BestPossibleStateCalcStage extends AbstractBaseStage
       for (String instance : currentStateMap.keySet())
       {
         if ((instancePreferenceList == null || !instancePreferenceList.contains(instance))
-            && !"ERROR".equals(currentStateMap.get(instance)))
+            && !disabledInstancesForPartition.contains(instance))
         {
-          // move to DROPPED state only if not in ERROR state
-          instanceStateMap.put(instance, "DROPPED");
+          // if dropped and not disabled, transit to DROPPED
+          instanceStateMap.put(instance, HelixDefinedState.DROPPED.toString());
         }
-        else if (!"ERROR".equals(currentStateMap.get(instance))
+        else if ( (currentStateMap.get(instance) == null 
+            || !currentStateMap.get(instance).equals(HelixDefinedState.ERROR.toString()))
             && disabledInstancesForPartition.contains(instance))
         {
-          // if a non-error node is disabled, put it into initial state (OFFLINE)
+          // if disabled and not in ERROR state, transit to initial-state (e.g. OFFLINE)
           instanceStateMap.put(instance, stateModelDef.getInitialState());
         }
       }
@@ -467,9 +470,9 @@ public class BestPossibleStateCalcStage extends AbstractBaseStage
         {
           String instanceName = instancePreferenceList.get(i);
 
-          boolean notInErrorState =
-              currentStateMap == null
-                  || !"ERROR".equals(currentStateMap.get(instanceName));
+          boolean notInErrorState = currentStateMap == null 
+              || currentStateMap.get(instanceName) == null
+              || !currentStateMap.get(instanceName).equals(HelixDefinedState.ERROR.toString());
 
           if (liveInstancesMap.containsKey(instanceName) && !assigned[i]
               && notInErrorState && !disabledInstancesForPartition.contains(instanceName))
@@ -512,16 +515,17 @@ public class BestPossibleStateCalcStage extends AbstractBaseStage
     {
       for (String instance : currentStateMap.keySet())
       {
-        if ( (idealStateMap == null || !idealStateMap.containsKey(instance))
-            && !"ERROR".equals(currentStateMap.get(instance)))
+        if ((idealStateMap == null || !idealStateMap.containsKey(instance))
+            && !disabledInstancesForPartition.contains(instance))
         {
-          // move to DROPPED state only if not in ERROR state
-          instanceStateMap.put(instance, "DROPPED");
+          // if dropped and not disabled, transit to DROPPED
+          instanceStateMap.put(instance, HelixDefinedState.DROPPED.toString());
         }
-        else if (!"ERROR".equals(currentStateMap.get(instance))
+        else if ( (currentStateMap.get(instance) == null 
+            || !currentStateMap.get(instance).equals(HelixDefinedState.ERROR.toString()))
             && disabledInstancesForPartition.contains(instance))
         {
-          // if a non-error node is disabled, put it into initial state (OFFLINE)
+          // if disabled and not in ERROR state, transit to initial-state (e.g. OFFLINE)
           instanceStateMap.put(instance, stateModelDef.getInitialState());
         }
       }
@@ -536,8 +540,9 @@ public class BestPossibleStateCalcStage extends AbstractBaseStage
     Map<String, LiveInstance> liveInstancesMap = cache.getLiveInstances();
     for (String instance : idealStateMap.keySet())
     {
-      boolean notInErrorState =
-          currentStateMap == null || !"ERROR".equals(currentStateMap.get(instance));
+      boolean notInErrorState = currentStateMap == null 
+          || currentStateMap.get(instance) == null
+          || !currentStateMap.get(instance).equals(HelixDefinedState.ERROR.toString());
 
       if (liveInstancesMap.containsKey(instance) && notInErrorState
           && !disabledInstancesForPartition.contains(instance))

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateOutput.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateOutput.java b/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateOutput.java
index 4b967d2..18eca4f 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateOutput.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateOutput.java
@@ -68,6 +68,10 @@ public class BestPossibleStateOutput
     }
     return Collections.emptyMap();
   }
+  
+  public Map<String, Map<Partition, Map<String, String>>> getStateMap() {
+    return _dataMap;
+  }
 
   @Override
   public String toString()

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/controller/stages/ExternalViewComputeStage.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/controller/stages/ExternalViewComputeStage.java b/helix-core/src/main/java/org/apache/helix/controller/stages/ExternalViewComputeStage.java
index 69a676a..368db9d 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/stages/ExternalViewComputeStage.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/stages/ExternalViewComputeStage.java
@@ -27,6 +27,7 @@ import java.util.Map;
 import java.util.TreeMap;
 
 import org.apache.helix.HelixDataAccessor;
+import org.apache.helix.HelixDefinedState;
 import org.apache.helix.HelixManager;
 import org.apache.helix.NotificationContext;
 import org.apache.helix.PropertyKey;
@@ -183,7 +184,7 @@ public class ExternalViewComputeStage extends AbstractBaseStage
     {
       for(String taskState : ev.getStateMap(taskPartitionName).values())
       {
-        if(taskState.equalsIgnoreCase("ERROR") || taskState.equalsIgnoreCase("COMPLETED"))
+        if(taskState.equalsIgnoreCase(HelixDefinedState.ERROR.toString()) || taskState.equalsIgnoreCase("COMPLETED"))
         {
           log.info(taskPartitionName + " finished as " + taskState);
           finishedTasks.getListFields().put(taskPartitionName, emptyList);

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/controller/stages/ReadClusterDataStage.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/controller/stages/ReadClusterDataStage.java b/helix-core/src/main/java/org/apache/helix/controller/stages/ReadClusterDataStage.java
index 3e250e9..204fdca 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/stages/ReadClusterDataStage.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/stages/ReadClusterDataStage.java
@@ -63,11 +63,11 @@ public class ReadClusterDataStage extends AbstractBaseStage
       {
         if(config.getInstanceEnabled() == false)
         {
-          disabledInstances ++;
+          disabledInstances++;
         }
-        if(config.getDisabledPartitionMap() != null)
+        if(config.getDisabledPartitions() != null)
         {
-          disabledPartitions += config.getDisabledPartitionMap().size();
+          disabledPartitions += config.getDisabledPartitions().size();
         }
       }
       clusterStatusMonitor.setClusterStatusCounters(_cache._liveInstanceMap.size(), _cache._instanceConfigMap.size(), 

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/controller/stages/TaskAssignmentStage.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/controller/stages/TaskAssignmentStage.java b/helix-core/src/main/java/org/apache/helix/controller/stages/TaskAssignmentStage.java
index 2a8137c..f541999 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/stages/TaskAssignmentStage.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/stages/TaskAssignmentStage.java
@@ -136,7 +136,7 @@ public class TaskAssignmentStage extends AbstractBaseStage
           + " transit " + message.getPartitionName() + "|" + message.getPartitionNames()
           + " from:" + message.getFromState() + " to:" + message.getToState());
 
-//      System.out.println("[dbg]Sending Message " + message.getMsgId() + " to " + message.getTgtName()
+//      System.out.println("[dbg] Sending Message " + message.getMsgId() + " to " + message.getTgtName()
 //              + " transit " + message.getPartitionName() + "|" + message.getPartitionNames()
 //              + " from: " + message.getFromState() + " to: " + message.getToState());
 

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
index 7dd3b67..ca80a1e 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
@@ -44,6 +44,7 @@ import org.apache.helix.ConfigAccessor;
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.HelixConstants;
 import org.apache.helix.HelixDataAccessor;
+import org.apache.helix.HelixDefinedState;
 import org.apache.helix.HelixException;
 import org.apache.helix.InstanceType;
 import org.apache.helix.PropertyKey;
@@ -258,22 +259,26 @@ public class ZKHelixAdmin implements HelixAdmin
       // OK.
     }
 
+    // check resource exist. warn if not.
     if (idealStateRecord == null)
     {
-      throw new HelixException("Cluster: " + clusterName + ", resource: " + resourceName
-          + ", ideal state does not exist");
-    }
+//      throw new HelixException("Cluster: " + clusterName + ", resource: " + resourceName
+//          + ", ideal state does not exist");
+      logger.warn("Disable partitions: " + partitionNames + " but Cluster: " + clusterName 
+          + ", resource: " + resourceName + " does not exists. probably disable it during ERROR->DROPPED transtition");
 
-    // check partitions exist. warn if not
-    IdealState idealState = new IdealState(idealStateRecord);
-    for (String partitionName : partitionNames)
-    {
-      if ((idealState.getIdealStateMode() == IdealStateModeProperty.AUTO && idealState.getPreferenceList(partitionName) == null)
-          || (idealState.getIdealStateMode() == IdealStateModeProperty.CUSTOMIZED && idealState.getInstanceStateMap(partitionName) == null))
+    } else {
+      // check partitions exist. warn if not
+      IdealState idealState = new IdealState(idealStateRecord);
+      for (String partitionName : partitionNames)
       {
-        logger.warn("Cluster: " + clusterName + ", resource: " + resourceName
-            + ", partition: " + partitionName
-            + ", partition does not exist in ideal state");
+        if ((idealState.getIdealStateMode() == IdealStateModeProperty.AUTO && idealState.getPreferenceList(partitionName) == null)
+            || (idealState.getIdealStateMode() == IdealStateModeProperty.CUSTOMIZED && idealState.getInstanceStateMap(partitionName) == null))
+        {
+          logger.warn("Cluster: " + clusterName + ", resource: " + resourceName
+              + ", partition: " + partitionName
+              + ", partition does not exist in ideal state");
+        }
       }
     }
 
@@ -397,7 +402,7 @@ public class ZKHelixAdmin implements HelixAdmin
                                                      resourceName));
     for (String partitionName : resetPartitionNames)
     {
-      if (!curState.getState(partitionName).equals("ERROR"))
+      if (!curState.getState(partitionName).equals(HelixDefinedState.ERROR.toString()))
       {
         throw new HelixException("Can't reset state for " + resourceName + "/"
             + partitionNames + " on " + instanceName + ", because not all "
@@ -459,7 +464,7 @@ public class ZKHelixAdmin implements HelixAdmin
       message.setResourceName(resourceName);
       message.setTgtSessionId(sessionId);
       message.setStateModelDef(stateModelDef);
-      message.setFromState("ERROR");
+      message.setFromState(HelixDefinedState.ERROR.toString());
       message.setToState(stateModel.getInitialState());
       message.setStateModelFactoryName(idealState.getStateModelFactoryName());
 
@@ -491,7 +496,7 @@ public class ZKHelixAdmin implements HelixAdmin
           Map<String, String> instanceStateMap = stateMap.get(partitionName);
 
           if (instanceStateMap.containsKey(instanceName)
-              && instanceStateMap.get(instanceName).equals("ERROR"))
+              && instanceStateMap.get(instanceName).equals(HelixDefinedState.ERROR.toString()))
           {
             resetPartitionNames.add(partitionName);
           }
@@ -530,7 +535,7 @@ public class ZKHelixAdmin implements HelixAdmin
         Map<String, String> instanceStateMap = stateMap.get(partitionName);
         for (String instanceName : instanceStateMap.keySet())
         {
-          if (instanceStateMap.get(instanceName).equals("ERROR"))
+          if (instanceStateMap.get(instanceName).equals(HelixDefinedState.ERROR.toString()))
           {
             if (!resetPartitionNames.containsKey(instanceName))
             {

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/messaging/handling/BatchMessageHandler.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/messaging/handling/BatchMessageHandler.java b/helix-core/src/main/java/org/apache/helix/messaging/handling/BatchMessageHandler.java
index 77d1b43..4b9a966 100644
--- a/helix-core/src/main/java/org/apache/helix/messaging/handling/BatchMessageHandler.java
+++ b/helix-core/src/main/java/org/apache/helix/messaging/handling/BatchMessageHandler.java
@@ -173,7 +173,7 @@ public class BatchMessageHandler extends MessageHandler {
 					MessageTask subTask = batchTasks.get(i);
 					try {
               HelixTaskResult subTaskResult = future.get();
-              if (!subTaskResult.isSucess()) {
+              if (!subTaskResult.isSuccess()) {
               	isBatchTaskSucceed = false;
               }
             } catch (InterruptedException e) {

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixBatchMessageTask.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixBatchMessageTask.java b/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixBatchMessageTask.java
index f4c82b3..50eba2c 100644
--- a/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixBatchMessageTask.java
+++ b/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixBatchMessageTask.java
@@ -56,7 +56,7 @@ public class HelixBatchMessageTask implements MessageTask {
     			if (handler != null) {
     				HelixTaskResult subTaskResult = handler.handleMessage();
     				// if any fails, return false
-    				if (!subTaskResult.isSucess()) {
+    				if (!subTaskResult.isSuccess()) {
     					// System.err.println("\t[dbg]error handling message: " + handler._message);
     					isSucceed = false;
     				}

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixStateTransitionHandler.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixStateTransitionHandler.java b/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixStateTransitionHandler.java
index 3c952b9..fc09070 100644
--- a/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixStateTransitionHandler.java
+++ b/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixStateTransitionHandler.java
@@ -27,7 +27,10 @@ import java.util.Date;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 
+import org.apache.helix.HelixAdmin;
+import org.apache.helix.HelixConstants;
 import org.apache.helix.HelixDataAccessor;
+import org.apache.helix.HelixDefinedState;
 import org.apache.helix.HelixException;
 import org.apache.helix.HelixManager;
 import org.apache.helix.NotificationContext;
@@ -62,6 +65,7 @@ public class HelixStateTransitionHandler extends MessageHandler
   StatusUpdateUtil               _statusUpdateUtil;
   private final StateModelParser _transitionMethodFinder;
   private final CurrentState     _currentStateDelta;
+  private final HelixManager     _manager;
   volatile boolean               _isTimeout = false;
 
   public HelixStateTransitionHandler(StateModel stateModel,
@@ -74,32 +78,30 @@ public class HelixStateTransitionHandler extends MessageHandler
     _statusUpdateUtil = new StatusUpdateUtil();
     _transitionMethodFinder = new StateModelParser();
     _currentStateDelta = currentStateDelta;
+    _manager = _notificationContext.getManager();;
   }
 
   void preHandleMessage() throws Exception
   {
-	Message message = _message;
-	HelixManager manager = _notificationContext.getManager();
-
-    if (!message.isValid())
+    if (!_message.isValid())
     {
       String errorMessage =
-          "Invalid Message, ensure that message: " + message
+          "Invalid Message, ensure that message: " + _message
               + " has all the required fields: "
               + Arrays.toString(Message.Attributes.values());
 
-      _statusUpdateUtil.logError(message,
+      _statusUpdateUtil.logError(_message,
                                  HelixStateTransitionHandler.class,
                                  errorMessage,
-                                 manager.getHelixDataAccessor());
+                                 _manager.getHelixDataAccessor());
       logger.error(errorMessage);
       throw new HelixException(errorMessage);
     }
     
-    HelixDataAccessor accessor = manager.getHelixDataAccessor();
+    HelixDataAccessor accessor = _manager.getHelixDataAccessor();
 
-    String partitionName = message.getPartitionName();
-    String fromState = message.getFromState();
+    String partitionName = _message.getPartitionName();
+    String fromState = _message.getFromState();
 
     // Verify the fromState and current state of the stateModel
     String state = _currentStateDelta.getState(partitionName);
@@ -109,10 +111,10 @@ public class HelixStateTransitionHandler extends MessageHandler
       String errorMessage =
           "Current state of stateModel does not match the fromState in Message"
               + ", Current State:" + state + ", message expected:" + fromState
-              + ", partition: " + partitionName + ", from: " + message.getMsgSrc()
-              + ", to: " + message.getTgtName();
+              + ", partition: " + partitionName + ", from: " + _message.getMsgSrc()
+              + ", to: " + _message.getTgtName();
 
-      _statusUpdateUtil.logError(message,
+      _statusUpdateUtil.logError(_message,
                                  HelixStateTransitionHandler.class,
                                  errorMessage,
                                  accessor);
@@ -123,42 +125,42 @@ public class HelixStateTransitionHandler extends MessageHandler
 
   void postHandleMessage()
   {
-	Message message = _message;
-	HelixManager manager = _notificationContext.getManager();
+	// Message message = _message;
+	// HelixManager manager = _notificationContext.getManager();
 	HelixTaskResult taskResult = (HelixTaskResult) _notificationContext.get(MapKey.HELIX_TASK_RESULT.toString());
 	Exception exception = taskResult.getException();
 		
-    String partitionKey = message.getPartitionName();
-    String resource = message.getResourceName();
-    String sessionId = message.getTgtSessionId();
-    String instanceName = manager.getInstanceName();
+    String partitionKey = _message.getPartitionName();
+    String resource = _message.getResourceName();
+    String sessionId = _message.getTgtSessionId();
+    String instanceName = _manager.getInstanceName();
 
-    HelixDataAccessor accessor = manager.getHelixDataAccessor();
+    HelixDataAccessor accessor = _manager.getHelixDataAccessor();
     Builder keyBuilder = accessor.keyBuilder();
 
-    int bucketSize = message.getBucketSize();
+    int bucketSize = _message.getBucketSize();
     ZNRecordBucketizer bucketizer = new ZNRecordBucketizer(bucketSize);
 
     // Lock the helix manager so that the session id will not change when we update
     // the state model state. for zk current state it is OK as we have the per-session
     // current state node
-    synchronized (manager)
+    synchronized (_manager)
     {
-      if (!message.getTgtSessionId().equals(manager.getSessionId()))
+      if (!_message.getTgtSessionId().equals(_manager.getSessionId()))
       {
         logger.warn("Session id has changed. Skip postExecutionMessage. Old session "
-            + message.getExecutionSessionId() + " , new session : "
-            + manager.getSessionId());
+            + _message.getExecutionSessionId() + " , new session : "
+            + _manager.getSessionId());
         return;
       }
 
-      if (taskResult.isSucess())
+      if (taskResult.isSuccess())
       {
         // String fromState = message.getFromState();
-        String toState = message.getToState();
+        String toState = _message.getToState();
         _currentStateDelta.setState(partitionKey, toState);
 
-        if (toState.equalsIgnoreCase("DROPPED"))
+        if (toState.equalsIgnoreCase(HelixDefinedState.DROPPED.toString()))
         {
           // for "OnOfflineToDROPPED" message, we need to remove the resource key record
           // from the current state of the instance because the resource key is dropped.
@@ -187,7 +189,7 @@ public class HelixStateTransitionHandler extends MessageHandler
               + partitionKey
               + ", currentState: "
               + _stateModel.getCurrentState()
-              + ", message: " + message);
+              + ", message: " + _message);
           _currentStateDelta.setState(partitionKey, _stateModel.getCurrentState());
         }
         else
@@ -208,13 +210,18 @@ public class HelixStateTransitionHandler extends MessageHandler
               // State transition interrupted but not caused by timeout. Keep the current
               // state in this case
               logger.error("State transition interrupted but not timeout. Not updating state. Partition : "
-                  + message.getPartitionName() + " MsgId : " + message.getMsgId());
+                  + _message.getPartitionName() + " MsgId : " + _message.getMsgId());
               return;
             }
           }
-          _stateModel.rollbackOnError(message, _notificationContext, error);
-          _currentStateDelta.setState(partitionKey, "ERROR");
-          _stateModel.updateState("ERROR");
+          _stateModel.rollbackOnError(_message, _notificationContext, error);
+          _currentStateDelta.setState(partitionKey, HelixDefinedState.ERROR.toString());
+          _stateModel.updateState(HelixDefinedState.ERROR.toString());
+          
+          // if we have errors transit from ERROR state, disable the partition
+          if (_message.getFromState().equalsIgnoreCase(HelixDefinedState.ERROR.toString())) {
+            disablePartition();
+          }
         }
       }
     }
@@ -227,12 +234,12 @@ public class HelixStateTransitionHandler extends MessageHandler
                               bucketizer.getBucketName(partitionKey));
       if (_message.getAttribute(Attributes.PARENT_MSG_ID) == null)
       {
-    	// normal message
+        // normal message
         accessor.updateProperty(key, _currentStateDelta);
       }
       else
       {
-    	// sub-message of a batch message
+        // sub-message of a batch message
         ConcurrentHashMap<String, CurrentStateUpdate> csUpdateMap 
           = (ConcurrentHashMap<String, CurrentStateUpdate>) _notificationContext.get(MapKey.CURRENT_STATE_UPDATE.toString());
         csUpdateMap.put(partitionKey, new CurrentStateUpdate(key, _currentStateDelta));
@@ -243,8 +250,8 @@ public class HelixStateTransitionHandler extends MessageHandler
       logger.error("Error when updating current-state ", e);
       StateTransitionError error =
           new StateTransitionError(ErrorType.FRAMEWORK, ErrorCode.ERROR, e);
-      _stateModel.rollbackOnError(message, _notificationContext, error);
-      _statusUpdateUtil.logError(message,
+      _stateModel.rollbackOnError(_message, _notificationContext, error);
+      _statusUpdateUtil.logError(_message,
                                  HelixStateTransitionHandler.class,
                                  e,
                                  "Error when update current-state ",
@@ -252,6 +259,18 @@ public class HelixStateTransitionHandler extends MessageHandler
     }
   }
 
+  void disablePartition() {
+    String instanceName = _manager.getInstanceName();
+    String resourceName = _message.getResourceName();
+    String partitionName = _message.getPartitionName();
+    String clusterName = _manager.getClusterName();
+    HelixAdmin admin = _manager.getClusterManagmentTool();
+    admin.enablePartition(false, clusterName, instanceName, resourceName, Arrays.asList(partitionName));
+    logger.info("error in transit from ERROR to " + _message.getToState()
+          + " for partition: " + partitionName + ". disable it on " + instanceName);
+
+  }
+  
   @Override
   public HelixTaskResult handleMessage()
   {
@@ -343,7 +362,7 @@ public class HelixStateTransitionHandler extends MessageHandler
     {
       String errorMessage =
           "Unable to find method for transition from " + fromState + " to " + toState
-              + "in " + _stateModel.getClass();
+              + " in " + _stateModel.getClass();
       logger.error(errorMessage);
       taskResult.setSuccess(false);
 
@@ -356,35 +375,42 @@ public class HelixStateTransitionHandler extends MessageHandler
 
   @Override
   public void onError(Exception e, ErrorCode code, ErrorType type)
-  {
+  {    
+    HelixDataAccessor accessor = _manager.getHelixDataAccessor();
+    Builder keyBuilder = accessor.keyBuilder();
+    String instanceName = _manager.getInstanceName();
+    String resourceName = _message.getResourceName();
+    String partition = _message.getPartitionName();
+    
     // All internal error has been processed already, so we can skip them
     if (type == ErrorType.INTERNAL)
     {
       logger.error("Skip internal error. errCode: " + code + ", errMsg: " + e.getMessage());
       return;
     }
-    HelixManager manager = _notificationContext.getManager();
-    HelixDataAccessor accessor = manager.getHelixDataAccessor();
-    Builder keyBuilder = accessor.keyBuilder();
-
-    String instanceName = manager.getInstanceName();
-    String partition = _message.getPartitionName();
-    String resourceName = _message.getResourceName();
-    CurrentState currentStateDelta = new CurrentState(resourceName);
-
-    StateTransitionError error = new StateTransitionError(type, code, e);
-    _stateModel.rollbackOnError(_message, _notificationContext, error);
-    // if the transition is not canceled, it should go into error state
-    if (code == ErrorCode.ERROR)
-    {
-      currentStateDelta.setState(partition, "ERROR");
-      _stateModel.updateState("ERROR");
 
-      accessor.updateProperty(keyBuilder.currentState(instanceName,
-                                                      _message.getTgtSessionId(),
-                                                      resourceName),
-                              currentStateDelta);
+    try {
+      // set current state to ERROR for the partition
+      // if the transition is not canceled, it should go into error state
+      if (code == ErrorCode.ERROR) {
+        CurrentState currentStateDelta = new CurrentState(resourceName);
+        currentStateDelta.setState(partition, HelixDefinedState.ERROR.toString());
+        _stateModel.updateState(HelixDefinedState.ERROR.toString());
+  
+        // if transit from ERROR state, disable the partition
+        if (_message.getFromState().equalsIgnoreCase(HelixDefinedState.ERROR.toString())) {
+          disablePartition();
+        }
+        accessor.updateProperty(keyBuilder.currentState(instanceName,
+                                                        _message.getTgtSessionId(),
+                                                        resourceName),
+                                currentStateDelta);
+    }
+    } finally {
+      StateTransitionError error = new StateTransitionError(type, code, e);
+      _stateModel.rollbackOnError(_message, _notificationContext, error);
     }
+
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixTask.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixTask.java b/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixTask.java
index 6b5a66e..2e91cd6 100644
--- a/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixTask.java
+++ b/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixTask.java
@@ -133,7 +133,7 @@ public class HelixTask implements MessageTask
     Exception exception = null;
     try
     {
-      if (taskResult.isSucess())
+      if (taskResult.isSuccess())
       {
         _statusUpdateUtil.logInfo(_message,
                                 _handler.getClass(),
@@ -204,7 +204,7 @@ public class HelixTask implements MessageTask
     {
       long end = System.currentTimeMillis();
       logger.info("msg: " + _message.getMsgId() + " handling task completed, results:"
-          + taskResult.isSucess() + ", at: " + end + ", took:" + (end - start));
+          + taskResult.isSuccess() + ", at: " + end + ", took:" + (end - start));
 
       // Notify the handler about any error happened in the handling procedure, so that
       // the handler have chance to finally cleanup
@@ -244,9 +244,9 @@ public class HelixTask implements MessageTask
       logger.info("Sending reply for message " + message.getCorrelationId());
       _statusUpdateUtil.logInfo(message, HelixTask.class, "Sending reply", accessor);
 
-      taskResult.getTaskResultMap().put("SUCCESS", "" + taskResult.isSucess());
+      taskResult.getTaskResultMap().put("SUCCESS", "" + taskResult.isSuccess());
       taskResult.getTaskResultMap().put("INTERRUPTED", "" + taskResult.isInterrupted());
-      if (!taskResult.isSucess())
+      if (!taskResult.isSuccess())
       {
         taskResult.getTaskResultMap().put("ERRORINFO", taskResult.getMessage());
       }
@@ -305,7 +305,7 @@ public class HelixTask implements MessageTask
         StateTransitionDataPoint data =
             new StateTransitionDataPoint(totalDelay,
                                          executionDelay,
-                                         taskResult.isSucess());
+                                         taskResult.isSuccess());
         _executor.getParticipantMonitor().reportTransitionStat(cxt, data);
       }
     }

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixTaskResult.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixTaskResult.java b/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixTaskResult.java
index 7c0f19a..1ccf28b 100644
--- a/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixTaskResult.java
+++ b/helix-core/src/main/java/org/apache/helix/messaging/handling/HelixTaskResult.java
@@ -31,7 +31,7 @@ public class HelixTaskResult
   private boolean _interrupted = false;
   Exception _exception = null;
   
-  public boolean isSucess()
+  public boolean isSuccess()
   {
     return _success;
   }

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java b/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
index ebde5df..6b4ed24 100644
--- a/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
+++ b/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
@@ -149,27 +149,14 @@ public class InstanceConfig extends HelixProperty
     }
   }
 
-  public Map<String, String> getDisabledPartitionMap()
+  public List<String> getDisabledPartitions()
   {
-    return _record.getMapField(InstanceConfigProperty.HELIX_DISABLED_PARTITION.toString());
+    return _record.getListField(InstanceConfigProperty.HELIX_DISABLED_PARTITION.toString());
+
   }
 
   public void setInstanceEnabledForPartition(String partitionName, boolean enabled)
   {
-//    if (_record.getListField(InstanceConfigProperty.HELIX_DISABLED_PARTITION.toString()) == null)
-//    {
-//      _record.setMapField(InstanceConfigProperty.HELIX_DISABLED_PARTITION.toString(),
-//                             new TreeMap<String, String>());
-//    }
-//    if (enabled == true)
-//    {
-//      _record.getMapField(InstanceConfigProperty.HELIX_DISABLED_PARTITION.toString()).remove(partition);
-//    }
-//    else
-//    {
-//      _record.getMapField(InstanceConfigProperty.HELIX_DISABLED_PARTITION.toString()).put(partition, Boolean.toString(false));
-//    }
-    
     List<String> list =
         _record.getListField(InstanceConfigProperty.HELIX_DISABLED_PARTITION.toString());
     Set<String> disabledPartitions = new HashSet<String>();

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/model/StateModelDefinition.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/model/StateModelDefinition.java b/helix-core/src/main/java/org/apache/helix/model/StateModelDefinition.java
index c61d4ca..cf787ff 100644
--- a/helix-core/src/main/java/org/apache/helix/model/StateModelDefinition.java
+++ b/helix-core/src/main/java/org/apache/helix/model/StateModelDefinition.java
@@ -25,7 +25,10 @@ import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.TreeMap;
 
+import org.apache.helix.HelixConstants;
+import org.apache.helix.HelixDefinedState;
 import org.apache.helix.HelixProperty;
 import org.apache.helix.ZNRecord;
 import org.apache.helix.model.builder.StateTransitionTableBuilder;
@@ -40,10 +43,15 @@ public class StateModelDefinition extends HelixProperty
   {
     INITIAL_STATE, STATE_TRANSITION_PRIORITYLIST, STATE_PRIORITY_LIST
   }
-
+  
   private static final Logger _logger = Logger
       .getLogger(StateModelDefinition.class.getName());
   /**
+   * state model's initial state
+   */
+  private final String _initialState;
+  
+  /**
    * State Names in priority order. Indicates the order in which states are
    * fulfilled
    */
@@ -72,6 +80,12 @@ public class StateModelDefinition extends HelixProperty
   {
     super(record);
 
+    _initialState = record.getSimpleField(StateModelDefinitionProperty.INITIAL_STATE.toString());
+    
+    if (_initialState == null) {
+      throw new IllegalArgumentException("initial-state for " + record.getId() + " is null");
+    }
+    
     _statesPriorityList = record
         .getListField(StateModelDefinitionProperty.STATE_PRIORITY_LIST
             .toString());
@@ -96,6 +110,37 @@ public class StateModelDefinition extends HelixProperty
         _stateTransitionTable.put(state, nextData);
       }
     }
+    
+    // add transitions for helix-defined states
+    for (HelixDefinedState state : HelixDefinedState.values()) {
+      if (!_statesPriorityList.contains(state.toString())) {
+        _statesCountMap.put(state.toString(), "-1");
+      }
+    }
+    
+    addDefaultTransition(HelixDefinedState.ERROR.toString(), 
+        HelixDefinedState.DROPPED.toString(), HelixDefinedState.DROPPED.toString()); 
+    addDefaultTransition(HelixDefinedState.ERROR.toString(), 
+        _initialState, _initialState);
+    addDefaultTransition(_initialState, 
+        HelixDefinedState.DROPPED.toString(), HelixDefinedState.DROPPED.toString());  
+  }
+  
+  /**
+   * add transitions involving helix-defines states
+   * these transitions need not to be specified in state-model-definition
+   * @param from
+   * @param to
+   * @param next
+   */
+  void addDefaultTransition(String from, String to, String next) {
+    if (!_stateTransitionTable.containsKey(from)) {
+      _stateTransitionTable.put(from, new TreeMap<String, String>());
+    }
+    
+    if (!_stateTransitionTable.get(from).containsKey(to)) {
+      _stateTransitionTable.get(from).put(to, next);
+    }
   }
 
   public List<String> getStateTransitionPriorityList()
@@ -120,8 +165,9 @@ public class StateModelDefinition extends HelixProperty
 
   public String getInitialState()
   {
-    return _record.getSimpleField(StateModelDefinitionProperty.INITIAL_STATE
-        .toString());
+//    return _record.getSimpleField(StateModelDefinitionProperty.INITIAL_STATE
+//        .toString());
+    return _initialState;
   }
 
   public String getNumInstancesPerState(String state)
@@ -149,6 +195,7 @@ public class StateModelDefinition extends HelixProperty
     return true;
   }
 
+  // TODO move this to model.builder package, refactor StateModelConfigGenerator to use this
   /**
    * 
    *

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ResourceMonitor.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ResourceMonitor.java b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ResourceMonitor.java
index ba5fa00..f67b432 100644
--- a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ResourceMonitor.java
+++ b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ResourceMonitor.java
@@ -21,6 +21,7 @@ package org.apache.helix.monitoring.mbeans;
 
 import java.util.Map;
 
+import org.apache.helix.HelixDefinedState;
 import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyType;
 import org.apache.helix.model.ExternalView;
@@ -120,7 +121,7 @@ public class ResourceMonitor implements ResourceMonitorMBean
 
       for (String host : externalViewRecord.keySet())
       {
-        if (externalViewRecord.get(host).equalsIgnoreCase("ERROR"))
+        if (externalViewRecord.get(host).equalsIgnoreCase(HelixDefinedState.ERROR.toString()))
         {
           numOfErrorPartitions++;
         }

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/participant/statemachine/StateModel.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/participant/statemachine/StateModel.java b/helix-core/src/main/java/org/apache/helix/participant/statemachine/StateModel.java
index 3dfd79c..01890f8 100644
--- a/helix-core/src/main/java/org/apache/helix/participant/statemachine/StateModel.java
+++ b/helix-core/src/main/java/org/apache/helix/participant/statemachine/StateModel.java
@@ -63,7 +63,7 @@ public abstract class StateModel
 	    StateTransitionError error)
 	{
 
-		logger.error("Default rollback method invoked on error. Error Code:"
+		logger.error("Default rollback method invoked on error. Error Code: "
 		    + error.getCode());
 
 	}
@@ -76,4 +76,17 @@ public abstract class StateModel
     logger.warn("Default reset method invoked. Either because the process longer own this resource or session timedout");
 	}
 
+	/**
+	 * default transition for drop partition in error state
+	 * 
+	 * @param message
+	 * @param context
+	 * @throws InterruptedException
+	 */
+  @Transition(to = "DROPPED", from = "ERROR")
+  public void onBecomeDroppedFromError(Message message, NotificationContext context)
+  {
+    logger.info("Default ERROR->DROPPED transition invoked.");
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/participant/statemachine/StateModelParser.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/participant/statemachine/StateModelParser.java b/helix-core/src/main/java/org/apache/helix/participant/statemachine/StateModelParser.java
index 842532e..b7c7c3a 100644
--- a/helix-core/src/main/java/org/apache/helix/participant/statemachine/StateModelParser.java
+++ b/helix-core/src/main/java/org/apache/helix/participant/statemachine/StateModelParser.java
@@ -133,7 +133,7 @@ public class StateModelParser
 	}
 
 	/**
-	 * Get the intial state for the state model
+	 * Get the initial state for the state model
 	 * 
 	 * @param clazz
 	 * @return

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/tools/ClusterStateVerifier.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterStateVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterStateVerifier.java
index 4b39292..eaada16 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterStateVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterStateVerifier.java
@@ -40,6 +40,7 @@ import org.apache.commons.cli.OptionBuilder;
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
 import org.apache.helix.HelixDataAccessor;
+import org.apache.helix.HelixDefinedState;
 import org.apache.helix.PropertyPathConfig;
 import org.apache.helix.PropertyType;
 import org.apache.helix.ZNRecord;
@@ -295,6 +296,7 @@ public class ClusterStateVerifier
       // calculate best possible state
       BestPossibleStateOutput bestPossOutput =
           ClusterStateVerifier.calcBestPossState(cache);
+      Map<String, Map<Partition, Map<String, String>>> bestPossStateMap = bestPossOutput.getStateMap();
 
       // set error states
       if (errStates != null)
@@ -305,13 +307,20 @@ public class ClusterStateVerifier
           for (String partitionName : partErrStates.keySet())
           {
             String instanceName = partErrStates.get(partitionName);
-            Map<String, String> partStateMap =
-                bestPossOutput.getInstanceStateMap(resourceName,
-                                                   new Partition(partitionName));
-            partStateMap.put(instanceName, "ERROR");
+            
+            if (!bestPossStateMap.containsKey(resourceName)) {
+              bestPossStateMap.put(resourceName, new HashMap<Partition, Map<String, String>>());
+            }
+            Partition partition = new Partition(partitionName);
+            if (!bestPossStateMap.get(resourceName).containsKey(partition)) {
+              bestPossStateMap.get(resourceName).put(partition, new HashMap<String, String>());
+            }
+            bestPossStateMap.get(resourceName).get(partition).put(instanceName, HelixDefinedState.ERROR.toString());
           }
         }
       }
+      
+      // System.out.println("stateMap: " + bestPossStateMap);
 
       
       for (String resourceName : idealStates.keySet())
@@ -343,7 +352,7 @@ public class ClusterStateVerifier
             {
               Map.Entry<String, String> insEntry = insIter.next();
               String state = insEntry.getValue();
-              if (state.equalsIgnoreCase("DROPPED"))
+              if (state.equalsIgnoreCase(HelixDefinedState.DROPPED.toString()))
               {
                 insIter.remove();
               }   
@@ -361,6 +370,10 @@ public class ClusterStateVerifier
           LOG.info("exterView size (" + extViewSize
               + ") is different from bestPossState size (" + bestPossStateSize
               + ") for resource: " + resourceName);
+          
+//          System.err.println("exterView size (" + extViewSize
+//              + ") is different from bestPossState size (" + bestPossStateSize
+//              + ") for resource: " + resourceName);
           // System.out.println("extView: " + extView.getRecord().getMapFields());
           // System.out.println("bestPossState: " +
           // bestPossOutput.getResourceMap(resourceName));
@@ -382,6 +395,9 @@ public class ClusterStateVerifier
           {
             LOG.info("externalView is different from bestPossibleState for partition:"
                 + partition);
+            
+//            System.err.println("externalView is different from bestPossibleState for partition: "
+//                + partition + ", actual: " + evInstanceStateMap + ", bestPoss: " + bpInstanceStateMap);
             return false;
           }
         }

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/main/java/org/apache/helix/tools/StateModelConfigGenerator.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/tools/StateModelConfigGenerator.java b/helix-core/src/main/java/org/apache/helix/tools/StateModelConfigGenerator.java
index 527748f..705f5bd 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/StateModelConfigGenerator.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/StateModelConfigGenerator.java
@@ -32,6 +32,7 @@ import org.apache.helix.model.StateModelDefinition.StateModelDefinitionProperty;
 import org.apache.helix.model.builder.StateTransitionTableBuilder;
 
 
+// TODO refactor to use StateModelDefinition.Builder
 public class StateModelConfigGenerator
 {
 

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/test/java/org/apache/helix/integration/TestDrop.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestDrop.java b/helix-core/src/test/java/org/apache/helix/integration/TestDrop.java
index 4561124..a0580dd 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestDrop.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestDrop.java
@@ -21,9 +21,15 @@ package org.apache.helix.integration;
 
 import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.I0Itec.zkclient.DataUpdater;
+import org.I0Itec.zkclient.IZkChildListener;
+import org.I0Itec.zkclient.IZkDataListener;
+import org.apache.helix.BaseDataAccessor;
+import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.TestHelper;
 import org.apache.helix.ZNRecord;
 import org.apache.helix.PropertyKey.Builder;
@@ -33,9 +39,13 @@ import org.apache.helix.mock.controller.ClusterController;
 import org.apache.helix.mock.participant.MockParticipant;
 import org.apache.helix.mock.participant.ErrTransition;
 import org.apache.helix.model.ExternalView;
+import org.apache.helix.model.InstanceConfig;
+import org.apache.helix.model.builder.CustomModeISBuilder;
+import org.apache.helix.model.builder.IdealStateBuilder;
 import org.apache.helix.tools.ClusterSetup;
 import org.apache.helix.tools.ClusterStateVerifier;
 import org.apache.helix.tools.ClusterStateVerifier.BestPossAndExtViewZkVerifier;
+import org.apache.zookeeper.data.Stat;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -43,7 +53,7 @@ import org.testng.annotations.Test;
 public class TestDrop extends ZkIntegrationTestBase
 {
   @Test
-  public void testDropErrorPartition() throws Exception
+  public void testDropErrorPartitionAutoIS() throws Exception
   {
     // Logger.getRootLogger().setLevel(Level.INFO);
     String className = TestHelper.getTestClassName();
@@ -106,17 +116,113 @@ public class TestDrop extends ZkIntegrationTestBase
                                                                                  errStateMap));
     Assert.assertTrue(result);
     
-    // drop resource containing error partitions should not change partitions in error state
+    // drop resource containing error partitions should drop the partition successfully
     ClusterSetup.processCommandLineArgs(new String[]{"--zkSvr", ZK_ADDR, "--dropResource", clusterName, "TestDB0"});
 
 
-    // make sure TestDB0_4 and TestDB0_8 partitions stay in ERROR state
+    // make sure TestDB0_4 and TestDB0_8 partitions are dropped
     result =
         ClusterStateVerifier.verifyByZkCallback(new BestPossAndExtViewZkVerifier(ZK_ADDR,
+                                                                                 clusterName));
+    Assert.assertTrue(result);
+    
+    
+    // clean up
+    // wait for all zk callbacks done
+//    Thread.sleep(1000);
+//    controller.syncStop();
+//    for (int i = 0; i < 5; i++)
+//    {
+//      participants[i].syncStop();
+//    }
+
+    System.out.println("END " + clusterName + " at "
+        + new Date(System.currentTimeMillis())); 
+  }
+  
+  @Test
+  public void testDropErrorPartitionFailedAutoIS() throws Exception
+  {
+    // Logger.getRootLogger().setLevel(Level.INFO);
+    String className = TestHelper.getTestClassName();
+    String methodName = TestHelper.getTestMethodName();
+    String clusterName = className + "_" + methodName;
+    final int n = 5;
+
+    System.out.println("START " + clusterName + " at "
+        + new Date(System.currentTimeMillis()));
+
+    MockParticipant[] participants = new MockParticipant[n];
+
+    TestHelper.setupCluster(clusterName, ZK_ADDR, 12918, // participant port
+                            "localhost", // participant name prefix
+                            "TestDB", // resource name prefix
+                            1, // resources
+                            8, // partitions per resource
+                            n, // number of nodes
+                            3, // replicas
+                            "MasterSlave",
+                            true); // do rebalance
+
+    // start controller
+    ClusterController controller =
+        new ClusterController(clusterName, "controller_0", ZK_ADDR);
+    controller.syncStart();
+    
+    // start participants
+    Map<String, Set<String>> errTransitions = new HashMap<String, Set<String>>();
+    errTransitions.put("SLAVE-MASTER", TestHelper.setOf("TestDB0_4"));
+    errTransitions.put("ERROR-DROPPED", TestHelper.setOf("TestDB0_4"));
+
+    for (int i = 0; i < n; i++)
+    {
+      String instanceName = "localhost_" + (12918 + i);
+
+      if (i == 0)
+      {
+        participants[i] =
+            new MockParticipant(clusterName,
+                                instanceName,
+                                ZK_ADDR,
+                                new ErrTransition(errTransitions));
+      }
+      else
+      {
+        participants[i] = new MockParticipant(clusterName, instanceName, ZK_ADDR, null);
+      }
+      participants[i].syncStart();
+    }
+
+    Map<String, Map<String, String>> errStateMap =
+        new HashMap<String, Map<String, String>>();
+    errStateMap.put("TestDB0", new HashMap<String, String>());
+    errStateMap.get("TestDB0").put("TestDB0_4", "localhost_12918");
+    boolean result =
+        ClusterStateVerifier.verifyByZkCallback(new BestPossAndExtViewZkVerifier(ZK_ADDR,
                                                                                  clusterName,
                                                                                  errStateMap));
     Assert.assertTrue(result);
     
+    // drop resource containing error partitions should invoke error->dropped transition
+    // if error happens during error->dropped transition, partition should be disabled
+    ClusterSetup.processCommandLineArgs(new String[]{"--zkSvr", ZK_ADDR, "--dropResource", clusterName, "TestDB0"});
+
+
+    // make sure TestDB0_4 stay in ERROR state and is disabled
+    result =
+        ClusterStateVerifier.verifyByZkCallback(new BestPossAndExtViewZkVerifier(ZK_ADDR,
+                                                                                 clusterName,
+                                                                                 errStateMap));
+    Assert.assertTrue(result);
+    
+    ZKHelixDataAccessor accessor = new ZKHelixDataAccessor(clusterName, new ZkBaseDataAccessor<ZNRecord>(_gZkClient));
+    Builder keyBuilder = accessor.keyBuilder();
+    InstanceConfig config = accessor.getProperty(keyBuilder.instanceConfig("localhost_12918"));
+    List<String> disabledPartitions = config.getDisabledPartitions();
+    // System.out.println("disabledPartitions: " + disabledPartitions);
+    Assert.assertEquals(disabledPartitions.size(), 1, "TestDB0_4 should be disabled");
+    Assert.assertEquals(disabledPartitions.get(0), "TestDB0_4");
+    
     // clean up
     // wait for all zk callbacks done
 //    Thread.sleep(1000);
@@ -131,6 +237,97 @@ public class TestDrop extends ZkIntegrationTestBase
   }
   
   @Test
+  public void testDropErrorPartitionCustomIS() throws Exception
+  {
+    // Logger.getRootLogger().setLevel(Level.INFO);
+    String className = TestHelper.getTestClassName();
+    String methodName = TestHelper.getTestMethodName();
+    String clusterName = className + "_" + methodName;
+    final int n = 2;
+
+    System.out.println("START " + clusterName + " at "
+        + new Date(System.currentTimeMillis()));
+
+    MockParticipant[] participants = new MockParticipant[n];
+
+    TestHelper.setupCluster(clusterName, ZK_ADDR, 12918, // participant port
+                            "localhost", // participant name prefix
+                            "TestDB", // resource name prefix
+                            1, // resources
+                            2, // partitions per resource
+                            n, // number of nodes
+                            2, // replicas
+                            "MasterSlave",
+                            false); // do rebalance
+
+    // set custom ideal-state
+    CustomModeISBuilder isBuilder = new CustomModeISBuilder("TestDB0");
+    isBuilder.setNumPartitions(2);
+    isBuilder.setNumReplica(2);
+    isBuilder.setStateModel("MasterSlave");
+    isBuilder.assignInstanceAndState("TestDB0_0", "localhost_12918", "MASTER");
+    isBuilder.assignInstanceAndState("TestDB0_0", "localhost_12919", "SLAVE");
+    isBuilder.assignInstanceAndState("TestDB0_1", "localhost_12919", "MASTER");
+    isBuilder.assignInstanceAndState("TestDB0_1", "localhost_12918", "SLAVE");
+    
+    HelixDataAccessor accessor = new ZKHelixDataAccessor(clusterName, 
+        new ZkBaseDataAccessor<ZNRecord>(_gZkClient));
+    Builder keyBuiler = accessor.keyBuilder();
+    accessor.setProperty(keyBuiler.idealStates("TestDB0"), isBuilder.build());
+    
+    // start controller
+    ClusterController controller =
+        new ClusterController(clusterName, "controller_0", ZK_ADDR);
+    controller.syncStart();
+    
+    // start participants
+    Map<String, Set<String>> errTransitions = new HashMap<String, Set<String>>();
+    errTransitions.put("SLAVE-MASTER", TestHelper.setOf("TestDB0_0"));
+
+    for (int i = 0; i < n; i++)
+    {
+      String instanceName = "localhost_" + (12918 + i);
+
+      if (i == 0)
+      {
+        participants[i] =
+            new MockParticipant(clusterName,
+                                instanceName,
+                                ZK_ADDR,
+                                new ErrTransition(errTransitions));
+      }
+      else
+      {
+        participants[i] = new MockParticipant(clusterName, instanceName, ZK_ADDR, null);
+      }
+      participants[i].syncStart();
+    }
+
+    Map<String, Map<String, String>> errStateMap =
+        new HashMap<String, Map<String, String>>();
+    errStateMap.put("TestDB0", new HashMap<String, String>());
+    errStateMap.get("TestDB0").put("TestDB0_0", "localhost_12918");
+    boolean result =
+        ClusterStateVerifier.verifyByZkCallback(new BestPossAndExtViewZkVerifier(ZK_ADDR,
+                                                                                 clusterName,
+                                                                                 errStateMap));
+    Assert.assertTrue(result);
+    
+    // drop resource containing error partitions should drop the partition successfully
+    ClusterSetup.processCommandLineArgs(new String[]{"--zkSvr", ZK_ADDR, "--dropResource", clusterName, "TestDB0"});
+
+
+    // make sure TestDB0_0 partition is dropped
+    result =
+        ClusterStateVerifier.verifyByZkCallback(new BestPossAndExtViewZkVerifier(ZK_ADDR,
+                                                                                 clusterName));
+    Assert.assertTrue(result, "Should be empty exeternal-view");
+    
+    System.out.println("END " + clusterName + " at "
+        + new Date(System.currentTimeMillis())); 
+  }
+  
+  @Test
   public void testDropSchemataResource() throws Exception
   {
     // Logger.getRootLogger().setLevel(Level.INFO);

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/test/java/org/apache/helix/integration/TestStateTransitionTimeout.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestStateTransitionTimeout.java b/helix-core/src/test/java/org/apache/helix/integration/TestStateTransitionTimeout.java
index 0a786d7..1b91025 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestStateTransitionTimeout.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestStateTransitionTimeout.java
@@ -116,7 +116,7 @@ public class TestStateTransitionTimeout extends ZkStandAloneCMTestBase
     }
 
     @Transition(to="MASTER",from="SLAVE")
-    public void onBecomeMasterFromSlave(Message message, NotificationContext context) throws InterruptedException
+    public void onBecomeMasterFromSlave(Message message, NotificationContext context)
     {
       LOG.info("Become MASTER from SLAVE");
       if (_transition != null && _sleep)

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/test/java/org/apache/helix/mock/participant/MockMSStateModel.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/mock/participant/MockMSStateModel.java b/helix-core/src/test/java/org/apache/helix/mock/participant/MockMSStateModel.java
index 3ed6e6d..acebf3e 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/participant/MockMSStateModel.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/participant/MockMSStateModel.java
@@ -44,8 +44,21 @@ public class MockMSStateModel extends StateModel
     _transition = transition;
   }
 
+  // overwrite default error->dropped transition
+  @Transition(to = "DROPPED", from = "ERROR")
+  public void onBecomeDroppedFromError(Message message, NotificationContext context)
+  {
+    LOG.info("Become DROPPED from ERROR");
+    if (_transition != null)
+    {
+      _transition.doTransition(message, context);
+
+    }
+  }
+
+  
   @Transition(to = "SLAVE", from = "OFFLINE")
-  public void onBecomeSlaveFromOffline(Message message, NotificationContext context) throws InterruptedException
+  public void onBecomeSlaveFromOffline(Message message, NotificationContext context)
   {
     LOG.info("Become SLAVE from OFFLINE");
     if (_transition != null)
@@ -56,7 +69,7 @@ public class MockMSStateModel extends StateModel
   }
 
   @Transition(to = "MASTER", from = "SLAVE")
-  public void onBecomeMasterFromSlave(Message message, NotificationContext context) throws InterruptedException
+  public void onBecomeMasterFromSlave(Message message, NotificationContext context)
   {
     LOG.info("Become MASTER from SLAVE");
     if (_transition != null)
@@ -66,7 +79,7 @@ public class MockMSStateModel extends StateModel
   }
 
   @Transition(to = "SLAVE", from = "MASTER")
-  public void onBecomeSlaveFromMaster(Message message, NotificationContext context) throws InterruptedException
+  public void onBecomeSlaveFromMaster(Message message, NotificationContext context)
   {
     LOG.info("Become SLAVE from MASTER");
     if (_transition != null)
@@ -76,7 +89,7 @@ public class MockMSStateModel extends StateModel
   }
 
   @Transition(to = "OFFLINE", from = "SLAVE")
-  public void onBecomeOfflineFromSlave(Message message, NotificationContext context) throws InterruptedException
+  public void onBecomeOfflineFromSlave(Message message, NotificationContext context)
   {
     LOG.info("Become OFFLINE from SLAVE");
     if (_transition != null)
@@ -86,7 +99,7 @@ public class MockMSStateModel extends StateModel
   }
 
   @Transition(to = "DROPPED", from = "OFFLINE")
-  public void onBecomeDroppedFromOffline(Message message, NotificationContext context) throws InterruptedException
+  public void onBecomeDroppedFromOffline(Message message, NotificationContext context)
   {
     LOG.info("Become DROPPED from OFFLINE");
     if (_transition != null)
@@ -96,7 +109,7 @@ public class MockMSStateModel extends StateModel
   }
 
   @Transition(to = "OFFLINE", from = "ERROR")
-  public void onBecomeOfflineFromError(Message message, NotificationContext context) throws InterruptedException
+  public void onBecomeOfflineFromError(Message message, NotificationContext context)
   {
     LOG.info("Become OFFLINE from ERROR");
     // System.err.println("Become OFFLINE from ERROR");

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/test/java/org/apache/helix/mock/participant/MockTransition.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/mock/participant/MockTransition.java b/helix-core/src/test/java/org/apache/helix/mock/participant/MockTransition.java
index e646849..bdbcd13 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/participant/MockTransition.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/participant/MockTransition.java
@@ -29,7 +29,7 @@ public class MockTransition
   private static Logger LOG = Logger.getLogger(MockTransition.class);
 
   // called by state model transition functions
-  public void doTransition(Message message, NotificationContext context) throws InterruptedException
+  public void doTransition(Message message, NotificationContext context)
   {
     LOG.info("default doTransition() invoked");
   }

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/test/java/org/apache/helix/mock/participant/SleepTransition.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/mock/participant/SleepTransition.java b/helix-core/src/test/java/org/apache/helix/mock/participant/SleepTransition.java
index fc6d0a1..eacd131 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/participant/SleepTransition.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/participant/SleepTransition.java
@@ -33,9 +33,14 @@ public class SleepTransition extends MockTransition
   }
 
   @Override
-  public void doTransition(Message message, NotificationContext context) throws InterruptedException
+  public void doTransition(Message message, NotificationContext context)
   {
-    Thread.sleep(_delay);
+    try {
+      Thread.sleep(_delay);
+    } catch (InterruptedException e) {
+      // TODO Auto-generated catch block
+      e.printStackTrace();
+    }
 
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessDiffNodeTransition.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessDiffNodeTransition.java b/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessDiffNodeTransition.java
index d3bba54..e6efba6 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessDiffNodeTransition.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessDiffNodeTransition.java
@@ -32,7 +32,7 @@ import org.apache.helix.store.zk.ZkHelixPropertyStore;
 public class StoreAccessDiffNodeTransition extends MockTransition
 {
   @Override
-  public void doTransition(Message message, NotificationContext context) throws InterruptedException
+  public void doTransition(Message message, NotificationContext context)
   {
     HelixManager manager = context.getManager();
     ZkHelixPropertyStore<ZNRecord> store = manager.getHelixPropertyStore();

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessOneNodeTransition.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessOneNodeTransition.java b/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessOneNodeTransition.java
index 44d8f0e..bfa8b22 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessOneNodeTransition.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/participant/StoreAccessOneNodeTransition.java
@@ -32,7 +32,7 @@ import org.apache.helix.store.zk.ZkHelixPropertyStore;
 public class StoreAccessOneNodeTransition extends MockTransition
 {
   @Override
-  public void doTransition(Message message, NotificationContext context) throws InterruptedException
+  public void doTransition(Message message, NotificationContext context)
   {
     HelixManager manager = context.getManager();
     ZkHelixPropertyStore<ZNRecord> store = manager.getHelixPropertyStore();

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/cd8272c9/helix-core/src/test/java/org/apache/helix/participant/statemachine/TestStateModelParser.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/participant/statemachine/TestStateModelParser.java b/helix-core/src/test/java/org/apache/helix/participant/statemachine/TestStateModelParser.java
new file mode 100644
index 0000000..0be990e
--- /dev/null
+++ b/helix-core/src/test/java/org/apache/helix/participant/statemachine/TestStateModelParser.java
@@ -0,0 +1,93 @@
+package org.apache.helix.participant.statemachine;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.lang.reflect.Method;
+
+import org.apache.helix.NotificationContext;
+import org.apache.helix.model.Message;
+import org.apache.log4j.Logger;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class TestStateModelParser {
+
+  private static Logger LOG = Logger.getLogger(TestStateModelParser.class);
+
+  @StateModelInfo(initialState = "OFFLINE", states = { "MASTER", "SLAVE", "ERROR" })
+  class TestStateModel extends StateModel {
+    @Transition(to = "SLAVE", from = "OFFLINE")
+    public void onBecomeSlaveFromOffline(Message message, NotificationContext context) {
+      LOG.info("Become SLAVE from OFFLINE");
+    }
+    
+    @Transition(to = "DROPPED", from = "ERROR")
+    public void onBecomeDroppedFromError(Message message, NotificationContext context) {
+      LOG.info("Become DROPPED from ERROR");
+    }
+
+  }
+  
+  @StateModelInfo(initialState = "OFFLINE", states = { "MASTER", "SLAVE", "ERROR" })
+  class DerivedTestStateModel extends TestStateModel {
+    @Transition(to = "SLAVE", from = "OFFLINE")
+    public void derivedOnBecomeSlaveFromOffline(Message message, NotificationContext context) {
+      LOG.info("Derived Become SLAVE from OFFLINE");
+    }
+  }
+  
+  @Test
+  public void test() {
+    StateModelParser parser = new StateModelParser();
+    TestStateModel testModel = new TestStateModel();
+    
+    Method method = parser.getMethodForTransitionUsingAnnotation(testModel.getClass(),
+        "offline",
+        "slave",
+        new Class[] { Message.class, NotificationContext.class});
+    
+    // System.out.println("method-name: " + method.getName());
+    Assert.assertEquals(method.getName(), "onBecomeSlaveFromOffline");
+  }
+  
+  @Test
+  public void testDerived() {
+    StateModelParser parser = new StateModelParser();
+    DerivedTestStateModel testModel = new DerivedTestStateModel();
+    
+    Method method = parser.getMethodForTransitionUsingAnnotation(testModel.getClass(),
+        "offline",
+        "slave",
+        new Class[] { Message.class, NotificationContext.class});
+    
+    // System.out.println("method-name: " + method.getName());
+    Assert.assertEquals(method.getName(), "derivedOnBecomeSlaveFromOffline");
+    
+    
+    method = parser.getMethodForTransitionUsingAnnotation(testModel.getClass(),
+        "error",
+        "dropped",
+        new Class[] { Message.class, NotificationContext.class});
+
+    // System.out.println("method: " + method);
+    Assert.assertEquals(method.getName(), "onBecomeDroppedFromError");
+
+  }
+}


Mime
View raw message