helix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From zzh...@apache.org
Subject git commit: HELIX-46: Add cmd admin command for message constraints
Date Tue, 26 Mar 2013 00:34:18 GMT
Updated Branches:
  refs/heads/master 9db0fd8fe -> 142dab542


HELIX-46: Add cmd admin command for message constraints


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

Branch: refs/heads/master
Commit: 142dab54227a005b33c8d1e58e8b216020bccade
Parents: 9db0fd8
Author: zzhang <zzhang5@uci.edu>
Authored: Mon Mar 25 17:34:11 2013 -0700
Committer: zzhang <zzhang5@uci.edu>
Committed: Mon Mar 25 17:34:11 2013 -0700

----------------------------------------------------------------------
 .../src/main/java/org/apache/helix/HelixAdmin.java |   22 ++-
 .../controller/stages/MessageThrottleStage.java    |    2 +-
 .../org/apache/helix/manager/zk/ZKHelixAdmin.java  |   77 ++++---
 .../org/apache/helix/model/ClusterConstraints.java |  164 +++++----------
 .../org/apache/helix/model/ConstraintItem.java     |  101 +++++++++
 .../model/builder/ClusterConstraintsBuilder.java   |   73 +++++++
 .../helix/model/builder/ConstraintItemBuilder.java |  111 ++++++++++
 .../stages/TestMessageThrottleStage.java           |    2 +-
 .../helix/integration/TestMessageThrottle.java     |   20 ++-
 .../helix/integration/TestSchedulerMessage.java    |   10 +-
 .../apache/helix/manager/zk/TestZkHelixAdmin.java  |   67 ++++++
 .../org/apache/helix/model/TestConstraint.java     |    1 -
 12 files changed, 498 insertions(+), 152 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/142dab54/helix-core/src/main/java/org/apache/helix/HelixAdmin.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/HelixAdmin.java b/helix-core/src/main/java/org/apache/helix/HelixAdmin.java
