You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@helix.apache.org by zz...@apache.org on 2014/12/03 03:26:20 UTC

helix git commit: [HELIX-546] Add REST API for Helix job queue management - third part, rb=28620

Repository: helix
Updated Branches:
  refs/heads/helix-0.6.x aa2e968f7 -> 9a5dbeaa6


[HELIX-546] Add REST API for Helix job queue management - third part, rb=28620


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

Branch: refs/heads/helix-0.6.x
Commit: 9a5dbeaa624715e76486ae908eab48e503d28ceb
Parents: aa2e968
Author: zzhang <zz...@apache.org>
Authored: Tue Dec 2 18:26:02 2014 -0800
Committer: zzhang <zz...@apache.org>
Committed: Tue Dec 2 18:26:02 2014 -0800

----------------------------------------------------------------------
 .../helix/webapp/resources/ClusterResource.java | 54 +++++++++----
 .../webapp/resources/ClustersResource.java      | 31 ++++++--
 .../helix/webapp/resources/ConfigResource.java  | 79 +++++++++++++-------
 .../webapp/resources/ConstraintResource.java    | 76 ++++++++++++-------
 .../webapp/resources/ControllerResource.java    | 42 ++++++++---
 .../webapp/resources/IdealStateResource.java    | 70 +++++++++++++++--
 .../webapp/resources/JobQueueResource.java      | 31 +++++++-
 .../webapp/resources/JobQueuesResource.java     | 20 +++++
 .../helix/webapp/resources/JobResource.java     | 10 +++
 .../helix/webapp/resources/ResourceUtil.java    |  5 +-
 10 files changed, 318 insertions(+), 100 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/helix/blob/9a5dbeaa/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClusterResource.java
----------------------------------------------------------------------
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClusterResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClusterResource.java
index b22d801..ea8b8db 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClusterResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClusterResource.java
@@ -20,9 +20,7 @@ package org.apache.helix.webapp.resources;
  */
 
 import java.io.IOException;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixException;
@@ -31,44 +29,56 @@ import org.apache.helix.ZNRecord;
 import org.apache.helix.manager.zk.ZkClient;
 import org.apache.helix.model.LiveInstance;
 import org.apache.helix.tools.ClusterSetup;
-import org.apache.helix.webapp.RestAdminApplication;
+import org.apache.log4j.Logger;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
 import org.restlet.data.MediaType;
-import org.restlet.data.Method;
 import org.restlet.data.Status;
 import org.restlet.representation.Representation;
 import org.restlet.representation.StringRepresentation;
 import org.restlet.representation.Variant;
 import org.restlet.resource.ServerResource;
 