index 903516d..682b8b7 100644
--- a/helix-core/src/main/java/org/apache/helix/HelixAdmin.java
+++ b/helix-core/src/main/java/org/apache/helix/HelixAdmin.java
@@ -404,16 +404,32 @@ public interface HelixAdmin
                         String stateModelDefFile) throws IOException;
 
   /**
-   * Add a message constraint
+   * Add a message constraint item
    * 
    * @param constraintId
-   * @param constraints
+   * @param constraintItem
    */
   void addMessageConstraint(String clusterName,
                             String constraintId,
-                            Map<String, String> constraints);
+                            ConstraintItem constraintItem);
 
   /**
+   * Remove a message constraint item
+   * 
+   * @param clusterName
+   * @param constraintId
+   */
+  void removeMessageConstraint(String clusterName, String constraintId);
+  
+  /**
+   * Get all message constraints for a cluster
+   * 
+   * @param clusterName
+   * @return
+   */
+  ClusterConstraints getMessageConstraints(String clusterName);
+  
+  /**
    * 
    * @param clusterName
    * @param currentIdealState

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/142dab54/helix-core/src/main/java/org/apache/helix/controller/stages/MessageThrottleStage.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/controller/stages/MessageThrottleStage.java
b/helix-core/src/main/java/org/apache/helix/controller/stages/MessageThrottleStage.java
index 0ab7e85..2cedb72 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/stages/MessageThrottleStage.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/stages/MessageThrottleStage.java
@@ -33,7 +33,7 @@ import org.apache.helix.model.Message;
 import org.apache.helix.model.Partition;
 import org.apache.helix.model.Resource;
 import org.apache.helix.model.ClusterConstraints.ConstraintAttribute;
-import org.apache.helix.model.ClusterConstraints.ConstraintItem;
+import org.apache.helix.model.ConstraintItem;
 import org.apache.helix.model.ClusterConstraints.ConstraintType;
 import org.apache.helix.model.ClusterConstraints.ConstraintValue;
 import org.apache.log4j.Logger;

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/142dab54/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 67ba6f1..38da4e7 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
@@ -40,35 +40,36 @@ import org.I0Itec.zkclient.DataUpdater;
 import org.I0Itec.zkclient.exception.ZkNoNodeException;
 import org.apache.helix.AccessOption;
 import org.apache.helix.ConfigAccessor;
-import org.apache.helix.model.ConfigScope;
 import org.apache.helix.HelixAdmin;
 import org.apache.helix.HelixConstants;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixException;
 import org.apache.helix.PropertyKey;
+import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.PropertyPathConfig;
 import org.apache.helix.PropertyType;
 import org.apache.helix.ZNRecord;
-import org.apache.helix.model.ConfigScope.ConfigScopeProperty;
-import org.apache.helix.PropertyKey.Builder;
 import org.apache.helix.alerts.AlertsHolder;
 import org.apache.helix.alerts.StatsHolder;
 import org.apache.helix.model.Alerts;
+import org.apache.helix.model.ClusterConstraints;
+import org.apache.helix.model.ClusterConstraints.ConstraintType;
+import org.apache.helix.model.ConfigScope;
+import org.apache.helix.model.ConfigScope.ConfigScopeProperty;
+import org.apache.helix.model.ConstraintItem;
 import org.apache.helix.model.CurrentState;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.IdealState;
+import org.apache.helix.model.IdealState.IdealStateModeProperty;
 import org.apache.helix.model.InstanceConfig;
+import org.apache.helix.model.InstanceConfig.InstanceConfigProperty;
 import org.apache.helix.model.LiveInstance;
 import org.apache.helix.model.Message;
+import org.apache.helix.model.Message.MessageState;
+import org.apache.helix.model.Message.MessageType;
 import org.apache.helix.model.PauseSignal;
 import org.apache.helix.model.PersistentStats;
 import org.apache.helix.model.StateModelDefinition;
-import org.apache.helix.model.ClusterConstraints.ConstraintAttribute;
-import org.apache.helix.model.ClusterConstraints.ConstraintType;
-import org.apache.helix.model.IdealState.IdealStateModeProperty;
-import org.apache.helix.model.InstanceConfig.InstanceConfigProperty;
-import org.apache.helix.model.Message.MessageState;
-import org.apache.helix.model.Message.MessageType;
 import org.apache.helix.tools.DefaultIdealStateCalculator;
 import org.apache.helix.util.HelixUtil;
 import org.apache.helix.util.RebalanceUtil;
@@ -1278,9 +1279,10 @@ public class ZKHelixAdmin implements HelixAdmin
 
   }
 
+  @Override
   public void addMessageConstraint(String clusterName,
                                    final String constraintId,
-                                   final Map<String, String> constraints)
+                                   final ConstraintItem constraintItem)
   {
     ZkBaseDataAccessor<ZNRecord> baseAccessor =
         new ZkBaseDataAccessor<ZNRecord>(_zkClient);
@@ -1293,34 +1295,49 @@ public class ZKHelixAdmin implements HelixAdmin
       @Override
       public ZNRecord update(ZNRecord currentData)
       {
-        if (currentData == null)
-        {
-          currentData = new ZNRecord(ConstraintType.MESSAGE_CONSTRAINT.toString());
-        }
+        ClusterConstraints constraints = currentData == null? 
+            new ClusterConstraints(ConstraintType.MESSAGE_CONSTRAINT) : new ClusterConstraints(currentData);
 
-        Map<String, String> map = currentData.getMapField(constraintId);
-        if (map == null)
-        {
-          map = new TreeMap<String, String>();
-          currentData.setMapField(constraintId, map);
-        } else
-        {
-          logger.warn("Overwrite existing constraint " + constraintId + ": " + map);
-        }
+        constraints.addConstraintItem(constraintId, constraintItem);
+        return constraints.getRecord();
+      }
+    }, AccessOption.PERSISTENT);
+  }
+  
+  @Override
+  public void removeMessageConstraint(String clusterName, final String constraintId)
+  {
+    ZkBaseDataAccessor<ZNRecord> baseAccessor =
+        new ZkBaseDataAccessor<ZNRecord>(_zkClient);
 
-        for (String key : constraints.keySet())
-        {
-          // make sure constraint attribute is valid
-          ConstraintAttribute attr = ConstraintAttribute.valueOf(key.toUpperCase());
+    Builder keyBuilder = new Builder(clusterName);
+    String path = keyBuilder.constraint(ConstraintType.MESSAGE_CONSTRAINT.toString()).getPath();
+
+    baseAccessor.update(path, new DataUpdater<ZNRecord>()
+    {
+      @Override
+      public ZNRecord update(ZNRecord currentData)
+      {
+        if (currentData != null) {
+          ClusterConstraints constraints = new ClusterConstraints(currentData);
 
-          map.put(attr.toString(), constraints.get(key));
+          constraints.removeConstraintItem(constraintId);
+          return constraints.getRecord();
         }
-        
-        return currentData;
+        return null;
       }
     }, AccessOption.PERSISTENT);
   }
   
+  @Override
+  public ClusterConstraints getMessageConstraints(String clusterName)
+  {
+    HelixDataAccessor accessor =
+        new ZKHelixDataAccessor(clusterName, new ZkBaseDataAccessor<ZNRecord>(_zkClient));
+
+    Builder keyBuilder = new Builder(clusterName);
+    return accessor.getProperty(keyBuilder.constraint(ConstraintType.MESSAGE_CONSTRAINT.toString()));
+  }
   
   /**
    * Takes the existing idealstate as input and computes newIdealState such that 

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/142dab54/helix-core/src/main/java/org/apache/helix/model/ClusterConstraints.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/model/ClusterConstraints.java b/helix-core/src/main/java/org/apache/helix/model/ClusterConstraints.java
index 2259b51..30ff526 100644
--- a/helix-core/src/main/java/org/apache/helix/model/ClusterConstraints.java
+++ b/helix-core/src/main/java/org/apache/helix/model/ClusterConstraints.java
@@ -19,10 +19,8 @@ package org.apache.helix.model;
  * under the License.
  */
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
@@ -30,6 +28,7 @@ import java.util.TreeMap;
 import org.apache.helix.HelixProperty;
 import org.apache.helix.ZNRecord;
 import org.apache.helix.model.Message.MessageType;
+import org.apache.helix.model.builder.ConstraintItemBuilder;
 import org.apache.log4j.Logger;
 
 
@@ -52,131 +51,82 @@ public class ClusterConstraints extends HelixProperty
     STATE_CONSTRAINT, MESSAGE_CONSTRAINT
   }
 
-  static public class ConstraintItem
-  {
-    // attributes e.g. {STATE:MASTER, RESOURCEG:TestDB, INSTANCE:localhost_12918}
-    final Map<ConstraintAttribute, String> _attributes;
-    String _constraintValue;
-
-    public ConstraintItem(Map<String, String> attributes)
-    {
-      _attributes = new TreeMap<ConstraintAttribute, String>();
-      _constraintValue = null;
-
-      if (attributes != null)
-      {
-        for (String key : attributes.keySet())
-        {
-          try
-          {
-            ConstraintAttribute attr = ConstraintAttribute.valueOf(key);
-            if (attr == ConstraintAttribute.CONSTRAINT_VALUE)
-            {
-              String value = attributes.get(key);
-              try
-              {
-                ConstraintValue.valueOf(value);
-              } catch (Exception e)
-              {
-                try
-                {
-                  Integer.parseInt(value);
-                }
-                catch (NumberFormatException ne)
-                {
-                  LOG.error("Invalid constraintValue " + key + ":" + value);
-                  continue;
-                }
-              }
-              _constraintValue = attributes.get(key);
-            } else
-            {
-              _attributes.put(attr, attributes.get(key));
-            }
-          } catch (Exception e)
-          {
-            LOG.error("Invalid constraintAttribute " + key + ":" + attributes.get(key));
-            continue;
-          }
-        }
-      }
-    }
-
-    public boolean match(Map<ConstraintAttribute, String> attributes)
-    {
-      for (ConstraintAttribute key : _attributes.keySet())
-      {
-        if (!attributes.containsKey(key))
-        {
-          return false;
-        }
-
-        if (!attributes.get(key).matches(_attributes.get(key)))
-        {
-          return false;
-        }
-      }
-      return true;
-    }
-
-    // filter out attributes that are not specified by this constraint
-    public Map<ConstraintAttribute, String> filter(Map<ConstraintAttribute, String>
attributes)
-    {
-      Map<ConstraintAttribute, String> ret = new HashMap<ConstraintAttribute, String>();
-      for (ConstraintAttribute key : _attributes.keySet())
-      {
-        // TODO: what if attributes.get(key)==null? might need match function at constrait
level  
-        ret.put(key, attributes.get(key));
-      }
-
-      return ret;
-    }
-
-    public String getConstraintValue()
-    {
-      return _constraintValue;
-    }
-
-    public Map<ConstraintAttribute, String> getAttributes()
-    {
-      return _attributes;
-    }
-
-    @Override
-    public String toString()
-    {
-      StringBuffer sb = new StringBuffer();
-      sb.append(_attributes + ":" + _constraintValue);
-      return sb.toString();
-    }
+  // constraint-id -> constraint-item
+  private final Map<String, ConstraintItem> _constraints = new HashMap<String, ConstraintItem>();
+  
+  public ClusterConstraints(ConstraintType type) {
+    super(type.toString());
   }
-
-  private final List<ConstraintItem> _constraints = new ArrayList<ConstraintItem>();
-
+  
   public ClusterConstraints(ZNRecord record)
   {
     super(record);
 
-    for (String key : _record.getMapFields().keySet())
+    for (String constraintId : _record.getMapFields().keySet())
     {
-      ConstraintItem item = new ConstraintItem(_record.getMapField(key));
+      ConstraintItemBuilder builder = new ConstraintItemBuilder();
+      ConstraintItem item =  builder.addConstraintAttributes(_record.getMapField(constraintId)).build();
+      // ignore item with empty attributes or no constraint-value
       if (item.getAttributes().size() > 0 && item.getConstraintValue() != null)
       {
-        _constraints.add(item);
+        addConstraintItem(constraintId, item);
       } else
       {
-        LOG.error("Invalid constraint " + key + ":" + _record.getMapField(key));
+        LOG.error("Skip invalid constraint. key: " + constraintId + ", value: " 
+              + _record.getMapField(constraintId));
       }
     }
   }
 
   /**
+   * add the constraint, overwrite existing one if constraint with same constraint-id already
exists
+   * 
+   * @param constraintId
+   * @param item
+   */
+  public void addConstraintItem(String constraintId, ConstraintItem item) {
+    Map<String, String> map = new TreeMap<String, String>();
+    for (ConstraintAttribute attr : item.getAttributes().keySet()) {
+      map.put(attr.toString(), item.getAttributeValue(attr));
+    }
+    map.put(ConstraintAttribute.CONSTRAINT_VALUE.toString(), item.getConstraintValue());
+    _record.setMapField(constraintId, map);
+    _constraints.put(constraintId, item);
+  }
+  
+  public void addConstraintItems(Map<String, ConstraintItem> items) {
+    for (String constraintId : items.keySet()) {
+      addConstraintItem(constraintId, items.get(constraintId));
+    }
+  }
+  
+  /**
+   * remove a constraint-item
+   * 
+   * @param constraintId
+   */
+  public void removeConstraintItem(String constraintId) {
+    _constraints.remove(constraintId);
+    _record.getMapFields().remove(constraintId);
+  }
+  
+  /**
+   * get a constraint-item
+   * 
+   * @param constraintId
+   * @return
+   */
+  public ConstraintItem getConstraintItem(String constraintId) {
+    return _constraints.get(constraintId);
+  }
+  
+  /**
    * return a set of constraints that match the attribute pairs
    */
   public Set<ConstraintItem> match(Map<ConstraintAttribute, String> attributes)
   {
     Set<ConstraintItem> matches = new HashSet<ConstraintItem>();
-    for (ConstraintItem item : _constraints)
+    for (ConstraintItem item : _constraints.values())
     {
       if (item.match(attributes))
       {

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/142dab54/helix-core/src/main/java/org/apache/helix/model/ConstraintItem.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/model/ConstraintItem.java b/helix-core/src/main/java/org/apache/helix/model/ConstraintItem.java
new file mode 100644
index 0000000..ad2f763
--- /dev/null
+++ b/helix-core/src/main/java/org/apache/helix/model/ConstraintItem.java
@@ -0,0 +1,101 @@
+package org.apache.helix.model;
+
+/*
+ * 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.util.HashMap;
+import java.util.Map;
+
+import org.apache.helix.model.ClusterConstraints.ConstraintAttribute;
+import org.apache.helix.model.builder.ConstraintItemBuilder;
+import org.apache.log4j.Logger;
+
+public class ConstraintItem {
+  private static Logger LOG = Logger.getLogger(ConstraintItem.class);
+
+  // attributes e.g. {STATE:MASTER, RESOURCE:TestDB, INSTANCE:localhost_12918}
+  final Map<ConstraintAttribute, String> _attributes;
+  final String _constraintValue;
+
+  public ConstraintItem(Map<String, String> attributes) {
+    ConstraintItemBuilder builder = new ConstraintItemBuilder();
+    builder.addConstraintAttributes(attributes);
+    _attributes = builder.getAttributes();
+    _constraintValue = builder.getConstraintValue();
+  }
+  
+  public ConstraintItem(Map<ConstraintAttribute, String> attributes, String constraintValue)
+  {
+    _attributes = attributes;
+    _constraintValue = constraintValue;
+  }
+
+  public boolean match(Map<ConstraintAttribute, String> attributes)
+  {
+    for (ConstraintAttribute key : _attributes.keySet())
+    {
+      if (!attributes.containsKey(key))
+      {
+        return false;
+      }
+
+      if (!attributes.get(key).matches(_attributes.get(key)))
+      {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  // filter out attributes that are not specified by this constraint
+  public Map<ConstraintAttribute, String> filter(Map<ConstraintAttribute, String>
attributes)
+  {
+    Map<ConstraintAttribute, String> ret = new HashMap<ConstraintAttribute, String>();
+    for (ConstraintAttribute key : _attributes.keySet())
+    {
+      // TODO: what if attributes.get(key)==null? might need match function at constrait
level  
+      ret.put(key, attributes.get(key));
+    }
+
+    return ret;
+  }
+
+  public String getConstraintValue()
+  {
+    return _constraintValue;
+  }
+
+  public Map<ConstraintAttribute, String> getAttributes()
+  {
+    return _attributes;
+  }
+  
+  public String getAttributeValue(ConstraintAttribute attr) {
+    return _attributes.get(attr);
+  }
+
+  @Override
+  public String toString()
+  {
+    StringBuffer sb = new StringBuffer();
+    sb.append(_attributes + ":" + _constraintValue);
+    return sb.toString();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/142dab54/helix-core/src/main/java/org/apache/helix/model/builder/ClusterConstraintsBuilder.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/model/builder/ClusterConstraintsBuilder.java
b/helix-core/src/main/java/org/apache/helix/model/builder/ClusterConstraintsBuilder.java
new file mode 100644
index 0000000..63020d6
--- /dev/null
+++ b/helix-core/src/main/java/org/apache/helix/model/builder/ClusterConstraintsBuilder.java
@@ -0,0 +1,73 @@
+package org.apache.helix.model.builder;
+
+/*
+ * 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.util.HashMap;
+import java.util.Map;
+
+import org.apache.helix.model.ClusterConstraints;
+import org.apache.helix.model.ClusterConstraints.ConstraintType;
+import org.apache.log4j.Logger;
+
+public class ClusterConstraintsBuilder {
+  private static Logger LOG = Logger.getLogger(ClusterConstraintsBuilder.class);
+
+  final private ConstraintType _constraintType;
+  
+  /**
+   * constraint-id -> constraint-item-builder
+   * 
+   *   e.g. constraint_1: at most 1 offline->slave state transition message for MyDB:
+   *   constraint_1:
+   *     MESSAGE_TYPE     : StateTransition
+   *     RESOURCE         : MyDB
+   *     TRANSITION       : OFFLINE->SLAVE
+   *     CONSTRAINT_VALUE : 1     
+   *     
+   */
+  private final Map<String, ConstraintItemBuilder> _constraintBuilderMap = new HashMap<String,
ConstraintItemBuilder>();
+
+
+  public ClusterConstraintsBuilder(ConstraintType type) {
+    if (type == null) {
+      throw new IllegalArgumentException("constraint-type should NOT be null");
+    }
+    _constraintType = type;
+  }
+  
+  public ClusterConstraintsBuilder addConstraintAttribute(String constraintId, String attribute,
String value) {
+    if (!_constraintBuilderMap.containsKey(constraintId)) {
+      _constraintBuilderMap.put(constraintId, new ConstraintItemBuilder());
+    }
+    ConstraintItemBuilder builder =  _constraintBuilderMap.get(constraintId);
+    builder.addConstraintAttribute(attribute, value);
+    return this;
+  }
+  
+  public ClusterConstraints build() {
+    ClusterConstraints constraints = new ClusterConstraints(_constraintType);
+    
+    for (String constraintId : _constraintBuilderMap.keySet()) {
+      ConstraintItemBuilder builder = _constraintBuilderMap.get(constraintId);
+      constraints.addConstraintItem(constraintId, builder.build());
+    }
+    return constraints;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/142dab54/helix-core/src/main/java/org/apache/helix/model/builder/ConstraintItemBuilder.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/model/builder/ConstraintItemBuilder.java
b/helix-core/src/main/java/org/apache/helix/model/builder/ConstraintItemBuilder.java
new file mode 100644
index 0000000..ed788b2
--- /dev/null
+++ b/helix-core/src/main/java/org/apache/helix/model/builder/ConstraintItemBuilder.java
@@ -0,0 +1,111 @@
+package org.apache.helix.model.builder;
+
+/*
+ * 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.util.Map;
+import java.util.TreeMap;
+
+import org.apache.helix.model.ConstraintItem;
+import org.apache.helix.model.ClusterConstraints.ConstraintAttribute;
+import org.apache.helix.model.ClusterConstraints.ConstraintValue;
+import org.apache.log4j.Logger;
+
+
+public class ConstraintItemBuilder {
+
+  private static Logger LOG = Logger.getLogger(ConstraintItemBuilder.class);
+  
+  // attributes e.g. {STATE:MASTER, RESOURCE:TestDB, INSTANCE:localhost_12918}
+  final Map<ConstraintAttribute, String> _attributes = new TreeMap<ConstraintAttribute,
String>();
+  String _constraintValue = null;
+
+  /**
+   * add an attribute to constraint-item, overwrite if already exists
+   * 
+   * @param attribute
+   * @param value
+   */
+  public ConstraintItemBuilder addConstraintAttribute(String attribute, String value) {
+    // make sure constraint attribute is valid
+    try {
+      ConstraintAttribute attr = ConstraintAttribute.valueOf(attribute.toUpperCase());
+      
+      if (attr == ConstraintAttribute.CONSTRAINT_VALUE) {
+        // make sure constraint-value is valid
+        try
+        {
+          ConstraintValue.valueOf(value);
+          if (_constraintValue == null) {
+            LOG.info("overwrite existing constraint-value. old-value: " + _constraintValue

+                + ", new-value: " + value);
+          }
+          _constraintValue = value;
+        } catch (IllegalArgumentException e)
+        {
+          try
+          {
+            Integer.parseInt(value);
+            if (_constraintValue == null) {
+              LOG.info("overwrite existing constraint-value. old-value: " + _constraintValue

+                  + ", new-value: " + value);
+            }
+            _constraintValue = value;
+          }
+          catch (NumberFormatException ne)
+          {
+            LOG.error("fail to add constraint attribute. Invalid constraintValue. " 
+              + attribute + ": " + attribute + ", value: " + value);
+          }
+        }
+      } else {
+        if (_attributes.containsKey(attr)) {
+          LOG.info("overwrite existing constraint attribute. attribute: " 
+            + attribute + ", old-value: " + _attributes.get(attr) + ", new-value: " + value);
+        }
+        _attributes.put(attr, value);
+      }
+    } catch (IllegalArgumentException e) {
+      LOG.error("fail to add constraint attribute. Invalid attribute type. attribute: " 
+          + attribute + ", value: " + value);
+    }
+    
+    return this;
+  }
+  
+  public ConstraintItemBuilder addConstraintAttributes(Map<String, String> attributes)
{
+    for (String attr : attributes.keySet()) {
+      addConstraintAttribute(attr, attributes.get(attr));
+    }
+    return this;
+  }
+  
+  public Map<ConstraintAttribute, String> getAttributes() {
+    return _attributes;
+  }
+  
+  public String getConstraintValue() {
+    return _constraintValue;
+  }
+  
+  public ConstraintItem build() {
+    // TODO: check if constraint-item is valid
+    return new ConstraintItem(_attributes, _constraintValue);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/142dab54/helix-core/src/test/java/org/apache/helix/controller/stages/TestMessageThrottleStage.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/controller/stages/TestMessageThrottleStage.java
b/helix-core/src/test/java/org/apache/helix/controller/stages/TestMessageThrottleStage.java
index 87fda22..6064e3a 100644
--- a/helix-core/src/test/java/org/apache/helix/controller/stages/TestMessageThrottleStage.java
+++ b/helix-core/src/test/java/org/apache/helix/controller/stages/TestMessageThrottleStage.java
@@ -42,10 +42,10 @@ import org.apache.helix.controller.stages.ResourceComputationStage;
 import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.helix.model.ClusterConstraints;
+import org.apache.helix.model.ConstraintItem;
 import org.apache.helix.model.Message;
 import org.apache.helix.model.Partition;
 import org.apache.helix.model.ClusterConstraints.ConstraintAttribute;
-import org.apache.helix.model.ClusterConstraints.ConstraintItem;
 import org.apache.helix.model.ClusterConstraints.ConstraintType;
 import org.apache.helix.model.Message.MessageType;
 import org.apache.log4j.Logger;

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/142dab54/helix-core/src/test/java/org/apache/helix/integration/TestMessageThrottle.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestMessageThrottle.java
b/helix-core/src/test/java/org/apache/helix/integration/TestMessageThrottle.java
index ea18fd0..8fa56c6 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestMessageThrottle.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestMessageThrottle.java
@@ -38,8 +38,11 @@ import org.apache.helix.manager.zk.ZKHelixDataAccessor;
 import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.helix.mock.participant.MockParticipant;
 import org.apache.helix.model.ClusterConstraints;
+import org.apache.helix.model.ConstraintItem;
 import org.apache.helix.model.Message;
 import org.apache.helix.model.ClusterConstraints.ConstraintType;
+import org.apache.helix.model.builder.ClusterConstraintsBuilder;
+import org.apache.helix.model.builder.ConstraintItemBuilder;
 import org.apache.helix.tools.ClusterStateVerifier;
 import org.apache.helix.tools.ClusterStateVerifier.BestPossAndExtViewZkVerifier;
 import org.apache.helix.tools.ClusterStateVerifier.MasterNbInExtViewVerifier;
@@ -75,12 +78,17 @@ public class TestMessageThrottle extends ZkIntegrationTestBase
     // setup message constraint
     // "MESSAGE_TYPE=STATE_TRANSITION,TRANSITION=OFFLINE-SLAVE,INSTANCE=.*,CONSTRAINT_VALUE=1";
     HelixAdmin admin = new ZKHelixAdmin(_gZkClient);
-    Map<String, String> constraints = new TreeMap<String, String>();
-    constraints.put("MESSAGE_TYPE", "STATE_TRANSITION");
-    // constraints.put("TRANSITION", "OFFLINE-SLAVE");
-    constraints.put("CONSTRAINT_VALUE", "1");
-    constraints.put("INSTANCE", ".*");
-    admin.addMessageConstraint(clusterName, "constraint1", constraints);
+    ConstraintItemBuilder builder = new ConstraintItemBuilder();
+    builder.addConstraintAttribute("MESSAGE_TYPE", "STATE_TRANSITION")
+           .addConstraintAttribute("InSTANCE", ".*")
+           .addConstraintAttribute("CONSTRAINT_VALUE", "1");
+    
+//    Map<String, String> constraints = new TreeMap<String, String>();
+//    constraints.put("MESSAGE_TYPE", "STATE_TRANSITION");
+//    // constraints.put("TRANSITION", "OFFLINE-SLAVE");
+//    constraints.put("CONSTRAINT_VALUE", "1");
+//    constraints.put("INSTANCE", ".*");
+    admin.addMessageConstraint(clusterName, "constraint1", builder.build());
     
 
     final ZKHelixDataAccessor accessor =

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/142dab54/helix-core/src/test/java/org/apache/helix/integration/TestSchedulerMessage.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestSchedulerMessage.java
b/helix-core/src/test/java/org/apache/helix/integration/TestSchedulerMessage.java
index 627e1cb..bae97d5 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestSchedulerMessage.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestSchedulerMessage.java
@@ -45,6 +45,7 @@ import org.apache.helix.messaging.AsyncCallback;
 import org.apache.helix.messaging.handling.HelixTaskResult;
 import org.apache.helix.messaging.handling.MessageHandler;
 import org.apache.helix.messaging.handling.MessageHandlerFactory;
+import org.apache.helix.model.ConstraintItem;
 import org.apache.helix.model.Message;
 import org.apache.helix.model.Message.MessageState;
 import org.apache.helix.model.Message.MessageType;
@@ -733,7 +734,8 @@ public class TestSchedulerMessage extends ZkStandAloneCMTestBaseWithPropertyServ
     constraints.put("TRANSITION", "OFFLINE-COMPLETED");
     constraints.put("CONSTRAINT_VALUE", "1");
     constraints.put("INSTANCE", ".*");
-    manager.getClusterManagmentTool().addMessageConstraint(manager.getClusterName(), "constraint1",
constraints);
+    manager.getClusterManagmentTool().addMessageConstraint(manager.getClusterName(), "constraint1",

+        new ConstraintItem(constraints));
     
     MockAsyncCallback callback = new MockAsyncCallback();
     cr.setInstanceName("localhost_%");
@@ -907,7 +909,8 @@ public class TestSchedulerMessage extends ZkStandAloneCMTestBaseWithPropertyServ
     constraints.put("TRANSITION", "OFFLINE-COMPLETED");
     constraints.put("CONSTRAINT_VALUE", "1");
     constraints.put("INSTANCE", ".*");
-    manager.getClusterManagmentTool().addMessageConstraint(manager.getClusterName(), "constraint1",
constraints);
+    manager.getClusterManagmentTool().addMessageConstraint(manager.getClusterName(), "constraint1",

+        new ConstraintItem(constraints));
     
     // Send scheduler message
     crString = sw.toString();
@@ -979,7 +982,8 @@ public class TestSchedulerMessage extends ZkStandAloneCMTestBaseWithPropertyServ
     }
     Assert.assertEquals(count, _PARTITIONS * 3);
     
-    manager.getClusterManagmentTool().addMessageConstraint(manager.getClusterName(), "constraint1",
new TreeMap<String, String>());
+    manager.getClusterManagmentTool().addMessageConstraint(manager.getClusterName(), "constraint1",

+        new ConstraintItem(new TreeMap<String, String>()));
     
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/142dab54/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkHelixAdmin.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkHelixAdmin.java b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkHelixAdmin.java
index 3684362..bc42493 100644
--- a/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkHelixAdmin.java
+++ b/helix-core/src/test/java/org/apache/helix/manager/zk/TestZkHelixAdmin.java
@@ -25,11 +25,16 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.helix.*;
+import org.apache.helix.model.ClusterConstraints;
+import org.apache.helix.model.ClusterConstraints.ConstraintAttribute;
+import org.apache.helix.model.ClusterConstraints.ConstraintType;
 import org.apache.helix.model.ConfigScope;
+import org.apache.helix.model.ConstraintItem;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.model.StateModelDefinition;
 import org.apache.helix.model.builder.ConfigScopeBuilder;
+import org.apache.helix.model.builder.ConstraintItemBuilder;
 import org.apache.helix.tools.StateModelConfigGenerator;
 import org.testng.Assert;
 import org.testng.AssertJUnit;
@@ -194,6 +199,7 @@ public class TestZkHelixAdmin extends ZkUnitTestBase
     System.out.println("END testZkHelixAdmin at " + new Date(System.currentTimeMillis()));
   }
 
+    // drop resource should drop corresponding resource-level config also
     @Test
     public void testDropResource() {
         String className = TestHelper.getTestClassName();
@@ -224,4 +230,65 @@ public class TestZkHelixAdmin extends ZkUnitTestBase
         System.out.println("END " + clusterName + " at "
                 + new Date(System.currentTimeMillis()));
     }
+    
+    // test add/remove message constraint
+    @Test
+    public void testAddRemoveMsgConstraint() {
+      String className = TestHelper.getTestClassName();
+      String methodName = TestHelper.getTestMethodName();
+      String clusterName = className + "_" + methodName;
+
+      System.out.println("START " + clusterName + " at "
+              + new Date(System.currentTimeMillis()));
+
+      ZKHelixAdmin tool = new ZKHelixAdmin(_gZkClient);
+      tool.addCluster(clusterName, true);
+      Assert.assertTrue(ZKUtil.isClusterSetup(clusterName, _gZkClient), "Cluster should be
setup");
+
+      // test admin.getMessageConstraints()
+      ClusterConstraints constraints = tool.getMessageConstraints(clusterName);
+      Assert.assertNull(constraints, "message-constraint should NOT exist for cluster: "
+ className);
+
+      // remove non-exist constraint
+      try {
+        tool.removeMessageConstraint(clusterName, "constraint1");
+        // will leave a null message-constraint znode on zk
+      } catch (Exception e) {
+        Assert.fail("Should not throw exception when remove a non-exist constraint.");
+      }
+
+      // add a message constraint
+      ConstraintItemBuilder builder = new ConstraintItemBuilder();
+      builder.addConstraintAttribute(ConstraintAttribute.RESOURCE.toString(), "MyDB")
+             .addConstraintAttribute(ConstraintAttribute.CONSTRAINT_VALUE.toString(), "1");
+      tool.addMessageConstraint(clusterName, "constraint1", builder.build());
+
+      HelixDataAccessor accessor = new ZKHelixDataAccessor(clusterName, new ZkBaseDataAccessor<ZNRecord>(_gZkClient));
+      PropertyKey.Builder keyBuilder = new PropertyKey.Builder(clusterName);
+      constraints = accessor.getProperty(keyBuilder.constraint(ConstraintType.MESSAGE_CONSTRAINT.toString()));
+      Assert.assertNotNull(constraints, "message-constraint should exist");
+      ConstraintItem item = constraints.getConstraintItem("constraint1");
+      Assert.assertNotNull(item, "message-constraint for constraint1 should exist");
+      Assert.assertEquals(item.getConstraintValue(), "1");
+      Assert.assertEquals(item.getAttributeValue(ConstraintAttribute.RESOURCE), "MyDB");
+      
+      // test admin.getMessageConstraints()
+      constraints = tool.getMessageConstraints(clusterName);
+      Assert.assertNotNull(constraints, "message-constraint should exist");
+      item = constraints.getConstraintItem("constraint1");
+      Assert.assertNotNull(item, "message-constraint for constraint1 should exist");
+      Assert.assertEquals(item.getConstraintValue(), "1");
+      Assert.assertEquals(item.getAttributeValue(ConstraintAttribute.RESOURCE), "MyDB");
+      
+
+      // remove a exist message-constraint
+      tool.removeMessageConstraint(clusterName, "constraint1");
+      constraints = accessor.getProperty(keyBuilder.constraint(ConstraintType.MESSAGE_CONSTRAINT.toString()));
+      Assert.assertNotNull(constraints, "message-constraint should exist");
+      item = constraints.getConstraintItem("constraint1");
+      Assert.assertNull(item, "message-constraint for constraint1 should NOT exist");
+
+      System.out.println("END " + clusterName + " at "
+              + new Date(System.currentTimeMillis()));
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/142dab54/helix-core/src/test/java/org/apache/helix/model/TestConstraint.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/model/TestConstraint.java b/helix-core/src/test/java/org/apache/helix/model/TestConstraint.java
index b9df378..bddc519 100644
--- a/helix-core/src/test/java/org/apache/helix/model/TestConstraint.java
+++ b/helix-core/src/test/java/org/apache/helix/model/TestConstraint.java
@@ -34,7 +34,6 @@ import org.apache.helix.manager.zk.ZkBaseDataAccessor;
 import org.apache.helix.model.ClusterConstraints;
 import org.apache.helix.model.Message;
 import org.apache.helix.model.ClusterConstraints.ConstraintAttribute;
-import org.apache.helix.model.ClusterConstraints.ConstraintItem;
 import org.apache.helix.model.ClusterConstraints.ConstraintType;
 import org.apache.helix.model.Message.MessageType;
 import org.apache.log4j.Logger;


Mime
View raw message