+/**
+ * Class for server-side resource at <code> "/clusters/{clusterName}"
+ * <p>
+ * <li>GET list cluster information
+ * <li>POST activate/deactivate a cluster in distributed controller mode
+ * <li>DELETE remove a cluster
+ */
 public class ClusterResource extends ServerResource {
-    
+  private final static Logger LOG = Logger.getLogger(ClusterResource.class);
+
   public ClusterResource() {
     getVariants().add(new Variant(MediaType.TEXT_PLAIN));
     getVariants().add(new Variant(MediaType.APPLICATION_JSON));
     setNegotiated(false);
   }
 
+  /**
+   * List cluster information
+   * <p>
+   * Usage: <code> curl http://{host:port}/clusters/{clusterName}
+   */
   @Override
   public Representation get() {
     StringRepresentation presentation = null;
     try {
-      String clusterName = (String) getRequest().getAttributes().get("clusterName");
+      String clusterName =
+          ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CLUSTER_NAME);
       presentation = getClusterRepresentation(clusterName);
-    }
-
-    catch (Exception e) {
+    } catch (Exception e) {
       String error = ClusterRepresentationUtil.getErrorAsJsonStringFromException(e);
       presentation = new StringRepresentation(error, MediaType.APPLICATION_JSON);
-      e.printStackTrace();
+      LOG.error("Exception in get cluster", e);
     }
     return presentation;
   }
 
   StringRepresentation getClusterRepresentation(String clusterName) throws JsonGenerationException,
       JsonMappingException, IOException {
-    ZkClient zkClient = (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+    ZkClient zkClient =
+        ResourceUtil.getAttributeFromCtx(getContext(), ResourceUtil.ContextKey.ZKCLIENT);
     ClusterSetup setupTool = new ClusterSetup(zkClient);
     List<String> instances =
         setupTool.getClusterManagementTool().getInstancesInCluster(clusterName);
@@ -100,12 +110,20 @@ public class ClusterResource extends ServerResource {
     return representation;
   }
 
+  /**
+   * Activate/deactivate a cluster in distributed controller mode
+   * <p>
+   * Usage: <code> curl -d 'jsonParameters=
+   * {"command":"activateCluster","grandCluster":"{controllerCluster}","enabled":"{true/false}"}' -H
+   * "Content-Type: application/json" http://{host:port}/clusters/{clusterName}}
+   */
   @Override
   public Representation post(Representation entity) {
     try {
-      String clusterName = (String) getRequest().getAttributes().get("clusterName");
+      String clusterName =
+          ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CLUSTER_NAME);
       ZkClient zkClient =
-          (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+          ResourceUtil.getAttributeFromCtx(getContext(), ResourceUtil.ContextKey.ZKCLIENT);
       ClusterSetup setupTool = new ClusterSetup(zkClient);
 
       JsonParameters jsonParameters = new JsonParameters(entity);
@@ -143,12 +161,18 @@ public class ClusterResource extends ServerResource {
     return getResponseEntity();
   }
 
+  /**
+   * Remove a cluster
+   * <p>
+   * Usage: <code> curl -X DELETE http://{host:port}/clusters/{clusterName}
+   */
   @Override
   public Representation delete() {
     try {
-      String clusterName = (String) getRequest().getAttributes().get("clusterName");
+      String clusterName =
+          ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CLUSTER_NAME);
       ZkClient zkClient =
-          (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+          ResourceUtil.getAttributeFromCtx(getContext(), ResourceUtil.ContextKey.ZKCLIENT);
       ClusterSetup setupTool = new ClusterSetup(zkClient);
       setupTool.deleteCluster(clusterName);
       getResponse().setStatus(Status.SUCCESS_OK);

http://git-wip-us.apache.org/repos/asf/helix/blob/9a5dbeaa/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClustersResource.java
----------------------------------------------------------------------
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClustersResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClustersResource.java
index 4b12054..f823bf8 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClustersResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ClustersResource.java
@@ -26,7 +26,6 @@ import org.apache.helix.HelixException;
 import org.apache.helix.ZNRecord;
 import org.apache.helix.manager.zk.ZkClient;
 import org.apache.helix.tools.ClusterSetup;
-import org.apache.helix.webapp.RestAdminApplication;
 import org.apache.log4j.Logger;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
@@ -37,6 +36,12 @@ import org.restlet.representation.StringRepresentation;
 import org.restlet.representation.Variant;
 import org.restlet.resource.ServerResource;
 
+/**
+ * Class for server-side resource at <code> "/clusters"
+ * <p>
+ * <li>GET list all Helix clusters
+ * <li>POST add a new cluster
+ */
 public class ClustersResource extends ServerResource {
   private final static Logger LOG = Logger.getLogger(ClustersResource.class);
 
@@ -44,27 +49,31 @@ public class ClustersResource extends ServerResource {
     getVariants().add(new Variant(MediaType.TEXT_PLAIN));
     getVariants().add(new Variant(MediaType.APPLICATION_JSON));
     setNegotiated(false);
-    // handle(request,response);
   }
 
+  /**
+   * List all Helix clusters
+   * <p>
+   * Usage: <code> curl http://{host:port}/clusters
+   */
   @Override
   public Representation get() {
     StringRepresentation presentation = null;
     try {
       presentation = getClustersRepresentation();
     } catch (Exception e) {
-      LOG.error("", e);
+      LOG.error("Exception in get all clusters", e);
       String error = ClusterRepresentationUtil.getErrorAsJsonStringFromException(e);
       presentation = new StringRepresentation(error, MediaType.APPLICATION_JSON);
-
-      e.printStackTrace();
     }
     return presentation;
   }
 
   StringRepresentation getClustersRepresentation() throws JsonGenerationException,
       JsonMappingException, IOException {
-    ZkClient zkClient = (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+    ZkClient zkClient =
+        ResourceUtil.getAttributeFromCtx(getContext(), ResourceUtil.ContextKey.ZKCLIENT);
+
     ClusterSetup setupTool = new ClusterSetup(zkClient);
     List<String> clusters = setupTool.getClusterManagementTool().getClusters();
 
@@ -77,6 +86,12 @@ public class ClustersResource extends ServerResource {
     return representation;
   }
 
+  /**
+   * Add a new Helix cluster
+   * <p>
+   * Usage: <code> curl -d 'jsonParameters={"command":"addCluster","clusterName":"{clusterName}"}' -H
+   * "Content-Type: application/json" http://{host:port}/clusters
+   */
   @Override
   public Representation post(Representation entity) {
     try {
@@ -90,7 +105,7 @@ public class ClustersResource extends ServerResource {
         jsonParameters.verifyCommand(ClusterSetup.addCluster);
 
         ZkClient zkClient =
-            (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+            ResourceUtil.getAttributeFromCtx(getContext(), ResourceUtil.ContextKey.ZKCLIENT);
         ClusterSetup setupTool = new ClusterSetup(zkClient);
         setupTool.addCluster(jsonParameters.getParameter(JsonParameters.CLUSTER_NAME), false);
       } else {
@@ -111,6 +126,6 @@ public class ClustersResource extends ServerResource {
 
   @Override
   public Representation delete() {
-      return null;
+    return null;
   }
 }

http://git-wip-us.apache.org/repos/asf/helix/blob/9a5dbeaa/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConfigResource.java
----------------------------------------------------------------------
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConfigResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConfigResource.java
index 3c384d4..575df9f 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConfigResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConfigResource.java
@@ -33,9 +33,6 @@ import org.apache.helix.model.builder.HelixConfigScopeBuilder;
 import org.apache.helix.tools.ClusterSetup;
 import org.apache.helix.webapp.RestAdminApplication;
 import org.apache.log4j.Logger;
-import org.restlet.Context;
-import org.restlet.Request;
-import org.restlet.Response;
 import org.restlet.data.MediaType;
 import org.restlet.data.Status;
 import org.restlet.representation.Representation;
@@ -43,6 +40,12 @@ import org.restlet.representation.StringRepresentation;
 import org.restlet.representation.Variant;
 import org.restlet.resource.ServerResource;
 
+/**
+ * Class for server-side resource at <code> "/clusters/{clusterName}/configs"
+ * <p>
+ * <li>GET get scoped configs
+ * <li>POST set/remove scoped configs
+ */
 public class ConfigResource extends ServerResource {
   private final static Logger LOG = Logger.getLogger(ConfigResource.class);
 
@@ -76,15 +79,14 @@ public class ConfigResource extends ServerResource {
   StringRepresentation getConfigKeys(ConfigScopeProperty scopeProperty, String... keys)
       throws Exception {
     StringRepresentation representation = null;
-    // String clusterName = getValue("clusterName");
 
-    ZkClient zkClient = (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+    ZkClient zkClient =
+        ResourceUtil.getAttributeFromCtx(getContext(), ResourceUtil.ContextKey.ZKCLIENT);
     ClusterSetup setupTool = new ClusterSetup(zkClient);
     HelixAdmin admin = setupTool.getClusterManagementTool();
     ZNRecord record = new ZNRecord(scopeProperty + " Config");
 
     HelixConfigScope scope = new HelixConfigScopeBuilder(scopeProperty, keys).build();
-    // List<String> configKeys = admin.getConfigKeys(scopeProperty, clusterName, keys);
     List<String> configKeys = admin.getConfigKeys(scope);
     record.setListField(scopeProperty.toString(), configKeys);
 
@@ -95,12 +97,12 @@ public class ConfigResource extends ServerResource {
     return representation;
   }
 
-  StringRepresentation getConfigs(// ConfigScope scope,
-      ConfigScopeProperty scopeProperty, String... keys) throws Exception {
+  StringRepresentation getConfigs(ConfigScopeProperty scopeProperty, String... keys)
+      throws Exception {
     StringRepresentation representation = null;
-    // String clusterName = getValue("clusterName");
 
-    ZkClient zkClient = (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+    ZkClient zkClient =
+        ResourceUtil.getAttributeFromCtx(getContext(), ResourceUtil.ContextKey.ZKCLIENT);
     ClusterSetup setupTool = new ClusterSetup(zkClient);
     HelixAdmin admin = setupTool.getClusterManagementTool();
     ZNRecord record = new ZNRecord(scopeProperty + " Config");
@@ -117,6 +119,18 @@ public class ConfigResource extends ServerResource {
     return representation;
   }
 
+  /**
+   * Get scoped configs
+   * <p>
+   * Usage:
+   * <p>
+   * <li>Get cluster-level configs:
+   * <code>curl http://{host:port}/clusters/{clusterName}/configs/cluster
+   * <li>Get instance-level configs:
+   * <code>curl http://{host:port}/clusters/{clusterName}/configs/participant/{instanceName}
+   * <li>Get resource-level configs:
+   * <code>curl http://{host:port}/clusters/{clusterName}/configs/resource/{resourceName}
+   */
   @Override
   public Representation get() {
     StringRepresentation representation = null;
@@ -143,15 +157,6 @@ public class ConfigResource extends ServerResource {
         } else {
           // path is "/clusters/{clusterName}/configs/cluster|participant|resource/
           // {clusterName}|{participantName}|{resourceName}"
-          // ConfigScope scope;
-          // if (scopeProperty == ConfigScopeProperty.CLUSTER)
-          // {
-          // scope = new ConfigScopeBuilder().build(scopeProperty, clusterName);
-          // }
-          // else
-          // {
-          // scope = new ConfigScopeBuilder().build(scopeProperty, clusterName, scopeKey1);
-          // }
           representation = getConfigs(scopeProperty, clusterName, scopeKey1);
         }
         break;
@@ -167,11 +172,6 @@ public class ConfigResource extends ServerResource {
         } else {
           // path is
           // "/clusters/{clusterName}/configs/partition/resourceName/partitionName"
-          // ConfigScope scope =
-          // new ConfigScopeBuilder().build(scopeProperty,
-          // clusterName,
-          // scopeKey1,
-          // scopeKey2);
           representation = getConfigs(scopeProperty, clusterName, scopeKey1, scopeKey2);
         }
         break;
@@ -198,13 +198,13 @@ public class ConfigResource extends ServerResource {
     JsonParameters jsonParameters = new JsonParameters(entity);
     String command = jsonParameters.getCommand();
 
-    ZkClient zkClient = (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+    ZkClient zkClient =
+        ResourceUtil.getAttributeFromCtx(getContext(), ResourceUtil.ContextKey.ZKCLIENT);
     ClusterSetup setupTool = new ClusterSetup(zkClient);
     if (command.equalsIgnoreCase(ClusterSetup.setConfig)) {
       jsonParameters.verifyCommand(ClusterSetup.setConfig);
       String propertiesStr = jsonParameters.getParameter(JsonParameters.CONFIGS);
 
-      // setupTool.setConfig(scopeStr, propertiesStr);
       setupTool.setConfig(type, scopeArgs, propertiesStr);
     } else if (command.equalsIgnoreCase(ClusterSetup.removeConfig)) {
       jsonParameters.verifyCommand(ClusterSetup.removeConfig);
@@ -221,8 +221,31 @@ public class ConfigResource extends ServerResource {
     getResponse().setStatus(Status.SUCCESS_OK);
   }
 
-
-@Override
+  /**
+   * Set/remove scoped configs
+   * <p>
+   * Usage:
+   * <p>
+   * <li>Set cluster level configs:
+   * <code>curl -d 'jsonParameters={"command":"setConfig","configs":"{key1=value1,key2=value2}"}'
+   * -H "Content-Type: application/json" http://{host:port}/clusters/{clusterName}/configs/cluster
+   * <li>Remove cluster level configs:
+   * <code>curl -d 'jsonParameters={"command":"removeConfig","configs":"{key1,key2}"}'
+   * -H "Content-Type: application/json" http://{host:port}/clusters/{clusterName}/configs/cluster
+   * <li>Set instance level configs:
+   * <code>curl -d 'jsonParameters={"command":"setConfig","configs":"{key1=value1,key2=value2}"}'
+   * -H "Content-Type: application/json" http://{host:port}/clusters/{clusterName}/configs/participant/{instanceName}
+   * <li>Remove instance level configs:
+   * <code>curl -d 'jsonParameters={"command":"removeConfig","configs":"{key1,key2}"}'
+   * -H "Content-Type: application/json" http://{host:port}/clusters/{clusterName}/configs/participant/{instanceName}
+   * <li>Set resource level configs:
+   * <code>curl -d 'jsonParameters={"command":"setConfig","configs":"{key1=value1,key2=value2}"}'
+   * -H "Content-Type: application/json" http://{host:port}/clusters/{clusterName}/configs/resource/{resourceName}
+   * <li>Remove resource level configs:
+   * <code>curl -d 'jsonParameters={"command":"removeConfig","configs":"{key1,key2}"}'
+   * -H "Content-Type: application/json" http://{host:port}/clusters/{clusterName}/configs/resource/{resourceName}
+   */
+  @Override
   public Representation post(Representation entity) {
     String clusterName = getValue("clusterName");
 

http://git-wip-us.apache.org/repos/asf/helix/blob/9a5dbeaa/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConstraintResource.java
----------------------------------------------------------------------
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConstraintResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConstraintResource.java
index 675d0ec..090a518 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConstraintResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ConstraintResource.java
@@ -27,11 +27,7 @@ import org.apache.helix.manager.zk.ZKHelixAdmin;
 import org.apache.helix.manager.zk.ZkClient;
 import org.apache.helix.model.ClusterConstraints.ConstraintType;
 import org.apache.helix.tools.ClusterSetup;
-import org.apache.helix.webapp.RestAdminApplication;
 import org.apache.log4j.Logger;
-import org.restlet.Context;
-import org.restlet.Request;
-import org.restlet.Response;
 import org.restlet.data.MediaType;
 import org.restlet.data.Status;
 import org.restlet.representation.Representation;
@@ -39,8 +35,14 @@ import org.restlet.representation.StringRepresentation;
 import org.restlet.representation.Variant;
 import org.restlet.resource.ServerResource;
 
+/**
+ * Class for server-side resource at <code>"/clusters/{clusterName}/constraints/{constraintType}"
+ * <p>
+ * <li>GET list all constraints
+ * <li>POST set constraints
+ * <li>DELETE remove constraints
+ */
 public class ConstraintResource extends ServerResource {
-
   private final static Logger LOG = Logger.getLogger(ConstraintResource.class);
 
   public ConstraintResource() {
@@ -49,24 +51,26 @@ public class ConstraintResource extends ServerResource {
     setNegotiated(false);
   }
 
-  // TODO move to a util function
-  String getValue(String key) {
-    return (String) getRequest().getAttributes().get(key);
-  }
-
+  /**
+   * List all constraints
+   * <p>
+   * Usage: <code>curl http://{host:port}/clusters/{clusterName}/constraints/MESSAGE_CONSTRAINT
+   */
   @Override
   public Representation get() {
     StringRepresentation representation = null;
-    String clusterName = getValue("clusterName");
-    String constraintTypeStr = getValue("constraintType").toUpperCase();
-    String constraintId = getValue("constraintId");
+    String clusterName =
+        ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CLUSTER_NAME);
+    String constraintTypeStr =
+        ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CONSTRAINT_TYPE);
+    String constraintId =
+        ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CONSTRAINT_ID);
 
     try {
       ConstraintType constraintType = ConstraintType.valueOf(constraintTypeStr);
       ZkClient zkClient =
-          (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
-      // ClusterSetup setupTool = new ClusterSetup(zkClient);
-      HelixAdmin admin = new ZKHelixAdmin(zkClient); // setupTool.getClusterManagementTool();
+          ResourceUtil.getAttributeFromCtx(getContext(), ResourceUtil.ContextKey.ZKCLIENT);
+      HelixAdmin admin = new ZKHelixAdmin(zkClient);
 
       ZNRecord record = admin.getConstraints(clusterName, constraintType).getRecord();
       if (constraintId == null) {
@@ -96,21 +100,31 @@ public class ConstraintResource extends ServerResource {
     } catch (Exception e) {
       String error = ClusterRepresentationUtil.getErrorAsJsonStringFromException(e);
       representation = new StringRepresentation(error, MediaType.APPLICATION_JSON);
-      LOG.error("", e);
+      LOG.error("Exception get constraints", e);
     }
 
     return representation;
   }
 
+  /**
+   * Set constraints
+   * <p>
+   * Usage:
+   * <code>curl -d 'jsonParameters={"constraintAttributes":"RESOURCE={resource},CONSTRAINT_VALUE={1}"}'
+   * -H "Content-Type: application/json" http://{host:port}/clusters/{cluster}/constraints/MESSAGE_CONSTRAINT/{constraintId}
+   */
   @Override
   public Representation post(Representation entity) {
-    String clusterName = getValue("clusterName");
-    String constraintTypeStr = getValue("constraintType").toUpperCase();
-    String constraintId = getValue("constraintId");
+    String clusterName =
+        ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CLUSTER_NAME);
+    String constraintTypeStr =
+        ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CONSTRAINT_TYPE);
+    String constraintId =
+        ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CONSTRAINT_ID);
 
     try {
       ZkClient zkClient =
-          (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+          ResourceUtil.getAttributeFromCtx(getContext(), ResourceUtil.ContextKey.ZKCLIENT);
       ClusterSetup setupTool = new ClusterSetup(zkClient);
       JsonParameters jsonParameters = new JsonParameters(entity);
 
@@ -126,21 +140,29 @@ public class ConstraintResource extends ServerResource {
     return null;
   }
 
+  /**
+   * Remove constraints
+   * <p>
+   * Usage:
+   * <code>curl -X DELETE http://{host:port}/clusters/{cluster}/constraints/MESSAGE_CONSTRAINT/{constraintId}
+   */
   @Override
   public Representation delete() {
-    String clusterName = getValue("clusterName");
-    String constraintTypeStr = getValue("constraintType").toUpperCase();
-    String constraintId = getValue("constraintId");
+    String clusterName =
+        ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CLUSTER_NAME);
+    String constraintTypeStr =
+        ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CONSTRAINT_TYPE);
+    String constraintId =
+        ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CONSTRAINT_ID);
 
     try {
       ZkClient zkClient =
-          (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+          ResourceUtil.getAttributeFromCtx(getContext(), ResourceUtil.ContextKey.ZKCLIENT);
       ClusterSetup setupTool = new ClusterSetup(zkClient);
 
       setupTool.removeConstraint(clusterName, constraintTypeStr, constraintId);
-
     } catch (Exception e) {
-      LOG.error("Error in deleting ", e);
+      LOG.error("Error in delete constraint", e);
       getResponse().setEntity(ClusterRepresentationUtil.getErrorAsJsonStringFromException(e),
           MediaType.APPLICATION_JSON);
       getResponse().setStatus(Status.SUCCESS_OK);

http://git-wip-us.apache.org/repos/asf/helix/blob/9a5dbeaa/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ControllerResource.java
----------------------------------------------------------------------
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ControllerResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ControllerResource.java
index ea7be42..6288ab7 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ControllerResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ControllerResource.java
@@ -37,12 +37,9 @@ import org.apache.helix.manager.zk.ZkClient;
 import org.apache.helix.model.LiveInstance;
 import org.apache.helix.tools.ClusterSetup;
 import org.apache.helix.util.StatusUpdateUtil.Level;
-import org.apache.helix.webapp.RestAdminApplication;
+import org.apache.log4j.Logger;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
-import org.restlet.Context;
-import org.restlet.Request;
-import org.restlet.Response;
 import org.restlet.data.MediaType;
 import org.restlet.data.Status;
 import org.restlet.representation.Representation;
@@ -50,10 +47,16 @@ import org.restlet.representation.StringRepresentation;
 import org.restlet.representation.Variant;
 import org.restlet.resource.ServerResource;
 
+/**
+ * Class for server-side resource at <code>"/clusters/{clusterName}/Controller"
+ * <p>
+ * <li>GET list Helix controller info
+ * <li>POST enable/disable Helix controller
+ */
 public class ControllerResource extends ServerResource {
+  private final static Logger LOG = Logger.getLogger(ControllerResource.class);
 
-  public ControllerResource()
-  {
+  public ControllerResource() {
     getVariants().add(new Variant(MediaType.TEXT_PLAIN));
     getVariants().add(new Variant(MediaType.APPLICATION_JSON));
     setNegotiated(false);
@@ -62,7 +65,8 @@ public class ControllerResource extends ServerResource {
   StringRepresentation getControllerRepresentation(String clusterName)
       throws JsonGenerationException, JsonMappingException, IOException {
     Builder keyBuilder = new PropertyKey.Builder(clusterName);
-    ZkClient zkClient = (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+    ZkClient zkClient =
+        ResourceUtil.getAttributeFromCtx(getContext(), ResourceUtil.ContextKey.ZKCLIENT);
 
     ZKHelixDataAccessor accessor =
         new ZKHelixDataAccessor(clusterName, new ZkBaseDataAccessor<ZNRecord>(zkClient));
@@ -90,26 +94,40 @@ public class ControllerResource extends ServerResource {
     return representation;
   }
 
+  /**
+   * List Helix controller info
+   * <p>
+   * Usage: <code>curl http://{host:port}/clusters/{cluster}/Controller
+   */
   @Override
   public Representation get() {
     StringRepresentation presentation = null;
     try {
-      String clusterName = (String) getRequest().getAttributes().get("clusterName");
+      String clusterName =
+          ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CLUSTER_NAME);
       presentation = getControllerRepresentation(clusterName);
     } catch (Exception e) {
+      LOG.error("Exception get controller info", e);
       String error = ClusterRepresentationUtil.getErrorAsJsonStringFromException(e);
       presentation = new StringRepresentation(error, MediaType.APPLICATION_JSON);
-      e.printStackTrace();
     }
     return presentation;
   }
 
+  /**
+   * Enable/disable Helix controller
+   * <p>
+   * Usage:
+   * <code>curl -d 'jsonParameters={"command":"enableCluster","enabled":"{true/false}"}'
+   * -H "Content-Type: application/json" http://{host:port}/clusters/{cluster}/Controller
+   */
   @Override
   public Representation post(Representation entity) {
     try {
-      String clusterName = (String) getRequest().getAttributes().get("clusterName");
+      String clusterName =
+          ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CLUSTER_NAME);
       ZkClient zkClient =
-          (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+          ResourceUtil.getAttributeFromCtx(getContext(), ResourceUtil.ContextKey.ZKCLIENT);
       ClusterSetup setupTool = new ClusterSetup(zkClient);
 
       JsonParameters jsonParameters = new JsonParameters(entity);
@@ -135,7 +153,7 @@ public class ControllerResource extends ServerResource {
           MediaType.APPLICATION_JSON);
       getResponse().setStatus(Status.SUCCESS_OK);
     }
-    
+
     return null;
   }
 }

http://git-wip-us.apache.org/repos/asf/helix/blob/9a5dbeaa/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/IdealStateResource.java
----------------------------------------------------------------------
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/IdealStateResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/IdealStateResource.java
index 49df073..0081922 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/IdealStateResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/IdealStateResource.java
@@ -30,7 +30,6 @@ import org.apache.helix.ZNRecord;
 import org.apache.helix.manager.zk.ZkClient;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.tools.ClusterSetup;
-import org.apache.helix.webapp.RestAdminApplication;
 import org.apache.log4j.Logger;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
@@ -41,6 +40,13 @@ import org.restlet.representation.StringRepresentation;
 import org.restlet.representation.Variant;
 import org.restlet.resource.ServerResource;
 
+/**
+ * Class for server-side resource at
+ * <code>"/clusters/{clusterName}/resourceGroups/{resourceName}/idealState"
+ * <p>
+ * <li>GET get ideal state
+ * <li>POST set ideal state
+ */
 public class IdealStateResource extends ServerResource {
   private final static Logger LOG = Logger.getLogger(IdealStateResource.class);
 
@@ -50,12 +56,20 @@ public class IdealStateResource extends ServerResource {
     setNegotiated(false);
   }
 
+  /**
+   * Get ideal state
+   * <p>
+   * Usage:
+   * <code>curl http://{host:port}/clusters/{clusterName}/resourceGroups/{resourceName}/idealState
+   */
   @Override
   public Representation get() {
     StringRepresentation presentation = null;
     try {
-      String clusterName = (String) getRequest().getAttributes().get("clusterName");
-      String resourceName = (String) getRequest().getAttributes().get("resourceName");
+      String clusterName =
+          ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CLUSTER_NAME);
+      String resourceName =
+          ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.RESOURCE_NAME);
       presentation = getIdealStateRepresentation(clusterName, resourceName);
     }
 
@@ -71,7 +85,8 @@ public class IdealStateResource extends ServerResource {
   StringRepresentation getIdealStateRepresentation(String clusterName, String resourceName)
       throws JsonGenerationException, JsonMappingException, IOException {
     Builder keyBuilder = new PropertyKey.Builder(clusterName);
-    ZkClient zkClient = (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+    ZkClient zkClient =
+        ResourceUtil.getAttributeFromCtx(getContext(), ResourceUtil.ContextKey.ZKCLIENT);
 
     String message =
         ClusterRepresentationUtil.getClusterPropertyAsString(zkClient, clusterName,
@@ -83,13 +98,54 @@ public class IdealStateResource extends ServerResource {
     return representation;
   }
 
+  /**
+   * Set ideal state
+   * <p>
+   * Usage:
+   * <p>
+   * <li>Add ideal state:
+   * <code>curl -d @'{newIdealState.json}' -H 'Content-Type: application/json'
+   * http://{host:port}/clusters/{cluster}/resourceGroups/{resource}/idealState
+   * <pre>
+   * newIdealState:
+   * jsonParameters={"command":"addIdealState"}&newIdealState={
+   *  "id" : "{MyDB}",
+   *  "simpleFields" : {
+   *    "IDEAL_STATE_MODE" : "AUTO",
+   *    "NUM_PARTITIONS" : "{8}",
+   *    "REBALANCE_MODE" : "SEMI_AUTO",
+   *    "REPLICAS" : "0",
+   *    "STATE_MODEL_DEF_REF" : "MasterSlave",
+   *    "STATE_MODEL_FACTORY_NAME" : "DEFAULT"
+   *  },
+   *  "listFields" : {
+   *  },
+   *  "mapFields" : {
+   *    "{MyDB_0}" : {
+   *      "{localhost_1001}" : "MASTER",
+   *      "{localhost_1002}" : "SLAVE"
+   *    }
+   *  }
+   * }
+   * </pre>
+   * <li>Rebalance cluster:
+   * <code>curl -d 'jsonParameters={"command":"rebalance","replicas":"{3}"}'
+   * -H "Content-Type: application/json" http://{host:port}/clusters/{cluster}/resourceGroups/{resource}/idealState
+   * <li>Expand resource: <code>n/a
+   * <li>Add resource property:
+   * <code>curl -d 'jsonParameters={"command":"addResourceProperty","{REBALANCE_TIMER_PERIOD}":"{500}"}'
+   * -H "Content-Type: application/json" http://{host:port}/clusters/{cluster}/resourceGroups/{resource}/idealState
+   */
   @Override
   public Representation post(Representation entity) {
     try {
-      String clusterName = (String) getRequest().getAttributes().get("clusterName");
-      String resourceName = (String) getRequest().getAttributes().get("resourceName");
+      String clusterName =
+          ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.CLUSTER_NAME);
+      String resourceName =
+          ResourceUtil.getAttributeFromRequest(getRequest(), ResourceUtil.RequestKey.RESOURCE_NAME);
+
       ZkClient zkClient =
-          (ZkClient) getContext().getAttributes().get(RestAdminApplication.ZKCLIENT);
+          ResourceUtil.getAttributeFromCtx(getContext(), ResourceUtil.ContextKey.ZKCLIENT);
       ClusterSetup setupTool = new ClusterSetup(zkClient);
 
       JsonParameters jsonParameters = new JsonParameters(entity);

http://git-wip-us.apache.org/repos/asf/helix/blob/9a5dbeaa/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueueResource.java
----------------------------------------------------------------------
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueueResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueueResource.java
index 3ff9a37..830e16b 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueueResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueueResource.java
@@ -46,6 +46,12 @@ import org.restlet.resource.ServerResource;
 
 import java.util.Map;
 
+/**
+ * Class for server-side resource at <code>"/clusters/{clusterName}/jobQueues/{jobQueue}"
+ * <p>
+ * <li>GET list job queue info
+ * <li>POST start a new job in a job queue, or stop/resume/flush/delete a job queue
+ */
 public class JobQueueResource extends ServerResource {
   private final static Logger LOG = Logger.getLogger(JobQueueResource.class);
 
@@ -55,6 +61,11 @@ public class JobQueueResource extends ServerResource {
     setNegotiated(false);
   }
 
+  /**
+   * List job queue info
+   * <p>
+   * Usage: <code>curl http://{host:port}/clusters/{clusterName}/jobQueues/{jobQueue}
+   */
   @Override
   public Representation get() {
     StringRepresentation presentation;
@@ -105,6 +116,22 @@ public class JobQueueResource extends ServerResource {
     return representation;
   }
 
+  /**
+   * Start a new job in a job queue, or stop/resume/flush/delete a job queue
+   * <p>
+   * Usage:
+   * <p>
+   * <li>Start a new job in a job queue:
+   * <code>curl -d @'./{input.txt}' -H 'Content-Type: application/json'
+   * http://{host:port}/clusters/{clusterName}/jobQueues/{jobQueue}
+   * <p>
+   * input.txt: <code>jsonParameters={"command":"start"}&newJob={newJobConfig.yaml}
+   * <p>
+   * For newJobConfig.yaml, see {@link Workflow#parse(String)}
+   * <li>Stop/resume/flush/delete a job queue:
+   * <code>curl -d 'jsonParameters={"command":"{stop/resume/flush/delete}"}'
+   * -H "Content-Type: application/json" http://{host:port}/clusters/{clusterName}/jobQueues/{jobQueue}
+   */
   @Override
   public Representation post(Representation entity) {
     String clusterName =
@@ -123,7 +150,8 @@ public class JobQueueResource extends ServerResource {
       switch (cmd) {
       case start: {
         // Get the job queue and submit it
-        String yamlPayload = ResourceUtil.getYamlParameters(form, ResourceUtil.YamlParamKey.NEW_JOB);
+        String yamlPayload =
+            ResourceUtil.getYamlParameters(form, ResourceUtil.YamlParamKey.NEW_JOB);
         if (yamlPayload == null) {
           throw new HelixException("Yaml job config is required!");
         }
@@ -161,7 +189,6 @@ public class JobQueueResource extends ServerResource {
       }
       getResponse().setEntity(getHostedEntitiesRepresentation(clusterName, jobQueueName));
       getResponse().setStatus(Status.SUCCESS_OK);
-
     } catch (Exception e) {
       getResponse().setEntity(ClusterRepresentationUtil.getErrorAsJsonStringFromException(e),
           MediaType.APPLICATION_JSON);

http://git-wip-us.apache.org/repos/asf/helix/blob/9a5dbeaa/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueuesResource.java
----------------------------------------------------------------------
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueuesResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueuesResource.java
index 24a4387..1a5cb17 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueuesResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobQueuesResource.java
@@ -47,6 +47,12 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
+/**
+ * Class for server-side resource at <code>"/clusters/{clusterName}/jobQueues"
+ * <p>
+ * <li>GET list all job queues
+ * <li>POST add a new job queue
+ */
 public class JobQueuesResource extends ServerResource {
   private final static Logger LOG = Logger.getLogger(JobQueuesResource.class);
 
@@ -56,6 +62,11 @@ public class JobQueuesResource extends ServerResource {
     setNegotiated(false);
   }
 
+  /**
+   * List all job queues
+   * <p>
+   * Usage: <code>curl http://{host:port}/clusters/{clusterName}/jobQueues
+   */
   @Override
   public Representation get() {
     StringRepresentation presentation = null;
@@ -110,6 +121,15 @@ public class JobQueuesResource extends ServerResource {
     return representation;
   }
 
+  /**
+   * Add a new job queue
+   * <p>
+   * Usage:
+   * <code>curl -d @'{jobQueueConfig.yaml}'
+   * -H 'Content-Type: application/json' http://{host:port}/clusters/{clusterName}/jobQueues
+   * <p>
+   * For jobQueueConfig.yaml, see {@link Workflow#parse(String)}
+   */
   @Override
   public Representation post(Representation entity) {
     try {

http://git-wip-us.apache.org/repos/asf/helix/blob/9a5dbeaa/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobResource.java
----------------------------------------------------------------------
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobResource.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobResource.java
index a58e223..0193c4c 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobResource.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/JobResource.java
@@ -38,6 +38,11 @@ import org.restlet.representation.StringRepresentation;
 import org.restlet.representation.Variant;
 import org.restlet.resource.ServerResource;
 
+/**
+ * Class for server-side resource at <code>"/clusters/{clusterName}/jobQueues/{jobQueue}/{job}"
+ * <p>
+ * <li>GET list job info
+ */
 public class JobResource extends ServerResource {
   private final static Logger LOG = Logger.getLogger(JobResource.class);
 
@@ -47,6 +52,11 @@ public class JobResource extends ServerResource {
     setNegotiated(false);
   }
 
+  /**
+   * List job info
+   * <p>
+   * Usage: <code>curl http://{host:port}/clusters/{clusterName}/jobQueues/{jobQueue}/{job}
+   */
   @Override
   public Representation get() {
     StringRepresentation presentation;

http://git-wip-us.apache.org/repos/asf/helix/blob/9a5dbeaa/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceUtil.java
----------------------------------------------------------------------
diff --git a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceUtil.java b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceUtil.java
index 969bdf5..f066dfc 100644
--- a/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceUtil.java
+++ b/helix-admin-webapp/src/main/java/org/apache/helix/webapp/resources/ResourceUtil.java
@@ -33,7 +33,10 @@ public class ResourceUtil {
   public enum RequestKey {
     CLUSTER_NAME("clusterName"),
     JOB_QUEUE("jobQueue"),
-    JOB("job");
+    JOB("job"),
+    CONSTRAINT_TYPE("constraintType"),
+    CONSTRAINT_ID("constraintId"),
+    RESOURCE_NAME("resourceName");
 
     private final String _key;