You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ma...@apache.org on 2012/10/24 08:05:33 UTC

svn commit: r1401562 [1/2] - in /incubator/ambari/branches/AMBARI-666: ./ ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ ambari-server/src/main/java/org/apache/ambari/server/api/query/ ambari-server/src/main/java/org/apache/ambari/s...

Author: mahadev
Date: Wed Oct 24 06:05:32 2012
New Revision: 1401562

URL: http://svn.apache.org/viewvc?rev=1401562&view=rev
Log:
AMBARI-893. provide api support for temporal queries. (John Speidel via mahadev)

Added:
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/TemporalInfoImpl.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/TemporalInfo.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/services/serializers/
    incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/services/serializers/JsonSerializerTest.java
Modified:
    incubator/ambari/branches/AMBARI-666/AMBARI-666-CHANGES.txt
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/query/Query.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryImpl.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostComponentResourceDefinition.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/Request.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestImpl.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNode.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNodeImpl.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/ganglia/GangliaPropertyProvider.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestImpl.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ResourceImpl.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Request.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/PropertyHelper.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/handlers/ReadHandlerTest.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/query/QueryImplTest.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/services/RequestImplTest.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/controller/jmx/TestStreamProvider.java
    incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/controller/predicate/ResourceImpl.java

Modified: incubator/ambari/branches/AMBARI-666/AMBARI-666-CHANGES.txt
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/AMBARI-666-CHANGES.txt?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/AMBARI-666-CHANGES.txt (original)
+++ incubator/ambari/branches/AMBARI-666/AMBARI-666-CHANGES.txt Wed Oct 24 06:05:32 2012
@@ -12,6 +12,9 @@ AMBARI-666 branch (unreleased changes)
 
   NEW FEATURES
 
+  AMBARI-893. provide api support for temporal queries. (John Speidel via 
+  mahadev)
+
   AMBARI-897. Operations request object and skeleton management methods.
   (jitendra)
 

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java Wed Oct 24 06:05:32 2012
@@ -22,6 +22,10 @@ import org.apache.ambari.server.api.serv
 import org.apache.ambari.server.api.services.Result;
 import org.apache.ambari.server.api.query.Query;
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.spi.PropertyId;
+import org.apache.ambari.server.controller.spi.TemporalInfo;
+
+import java.util.Map;
 
 /**
  * Responsible for read requests.
@@ -33,13 +37,10 @@ public class ReadHandler implements Requ
     Query query = request.getResourceDefinition().getQuery();
 
     //Partial response
-    for (String s : request.getPartialResponseFields()) {
-      int i = s.lastIndexOf('/');
-      if (i == -1) {
-        query.addProperty(null, s);
-      } else {
-        query.addProperty(s.substring(0, i), s.substring(i + 1));
-      }
+    for (Map.Entry<PropertyId, TemporalInfo> entry : request.getFields().entrySet()) {
+      // Iterate over map and add props/temporalInfo
+      PropertyId propertyId = entry.getKey();
+      query.addProperty(propertyId.getCategory(), propertyId.getName(), entry.getValue());
     }
 
    query.setUserPredicate(request.getQueryPredicate());

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/query/Query.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/query/Query.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/query/Query.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/query/Query.java Wed Oct 24 06:05:32 2012
@@ -22,6 +22,7 @@ import org.apache.ambari.server.api.serv
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.controller.spi.Predicate;
 import org.apache.ambari.server.controller.spi.PropertyId;
+import org.apache.ambari.server.controller.spi.TemporalInfo;
 
 import java.util.Map;
 import java.util.Set;
@@ -38,8 +39,9 @@ public interface Query {
    *
    * @param group    the group name that contains the property
    * @param property the property name
+   * @param temporalInfo
    */
-  public void addProperty(String group, String property);
+  public void addProperty(String group, String property, TemporalInfo temporalInfo);
 
   /**
    * Add a property to the query.

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryImpl.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryImpl.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryImpl.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryImpl.java Wed Oct 24 06:05:32 2012
@@ -20,6 +20,7 @@ package org.apache.ambari.server.api.que
 
 import org.apache.ambari.server.api.resources.ResourceDefinition;
 import org.apache.ambari.server.api.services.ResultImpl;
+import org.apache.ambari.server.api.util.TreeNodeImpl;
 import org.apache.ambari.server.controller.internal.PropertyIdImpl;
 import org.apache.ambari.server.controller.utilities.ClusterControllerHelper;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
@@ -48,11 +49,23 @@ public class QueryImpl implements Query 
   private Map<String, Set<String>> m_mapQueryProperties = new HashMap<String, Set<String>>();
 
   /**
+   * Map that associates each property set on the query to temporal data.
+   */
+  private Map<PropertyId, TemporalInfo> m_mapPropertyTemporalInfo = new HashMap<PropertyId, TemporalInfo>();
+
+  private Map<String, TemporalInfo> m_mapCategoryTemporalInfo = new HashMap<String, TemporalInfo>();
+
+  /**
    * All properties that are available for the resource.
    */
   private Map<String, Set<String>> m_mapAllProperties;
 
   /**
+   * Tree index of m_mapAllProperties.  Used to match sub-categories.
+   */
+  TreeNode<Set<String>> m_treeAllProperties = new TreeNodeImpl<Set<String>>(null, new HashSet<String>(), null);
+
+  /**
    * Sub-resources of the resource which is being operated on.
    */
   private Map<String, ResourceDefinition> m_mapSubResources = new HashMap<String, ResourceDefinition>();
@@ -72,45 +85,40 @@ public class QueryImpl implements Query 
     m_resourceDefinition = resourceDefinition;
     m_mapAllProperties = Collections.unmodifiableMap(getClusterController().
         getSchema(resourceDefinition.getType()).getCategories());
+    buildAllPropertiesTree();
   }
 
   @Override
-  public void addProperty(String path, String property) {
-    if (path == null && property.equals("*")) {
+  //todo: consider requiring a path and a property.  For categories the property name '*' could be used.
+  public void addProperty(String category, String property, TemporalInfo temporalInfo) {
+    if (category == null && property.equals("*")) {
       // wildcard
-      addAllProperties();
-    } else if (m_mapAllProperties.containsKey(path) && m_mapAllProperties.get(path).contains(property)) {
+      addAllProperties(temporalInfo);
+    } else if (m_mapAllProperties.containsKey(category) && m_mapAllProperties.get(category).contains(property)) {
       // local property
-      Set<String> setProps = m_mapQueryProperties.get(path);
+      Set<String> setProps = m_mapQueryProperties.get(category);
       if (setProps == null) {
         setProps = new HashSet<String>();
-        m_mapQueryProperties.put(path, setProps);
+        m_mapQueryProperties.put(category, setProps);
       }
       setProps.add(property);
-    } else if (m_mapAllProperties.containsKey(property)) {
-      // no path specified because path is provided as property
-      //local category
-      Set<String> setProps = m_mapQueryProperties.get(property);
-      if (setProps == null) {
-        setProps = new HashSet<String>();
-        m_mapQueryProperties.put(property, setProps);
+      if (temporalInfo != null) {
+        m_mapPropertyTemporalInfo.put(PropertyHelper.getPropertyId(property, category, true), temporalInfo);
       }
-      // add all props for category
-      setProps.addAll(m_mapAllProperties.get(property));
-    } else {
+    } else if (! addCategory(category, property, temporalInfo)){
       // not a local category/property
-      boolean success = addPropertyToSubResource(path, property);
+      boolean success = addPropertyToSubResource(category, property, temporalInfo);
       if (!success) {
         //TODO
         throw new RuntimeException("Attempted to add invalid property to resource.  Resource=" +
-            m_resourceDefinition.getType() + ", Property: Category=" + path + " Field=" + property);
+            m_resourceDefinition.getType() + ", Property: Category=" + category + " Field=" + property);
       }
     }
   }
 
   @Override
   public void addProperty(PropertyId property) {
-    addProperty(property.getCategory(), property.getName());
+    addProperty(property.getCategory(), property.getName(), null);
   }
 
   @Override
@@ -134,8 +142,10 @@ public class QueryImpl implements Query 
         m_resourceDefinition.getType(), createRequest(), predicate);
 
     TreeNode<Resource> tree = result.getResultTree();
+    int count = 1;
     for (Resource resource : iterResource) {
-      TreeNode<Resource> node = tree.addChild(resource, null);
+      // add a child node for the resource and provide a unique name.  The name is never used.
+      TreeNode<Resource> node = tree.addChild(resource, resource.getType() + ":" + count++);
 
       for (Map.Entry<String, ResourceDefinition> entry : m_mapSubResources.entrySet()) {
         String subResCategory = entry.getKey();
@@ -151,7 +161,6 @@ public class QueryImpl implements Query 
         node.addChild(childResult);
       }
     }
-
     return result;
   }
 
@@ -170,8 +179,18 @@ public class QueryImpl implements Query 
     m_userPredicate = predicate;
   }
 
-  private void addAllProperties() {
-    m_mapQueryProperties.putAll(m_mapAllProperties);
+  private void addAllProperties(TemporalInfo temporalInfo) {
+    if (temporalInfo == null) {
+      m_mapQueryProperties.putAll(m_mapAllProperties);
+    } else {
+      for (Map.Entry<String, Set<String>> entry : m_mapAllProperties.entrySet()) {
+        String path = entry.getKey();
+        Set<String> setProps = entry.getValue();
+        m_mapQueryProperties.put(path, setProps);
+        m_mapCategoryTemporalInfo.put(path, temporalInfo);
+      }
+    }
+
     for (Map.Entry<String, ResourceDefinition> entry : m_resourceDefinition.getSubResources().entrySet()) {
       String name = entry.getKey();
       if (! m_mapSubResources.containsKey(name)) {
@@ -180,7 +199,34 @@ public class QueryImpl implements Query 
     }
   }
 
-  private boolean addPropertyToSubResource(String path, String property) {
+  private boolean addCategory(String category, String name, TemporalInfo temporalInfo) {
+    name = category != null ? category + '/' + name : name;
+    TreeNode<Set<String>> node = m_treeAllProperties.getChild(name);
+    if (node == null) {
+      return false;
+    }
+
+    addCategory(node, name, temporalInfo);
+    return true;
+  }
+
+  private void addCategory(TreeNode<Set<String>> node, String category, TemporalInfo temporalInfo) {
+    if (node != null) {
+      Set<String> setProps = m_mapQueryProperties.get(category);
+      if (setProps == null) {
+        setProps = new HashSet<String>();
+        m_mapQueryProperties.put(category, setProps);
+      }
+      setProps.addAll(node.getObject());
+      m_mapCategoryTemporalInfo.put(category, temporalInfo);
+
+      for (TreeNode<Set<String>> child : node.getChildren()) {
+        addCategory(child, category + '/' + child.getName(), temporalInfo);
+      }
+    }
+  }
+
+  private boolean addPropertyToSubResource(String path, String property, TemporalInfo temporalInfo) {
     // cases:
     // - path is null, property is path (all sub-resource props will have a path)
     // - path is single token and prop in non null
@@ -203,7 +249,7 @@ public class QueryImpl implements Query 
 
       if (property != null || !path.equals(p)) {
         //only add if a sub property is set or if a sub category is specified
-        subResource.getQuery().addProperty(i == -1 ? null : path.substring(i + 1), property);
+        subResource.getQuery().addProperty(i == -1 ? null : path.substring(i + 1), property, temporalInfo);
       }
       resourceAdded = true;
     }
@@ -244,12 +290,35 @@ public class QueryImpl implements Query 
         predicate = m_userPredicate;
       }
     } else {
-      predicate = (m_userPredicate == null) ? internalPredicate :
-          new AndPredicate((BasePredicate) m_userPredicate, internalPredicate);
+      predicate = (m_userPredicate == null ? internalPredicate :
+          new AndPredicate((BasePredicate) m_userPredicate, internalPredicate));
     }
     return predicate;
   }
 
+  private void buildAllPropertiesTree() {
+    // build index
+    for (String category : m_mapAllProperties.keySet()) {
+      TreeNode<Set<String>> node = m_treeAllProperties.getChild(category);
+      if (node == null) {
+        if (category == null) {
+          node = m_treeAllProperties.addChild(new HashSet<String>(), null);
+        } else {
+          String[] tokens = category.split("/");
+          node = m_treeAllProperties;
+          for (String t : tokens) {
+            TreeNode<Set<String>> child = node.getChild(t);
+            if (child == null) {
+              child = node.addChild(new HashSet<String>(), t);
+            }
+            node = child;
+          }
+        }
+      }
+      node.getObject().addAll(m_mapAllProperties.get(category));
+    }
+  }
+
   ClusterController getClusterController() {
     return ClusterControllerHelper.getClusterController();
   }
@@ -260,14 +329,21 @@ public class QueryImpl implements Query 
     for (Map.Entry<String, Set<String>> entry : m_mapQueryProperties.entrySet()) {
       String group = entry.getKey();
       for (String property : entry.getValue()) {
-        setProperties.add(new PropertyIdImpl(property, group, false));
+        TemporalInfo temporalInfo = m_mapCategoryTemporalInfo.get(group);
+        if (temporalInfo == null) {
+          temporalInfo = m_mapPropertyTemporalInfo.get(new PropertyIdImpl(property, group, true));
+        }
+        setProperties.add(new PropertyIdImpl(property, group, temporalInfo != null));
       }
     }
-    return PropertyHelper.getReadRequest(setProperties);
+    //todo: need to pass in temporal info
+    Request r =  PropertyHelper.getReadRequest(setProperties);
+    r.setTemporalInfo(m_mapPropertyTemporalInfo);
+
+    return r;
   }
 
   Result createResult() {
     return new ResultImpl();
   }
-
 }

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostComponentResourceDefinition.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostComponentResourceDefinition.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostComponentResourceDefinition.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostComponentResourceDefinition.java Wed Oct 24 06:05:32 2012
@@ -105,7 +105,7 @@ public class HostComponentResourceDefini
 
   /**
    * Host_Component resource processor which is responsible for generating href's for host components.
-   * This is called by the {@link org.apache.ambari.server.api.services.ResultPostProcessor} during post processing of a result.
+   * This is called by the ResultPostProcessor during post processing of a result.
    */
   private class HostComponentHrefProcessor extends BaseHrefPostProcessor {
     @Override
@@ -126,13 +126,12 @@ public class HostComponentResourceDefini
       } else {
         super.process(request, resultNode, href);
       }
-
     }
   }
 
   /**
    * Host_Component resource processor which is responsible for generating a host section for host components.
-   * This is called by the {@link org.apache.ambari.server.api.services.ResultPostProcessor} during post processing of a result.
+   * This is called by the ResultPostProcessor during post processing of a result.
    */
   private class HostComponentHostProcessor implements PostProcessor {
     @Override

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/Request.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/Request.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/Request.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/Request.java Wed Oct 24 06:05:32 2012
@@ -22,6 +22,7 @@ import org.apache.ambari.server.api.reso
 import org.apache.ambari.server.api.services.serializers.ResultSerializer;
 import org.apache.ambari.server.controller.spi.Predicate;
 import org.apache.ambari.server.controller.spi.PropertyId;
+import org.apache.ambari.server.controller.spi.TemporalInfo;
 
 import java.net.URI;
 import java.util.List;
@@ -82,11 +83,12 @@ public interface Request {
   public Predicate getQueryPredicate();
 
   /**
-   * Obtain the set of partial response fields which were provided in the query string of the request uri.
+   * Obtain the partial response fields and associated temporal information which were provided
+   * in the query string of the request uri.
    *
-   * @return a set of the provided partial response fields
+   * @return map of partial response propertyId to temporal information
    */
-  public Set<String> getPartialResponseFields();
+  public Map<PropertyId, TemporalInfo> getFields();
 
   /**
    * Obtain the result serializer for the request. The default serializer is of type JSON.

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestImpl.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestImpl.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestImpl.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestImpl.java Wed Oct 24 06:05:32 2012
@@ -23,9 +23,11 @@ import org.apache.ambari.server.api.serv
 import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
 import org.apache.ambari.server.api.services.serializers.JsonSerializer;
 import org.apache.ambari.server.api.services.serializers.ResultSerializer;
+import org.apache.ambari.server.controller.internal.TemporalInfoImpl;
 import org.apache.ambari.server.controller.predicate.*;
 import org.apache.ambari.server.controller.spi.Predicate;
 import org.apache.ambari.server.controller.spi.PropertyId;
+import org.apache.ambari.server.controller.spi.TemporalInfo;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 
 import javax.ws.rs.core.HttpHeaders;
@@ -112,7 +114,6 @@ public class RequestImpl implements Requ
     String uri = getURI();
     int qsBegin = uri.indexOf("?");
 
-    //todo: consider returning an AlwaysPredicate in this case.
     if (qsBegin == -1) return null;
 
     Pattern pattern = Pattern.compile("!=|>=|<=|=|>|<");
@@ -141,13 +142,46 @@ public class RequestImpl implements Requ
   }
 
   @Override
-  public Set<String> getPartialResponseFields() {
+  public Map<PropertyId, TemporalInfo> getFields() {
+    Map<PropertyId, TemporalInfo> mapProperties;
     String partialResponseFields = m_uriInfo.getQueryParameters().getFirst("fields");
     if (partialResponseFields == null) {
-      return Collections.emptySet();
+      mapProperties = Collections.emptyMap();
     } else {
-      return new HashSet<String>(Arrays.asList(partialResponseFields.split(",")));
+      Set<String> setMatches = new HashSet<String>();
+      // Pattern basically splits a string using ',' as the deliminator unless ',' is between '[' and ']'.
+      // Actually, captures char sequences between ',' and all chars between '[' and ']' including ','.
+      Pattern re = Pattern.compile("[^,\\[]*?\\[[^\\]]*?\\]|[^,]+");
+      Matcher m = re.matcher(partialResponseFields);
+      while (m.find()){
+        for (int groupIdx = 0; groupIdx < m.groupCount() + 1; groupIdx++) {
+          setMatches.add(m.group(groupIdx));
+        }
+      }
+
+      mapProperties = new HashMap<PropertyId, TemporalInfo>(setMatches.size());
+      for (String field : setMatches) {
+        TemporalInfo temporalInfo = null;
+        if (field.contains("[")) {
+          String[] temporalData = field.substring(field.indexOf('[') + 1, field.indexOf(']')).split(",");
+          field = field.substring(0, field.indexOf('['));
+          long start = Long.parseLong(temporalData[0]);
+          long end   = -1;
+          long step  = -1;
+          if (temporalData.length >= 2) {
+            end = Long.parseLong(temporalData[1]);
+            if (temporalData.length == 3) {
+              step = Long.parseLong(temporalData[2]);
+            }
+          }
+          temporalInfo = new TemporalInfoImpl(start, end, step);
+        }
+        mapProperties.put(PropertyHelper.getPropertyId(
+            field, temporalInfo == null ? false : true), temporalInfo);
+      }
     }
+
+    return mapProperties;
   }
 
   @Override

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java Wed Oct 24 06:05:32 2012
@@ -57,7 +57,7 @@ public class ServiceService extends Base
   public Response getService(@Context HttpHeaders headers, @Context UriInfo ui,
                              @PathParam("serviceName") String serviceName) {
 
-    return handleRequest(headers, null, ui, org.apache.ambari.server.api.services.Request.Type.GET,
+    return handleRequest(headers, null, ui, Request.Type.GET,
         createResourceDefinition(serviceName, m_clusterName));
   }
 
@@ -72,7 +72,7 @@ public class ServiceService extends Base
   @GET
   @Produces("text/plain")
   public Response getServices(@Context HttpHeaders headers, @Context UriInfo ui) {
-    return handleRequest(headers, null, ui, org.apache.ambari.server.api.services.Request.Type.GET,
+    return handleRequest(headers, null, ui, Request.Type.GET,
         createResourceDefinition(null, m_clusterName));
   }
 
@@ -92,7 +92,8 @@ public class ServiceService extends Base
   public Response createService(String body, @Context HttpHeaders headers, @Context UriInfo ui,
                                 @PathParam("serviceName") String serviceName) {
 
-    return handleRequest(headers, body, ui, org.apache.ambari.server.api.services.Request.Type.PUT, createResourceDefinition(serviceName, m_clusterName));
+    return handleRequest(headers, body, ui, Request.Type.PUT,
+        createResourceDefinition(serviceName, m_clusterName));
   }
 
   /**
@@ -111,7 +112,7 @@ public class ServiceService extends Base
   public Response updateService(String body, @Context HttpHeaders headers, @Context UriInfo ui,
                                 @PathParam("serviceName") String serviceName) {
 
-    return handleRequest(headers, body, ui, org.apache.ambari.server.api.services.Request.Type.POST, createResourceDefinition(serviceName, m_clusterName));
+    return handleRequest(headers, body, ui, Request.Type.POST, createResourceDefinition(serviceName, m_clusterName));
   }
 
   /**
@@ -129,7 +130,7 @@ public class ServiceService extends Base
   public Response deleteService(@Context HttpHeaders headers, @Context UriInfo ui,
                                 @PathParam("serviceName") String serviceName) {
 
-    return handleRequest(headers, null, ui, org.apache.ambari.server.api.services.Request.Type.DELETE, createResourceDefinition(serviceName, m_clusterName));
+    return handleRequest(headers, null, ui, Request.Type.DELETE, createResourceDefinition(serviceName, m_clusterName));
   }
 
   /**

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java Wed Oct 24 06:05:32 2012
@@ -21,6 +21,7 @@ package org.apache.ambari.server.api.ser
 import org.apache.ambari.server.api.services.Result;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.api.util.TreeNode;
+import org.apache.log4j.Category;
 import org.codehaus.jackson.JsonFactory;
 import org.codehaus.jackson.JsonGenerator;
 import org.codehaus.jackson.util.DefaultPrettyPrinter;
@@ -82,7 +83,7 @@ public class JsonSerializer implements R
       m_generator.writeStartObject();
       writeHref(node);
       // resource props
-      handleResourceProperties(r.getCategories());
+      handleResourceProperties(r.getProperties());
     }
 
     for (TreeNode<Resource> child : node.getChildren()) {
@@ -101,22 +102,24 @@ public class JsonSerializer implements R
     }
   }
 
-  private void handleResourceProperties(Map<String, Map<String, Object>> mapCatProps) throws IOException {
-    for (Map.Entry<String, Map<String, Object>> categoryEntry : mapCatProps.entrySet()) {
-      String category = categoryEntry.getKey();
-      Map<String, Object> mapProps = categoryEntry.getValue();
-      if (category != null) {
-        m_generator.writeFieldName(category);
-        m_generator.writeStartObject();
-      }
+  private void handleResourceProperties(TreeNode<Map<String, Object>> node) throws IOException {
+    String category = node.getName();
 
-      for (Map.Entry<String, Object> propEntry : mapProps.entrySet()) {
-        m_generator.writeObjectField(propEntry.getKey(), propEntry.getValue());
-      }
+    if (category != null) {
+      m_generator.writeFieldName(category);
+      m_generator.writeStartObject();
+    }
 
-      if (category != null) {
-        m_generator.writeEndObject();
-      }
+    for (Map.Entry<String, Object> entry : node.getObject().entrySet()) {
+      m_generator.writeObjectField(entry.getKey(), entry.getValue());
+    }
+
+    for (TreeNode<Map<String, Object>> n : node.getChildren()) {
+      handleResourceProperties(n);
+    }
+
+    if (category != null) {
+      m_generator.writeEndObject();
     }
   }
 

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNode.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNode.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNode.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNode.java Wed Oct 24 06:05:32 2012
@@ -18,7 +18,7 @@
 
 package org.apache.ambari.server.api.util;
 
-import java.util.List;
+import java.util.Collection;
 
 /**
  * Tree where each node can have a name, properties and an associated object.
@@ -36,7 +36,7 @@ public interface TreeNode<T> {
    *
    * @return a list of child nodes or an empty list if a leaf node
    */
-  public List<TreeNode<T>> getChildren();
+  public Collection<TreeNode<T>> getChildren();
 
   /**
    * Obtain the object associated with this node.
@@ -98,4 +98,15 @@ public interface TreeNode<T> {
    * @return the requested property value or null
    */
   public String getProperty(String name);
+
+  /**
+   * Find a child node by name.
+   * The name may contain '/' to delimit names to find a child more then one level deep.
+   * To find a node named 'bar' that is a child of a child named 'foo', use the name 'foo/bar'.
+   *
+   * @param name  the name of the child.  May contain the '/' path separator.
+   *
+   * @return the requested node or null if the child was not found
+   */
+  public TreeNode<T> getChild(String name);
 }

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNodeImpl.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNodeImpl.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNodeImpl.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNodeImpl.java Wed Oct 24 06:05:32 2012
@@ -18,7 +18,7 @@
 
 package org.apache.ambari.server.api.util;
 
-import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -41,7 +41,7 @@ public class TreeNodeImpl<T> implements 
   /**
    * child nodes
    */
-  private List<TreeNode<T>> m_listChildren = new ArrayList<TreeNode<T>>();
+  private Map<String, TreeNode<T>> m_mapChildren = new HashMap<String, TreeNode<T>>();
 
   /**
    * associated object
@@ -72,8 +72,8 @@ public class TreeNodeImpl<T> implements 
   }
 
   @Override
-  public List<TreeNode<T>> getChildren() {
-    return m_listChildren;
+  public Collection<TreeNode<T>> getChildren() {
+    return m_mapChildren.values();
   }
 
   @Override
@@ -99,7 +99,7 @@ public class TreeNodeImpl<T> implements 
   @Override
   public TreeNode<T> addChild(T child, String name) {
     TreeNodeImpl<T> node = new TreeNodeImpl<T>(this, child, name);
-    m_listChildren.add(node);
+    m_mapChildren.put(name, node);
 
     return node;
   }
@@ -107,7 +107,7 @@ public class TreeNodeImpl<T> implements 
   @Override
   public TreeNode<T> addChild(TreeNode<T> child) {
     child.setParent(this);
-    m_listChildren.add(child);
+    m_mapChildren.put(child.getName(), child);
 
     return child;
   }
@@ -124,4 +124,16 @@ public class TreeNodeImpl<T> implements 
   public String getProperty(String name) {
     return m_mapNodeProps == null ? null : m_mapNodeProps.get(name);
   }
+
+  @Override
+  public TreeNode<T> getChild(String name) {
+    if (name != null && name.contains("/")) {
+      int i = name.indexOf('/');
+      String s = name.substring(0, i);
+      TreeNode<T> node = m_mapChildren.get(s);
+      return node == null ? null : node.getChild(name.substring(i + 1));
+    } else {
+      return m_mapChildren.get(name);
+    }
+  }
 }

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/ganglia/GangliaPropertyProvider.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/ganglia/GangliaPropertyProvider.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/ganglia/GangliaPropertyProvider.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/ganglia/GangliaPropertyProvider.java Wed Oct 24 06:05:32 2012
@@ -19,11 +19,8 @@
 package org.apache.ambari.server.controller.ganglia;
 
 import org.apache.ambari.server.AmbariException;
-import org.apache.ambari.server.controller.spi.Predicate;
-import org.apache.ambari.server.controller.spi.PropertyId;
-import org.apache.ambari.server.controller.spi.PropertyProvider;
-import org.apache.ambari.server.controller.spi.Request;
-import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.internal.TemporalInfoImpl;
+import org.apache.ambari.server.controller.spi.*;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.apache.ambari.server.controller.utilities.StreamProvider;
 import org.codehaus.jackson.map.ObjectMapper;
@@ -133,7 +130,7 @@ public class GangliaPropertyProvider imp
 //          propertyId.getName();
       String property = propertyId.getName();
 
-      Request.TemporalInfo temporalInfo = request.getTemporalInfo(propertyId);
+      TemporalInfo temporalInfo = request.getTemporalInfo(propertyId);
       String spec = getSpec(gangliaClusterName, hostName, property,
           temporalInfo.getStartTime(), temporalInfo.getEndTime(), temporalInfo.getStep());
 
@@ -193,9 +190,9 @@ public class GangliaPropertyProvider imp
   protected String getSpec(String gangliaCluster,
                                   String host,
                                   String metric,
-                                  Long startTime,
-                                  Long endTime,
-                                  Long step) {
+                                  long startTime,
+                                  long endTime,
+                                  long step) {
 
     StringBuilder sb = new StringBuilder();
 
@@ -208,13 +205,13 @@ public class GangliaPropertyProvider imp
        append("&m=").
        append(metric);
 
-    if (startTime != null) {
+    if (startTime != -1) {
       sb.append("&cs=").append(startTime);
     }
-    if (endTime != null) {
+    if (endTime != -1) {
       sb.append("&ce=").append(endTime);
     }
-    if (step != null) {
+    if (step != -1) {
       sb.append("&step=").append(step);
     }
     sb.append("&json=1");

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestImpl.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestImpl.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestImpl.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestImpl.java Wed Oct 24 06:05:32 2012
@@ -20,11 +20,9 @@ package org.apache.ambari.server.control
 
 import org.apache.ambari.server.controller.spi.PropertyId;
 import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.TemporalInfo;
 
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 /**
  * Default request implementation.
@@ -43,6 +41,13 @@ public class RequestImpl implements Requ
    */
   private final Set<Map<PropertyId, Object>> properties;
 
+  /**
+   * Map of property to temporal info.
+   */
+  private Map<PropertyId, TemporalInfo> m_mapTemporalInfo = new HashMap<PropertyId, TemporalInfo>();
+
+  private static final TemporalInfo DEFAULT_TEMPORAL_INFO = new TemporalInfoImpl(-1, -1, -1);
+
 
   // ----- Constructors ------------------------------------------------------
 
@@ -77,25 +82,13 @@ public class RequestImpl implements Requ
 
   @Override
   public TemporalInfo getTemporalInfo(PropertyId id) {
-    return new TemporalInfoImpl();
+    TemporalInfo info =  m_mapTemporalInfo.get(id);
+    return info == null ? DEFAULT_TEMPORAL_INFO : info;
   }
 
-
-  public static class TemporalInfoImpl implements TemporalInfo {
-    @Override
-    public Long getStartTime() {
-      return null;  //TODO
-    }
-
-    @Override
-    public Long getEndTime() {
-      return null;  //TODO
-    }
-
-    @Override
-    public Long getStep() {
-      return null;  //TODO
-    }
+  @Override
+  public void setTemporalInfo(Map<PropertyId, TemporalInfo> mapTemporalInfo) {
+    m_mapTemporalInfo = mapTemporalInfo;
   }
 
   @Override

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ResourceImpl.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ResourceImpl.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ResourceImpl.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ResourceImpl.java Wed Oct 24 06:05:32 2012
@@ -18,6 +18,8 @@
 
 package org.apache.ambari.server.controller.internal;
 
+import org.apache.ambari.server.api.util.TreeNode;
+import org.apache.ambari.server.api.util.TreeNodeImpl;
 import org.apache.ambari.server.controller.spi.PropertyId;
 import org.apache.ambari.server.controller.spi.Resource;
 
@@ -35,9 +37,11 @@ public class ResourceImpl implements Res
   private final Type type;
 
   /**
-   * The map of categories/properties for this resource.
+   * Tree of categories/properties.
+   * Each category is a sub node and each node contains a map of properties(n/v pairs).
    */
-  private final Map<String, Map<String, Object>> categories = new HashMap<String, Map<String, Object>>();
+  private final TreeNode<Map<String, Object>> m_treeProperties =
+      new TreeNodeImpl<Map<String, Object>>(null, new HashMap<String, Object>(), null);
 
 
   // ----- Constructors ------------------------------------------------------
@@ -60,33 +64,48 @@ public class ResourceImpl implements Res
   }
 
   @Override
-  public Map<String, Map<String, Object>> getCategories() {
-    return categories;
+  public TreeNode<Map<String, Object>> getProperties() {
+    return m_treeProperties;
   }
 
   @Override
-  public void setProperty(PropertyId id, Object value) {
-    String category = id.getCategory();
+  public Map<String, Map<String, Object>> getPropertiesMap() {
+    Map<String, Map<String, Object>> mapProps = new HashMap<String, Map<String, Object>>();
+    addNodeToMap(m_treeProperties, mapProps, null);
 
-    Map<String, Object> properties = categories.get(category);
+    return mapProps;
+  }
 
-    if (properties == null) {
-      properties = new HashMap<String, Object>();
-      categories.put(category, properties);
+  @Override
+  public void setProperty(PropertyId id, Object value) {
+    String category = id.getCategory();
+    TreeNode<Map<String, Object>> node;
+    if (category == null) {
+      node = m_treeProperties;
+    } else {
+      node = m_treeProperties.getChild(category);
+      if (node == null) {
+        String[] tokens = category.split("/");
+        node = m_treeProperties;
+        for (String t : tokens) {
+          TreeNode<Map<String, Object>> child = node.getChild(t);
+          if (child == null) {
+            child = node.addChild(new HashMap<String, Object>(), t);
+          }
+          node = child;
+        }
+      }
     }
-
-    properties.put(id.getName(), value);
+    node.getObject().put(id.getName(), value);
   }
 
   @Override
   public Object getPropertyValue(PropertyId id) {
+    String category = id.getCategory();
+    TreeNode<Map<String, Object>> node = (category == null) ? m_treeProperties :
+        m_treeProperties.getChild(category);
 
-    Map<String, Object> properties = categories.get(id.getCategory());
-
-    if (properties != null) {
-      return properties.get(id.getName());
-    }
-    return null;
+    return node == null ? null : node.getObject().get(id.getName());
   }
 
 
@@ -97,11 +116,53 @@ public class ResourceImpl implements Res
     StringBuilder sb = new StringBuilder();
 
     sb.append("Resource : ").append(type).append("\n");
-    for (Map.Entry<String, Map<String, Object>> catEntry : categories.entrySet()) {
-      for (Map.Entry<String, Object> propEntry : catEntry.getValue().entrySet()) {
-        sb.append("    ").append(catEntry.getKey()).append(".").append(propEntry.getKey()).append(" : ").append(propEntry.getValue()).append("\n");
-      }
-    }
+    sb.append("Properties:\n");
+
+    printPropertyNode(m_treeProperties, sb, null, "  ");
+
     return sb.toString();
   }
+
+
+  // ----- class private methods ---------------------------------------------
+
+  /**
+   * Recursively prints the properties for a given node and it's children to a StringBuffer.
+   *
+   * @param node      the node to print properties for
+   * @param sb        the SringBuffer to print to
+   * @param category  the absolute category name
+   * @param indent    the indent to be used
+   */
+  private void printPropertyNode(TreeNode<Map<String, Object>> node, StringBuilder sb, String category, String indent) {
+    if (node.getParent() != null) {
+      category = category == null ? node.getName() : category + '/' + node.getName();
+      sb.append(indent).append("Category: ").append(category).append('\n');
+      indent += "  ";
+    }
+    for (Map.Entry<String, Object> entry : node.getObject().entrySet()) {
+      sb.append(indent).append(entry.getKey()).append('=').append(entry.getValue()).append('\n');
+    }
+
+    for (TreeNode<Map<String, Object>> n : node.getChildren()) {
+      printPropertyNode(n, sb, category, indent);
+    }
+  }
+
+  /**
+   * Add the node properties to the specified map.
+   * Makes recursive calls for each child node.
+   *
+   * @param node      the node whose properties are to be added
+   * @param mapProps  the map that the props are to be added to
+   * @param path      the current category hierarchy
+   */
+  private void addNodeToMap(TreeNode<Map<String, Object>> node, Map<String, Map<String, Object>> mapProps, String path) {
+    path = path == null ? node.getName() : path + "/" + node.getName();
+    mapProps.put(path, node.getObject());
+
+    for (TreeNode<Map<String, Object>> child : node.getChildren()) {
+      addNodeToMap(child, mapProps, path);
+    }
+  }
 }

Added: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/TemporalInfoImpl.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/TemporalInfoImpl.java?rev=1401562&view=auto
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/TemporalInfoImpl.java (added)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/TemporalInfoImpl.java Wed Oct 24 06:05:32 2012
@@ -0,0 +1,71 @@
+/**
+ * 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.
+ */
+
+package org.apache.ambari.server.controller.internal;
+
+import org.apache.ambari.server.controller.spi.TemporalInfo;
+
+/**
+* Temporal query data.
+*/
+public class TemporalInfoImpl implements TemporalInfo {
+  private long m_startTime;
+  private long m_endTime;
+  private long m_step;
+
+  public TemporalInfoImpl(long startTime, long endTime, long step) {
+    m_startTime = startTime;
+    m_endTime = endTime;
+    m_step = step;
+  }
+
+  @Override
+  public Long getStartTime() {
+    return m_startTime;
+  }
+
+  @Override
+  public Long getEndTime() {
+    return m_endTime;
+  }
+
+  @Override
+  public Long getStep() {
+    return m_step;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    TemporalInfoImpl that = (TemporalInfoImpl) o;
+    return m_endTime == that.m_endTime &&
+           m_startTime == that.m_startTime &&
+           m_step == that.m_step;
+
+  }
+
+  @Override
+  public int hashCode() {
+    int result = (int) (m_startTime ^ (m_startTime >>> 32));
+    result = 31 * result + (int) (m_endTime ^ (m_endTime >>> 32));
+    result = 31 * result + (int) (m_step ^ (m_step >>> 32));
+    return result;
+  }
+}

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Request.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Request.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Request.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Request.java Wed Oct 24 06:05:32 2012
@@ -56,33 +56,5 @@ public interface Request {
    */
   public TemporalInfo getTemporalInfo(PropertyId id);
 
-  /**
-   * Temporal request information describing a range and increment of time.
-   */
-  public static interface TemporalInfo {
-
-    /**
-     * Get the start of the requested time range.  The time is given in
-     * seconds since the Unix epoch.
-     *
-     * @return the start time in seconds
-     */
-    public Long getStartTime();
-
-    /**
-     * Get the end of the requested time range.  The time is given in
-     * seconds since the Unix epoch.
-     *
-     * @return the end time in seconds
-     */
-    public Long getEndTime();
-
-    /**
-     * Get the requested time between each data point of the temporal
-     * data.  The time is given in seconds.
-     *
-     * @return the step time in seconds
-     */
-    public Long getStep();
-  }
+  public void setTemporalInfo(Map<PropertyId, TemporalInfo> mapTemporalInfo);
 }

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java Wed Oct 24 06:05:32 2012
@@ -17,6 +17,8 @@
  */
 package org.apache.ambari.server.controller.spi;
 
+import org.apache.ambari.server.api.util.TreeNode;
+
 import java.util.Map;
 
 /**
@@ -32,13 +34,22 @@ public interface Resource {
   public Type getType();
 
   /**
-   * Get the map of categories contained by this resource.  The map
-   * is keyed by the category name and contains maps of properties
-   * for each category.
+   * Get the properties contained by this resource.
+   * Each category is contained in a sub-node.
+   *
+   * @return resource properties tree
+   */
+  public TreeNode<Map<String, Object>> getProperties();
+
+  /**
+   * Obtain the properties contained by this group in a map structure.
+   * The category/property hierarchy is flattened into a map where
+   * each key is the absolute category name and the corresponding
+   * value is a map of properties(name/value pairs) for that category.
    *
-   * @return the map of categories
+   * @return  resource properties map
    */
-  public Map<String, Map<String, Object>> getCategories();
+  public Map<String, Map<String, Object>> getPropertiesMap();
 
   /**
    * Set a property value for the given property id on this resource.

Added: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/TemporalInfo.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/TemporalInfo.java?rev=1401562&view=auto
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/TemporalInfo.java (added)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/TemporalInfo.java Wed Oct 24 06:05:32 2012
@@ -0,0 +1,48 @@
+/**
+ * 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.
+ */
+
+package org.apache.ambari.server.controller.spi;
+
+/**
+ * Temporal query data.
+ */
+public interface TemporalInfo {
+  /**
+   * Get the start of the requested time range.  The time is given in
+   * seconds since the Unix epoch.
+   *
+   * @return the start time in seconds
+   */
+  Long getStartTime();
+
+  /**
+   * Get the end of the requested time range.  The time is given in
+   * seconds since the Unix epoch.
+   *
+   * @return the end time in seconds
+   */
+  Long getEndTime();
+
+  /**
+   * Get the requested time between each data point of the temporal
+   * data.  The time is given in seconds.
+   *
+   * @return the step time in seconds
+   */
+  Long getStep();
+}

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/PropertyHelper.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/PropertyHelper.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/PropertyHelper.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/PropertyHelper.java Wed Oct 24 06:05:32 2012
@@ -82,6 +82,37 @@ public class PropertyHelper {
     return getPropertyId(name, category);
   }
 
+  /**
+   * Helper to create a PropertyId from an string.
+   * The provided string must not be null and should be the fully qualified property name.
+   * The fully qualified property name may or may not have a category name.
+   * If the fully qualified property name contains a path separator char, then the
+   * path up to the last pth separator is considered the path and the token after the last
+   * path separator char is the property name.  If no path separator is present, the category
+   * is null and the property name is the provided string.
+   *
+   * @param absProperty  the fully qualified property
+   * @param temporal     whether the property is temporal
+   *
+   * @return a new PropertyId for the provided property string
+   *         with the temporal flag set to the provided value
+   */
+  public static PropertyId getPropertyId(String absProperty, boolean temporal) {
+    String category;
+    String name;
+
+    int lastPathSep = absProperty.lastIndexOf(EXTERNAL_PATH_SEP);
+    if (lastPathSep == -1) {
+      category = null;
+      name     = absProperty;
+    } else {
+      category = absProperty.substring(0, lastPathSep);
+      name     = absProperty.substring(lastPathSep + 1);
+    }
+
+    return getPropertyId(name, category, temporal);
+  }
+
   public static Set<PropertyId> getPropertyIds(Resource.Type resourceType, String providerKey) {
 
     Map<String, Set<PropertyId>> propertyIds = PROPERTY_IDS.get(resourceType);
@@ -105,7 +136,7 @@ public class PropertyHelper {
   public static Map<PropertyId, Object> getProperties(Resource resource) {
     Map<PropertyId, Object> properties = new HashMap<PropertyId, Object>();
 
-    Map<String, Map<String, Object>> categories = resource.getCategories();
+    Map<String, Map<String, Object>> categories = resource.getPropertiesMap();
 
     for (Map.Entry<String, Map<String, Object>> categoryEntry : categories.entrySet()) {
       for (Map.Entry<String, Object>  propertyEntry : categoryEntry.getValue().entrySet()) {

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/handlers/ReadHandlerTest.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/handlers/ReadHandlerTest.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/handlers/ReadHandlerTest.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/handlers/ReadHandlerTest.java Wed Oct 24 06:05:32 2012
@@ -23,10 +23,15 @@ import org.apache.ambari.server.api.reso
 import org.apache.ambari.server.api.services.Request;
 import org.apache.ambari.server.api.services.Result;
 import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.PropertyId;
+import org.apache.ambari.server.controller.spi.TemporalInfo;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.junit.Test;
 
 
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 import static org.easymock.EasyMock.*;
@@ -45,19 +50,23 @@ public class ReadHandlerTest {
     Predicate predicate = createMock(Predicate.class);
     Result result = createStrictMock(Result.class);
 
-    Set<String> setPartialResponseFields = new HashSet<String>();
-    setPartialResponseFields.add("foo");
-    setPartialResponseFields.add("bar/c");
-    setPartialResponseFields.add("bar/d/e");
+    Map<PropertyId, TemporalInfo> mapPartialResponseFields = new HashMap<PropertyId, TemporalInfo>();
+    mapPartialResponseFields.put(PropertyHelper.getPropertyId("foo"), null);
+    mapPartialResponseFields.put(PropertyHelper.getPropertyId("bar/c"), null);
+    mapPartialResponseFields.put(PropertyHelper.getPropertyId("bar/d/e"), null);
+    //Set<String> setPartialResponseFields = new HashSet<String>();
+//    setPartialResponseFields.add("foo");
+//    setPartialResponseFields.add("bar/c");
+//    setPartialResponseFields.add("bar/d/e");
 
     //expectations
     expect(request.getResourceDefinition()).andReturn(resourceDefinition);
     expect(resourceDefinition.getQuery()).andReturn(query);
 
-    expect(request.getPartialResponseFields()).andReturn(setPartialResponseFields);
-    query.addProperty(null, "foo");
-    query.addProperty("bar", "c");
-    query.addProperty("bar/d", "e");
+    expect(request.getFields()).andReturn(mapPartialResponseFields);
+    query.addProperty(null, "foo", null);
+    query.addProperty("bar", "c", null);
+    query.addProperty("bar/d", "e", null);
 
     expect(request.getQueryPredicate()).andReturn(predicate);
     query.setUserPredicate(predicate);

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/query/QueryImplTest.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/query/QueryImplTest.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/query/QueryImplTest.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/query/QueryImplTest.java Wed Oct 24 06:05:32 2012
@@ -1,6 +1,7 @@
 package org.apache.ambari.server.api.query;
 
 import org.apache.ambari.server.api.util.TreeNode;
+import org.apache.ambari.server.api.util.TreeNodeImpl;
 import org.apache.ambari.server.controller.internal.PropertyIdImpl;
 import org.apache.ambari.server.controller.predicate.AndPredicate;
 import org.apache.ambari.server.controller.predicate.BasePredicate;
@@ -29,8 +30,6 @@ public class QueryImplTest {
   @Test
   public void testExecute__Component_instance_noSpecifiedProps() throws Exception {
     Result result = createStrictMock(Result.class);
-    TreeNode<Resource> tree = createStrictMock(TreeNode.class);
-    TreeNode<Resource> componentNode = createStrictMock(TreeNode.class);
     ResourceDefinition componentResourceDef = createMock(ResourceDefinition.class);
     ResourceDefinition hostComponentResourceDef = createStrictMock(ResourceDefinition.class);
     Schema componentSchema = createMock(Schema.class);
@@ -38,8 +37,9 @@ public class QueryImplTest {
     PropertyId componentPropertyId = PropertyHelper.getPropertyId("componentId", "");
     Query hostComponentQuery = createStrictMock(Query.class);
     Result hostComponentQueryResult = createStrictMock(Result.class);
-    TreeNode<Resource> hostComponentResultTree = createMock(TreeNode.class);
 
+    TreeNode<Resource> tree = new TreeNodeImpl<Resource>(null, null, null);
+    TreeNode<Resource> hostComponentResultNode = new TreeNodeImpl<Resource>(null, null, null);
     List<Resource> listResources = Collections.singletonList(componentResource);
 
     Map<Resource.Type, String> mapResourceIds = new HashMap<Resource.Type, String>();
@@ -55,8 +55,8 @@ public class QueryImplTest {
         property("serviceId", "").equals("serviceName").and().
         property("componentId", "").equals("componentName").toPredicate();
 
-
     // expectations
+    expect(componentResource.getType()).andReturn(Resource.Type.Component).anyTimes();
     expect(componentResourceDef.getId()).andReturn("componentName").atLeastOnce();
     expect(m_controller.getSchema(Resource.Type.Component)).andReturn(componentSchema).atLeastOnce();
     expect(componentSchema.getCategories()).andReturn(Collections.<String, Set<String>>emptyMap());
@@ -71,31 +71,34 @@ public class QueryImplTest {
         eq(predicate))).andReturn(listResources);
 
     expect(result.getResultTree()).andReturn(tree);
-    expect(tree.addChild(componentResource, null)).andReturn(componentNode);
-    expect(componentNode.addChild(hostComponentResultTree)).andReturn(hostComponentResultTree);
     expect(componentResource.getPropertyValue(componentPropertyId)).andReturn("componentName");
     hostComponentResourceDef.setParentId(Resource.Type.Component, "componentName");
     expect(hostComponentResourceDef.getQuery()).andReturn(hostComponentQuery);
     expect(hostComponentQuery.execute()).andReturn(hostComponentQueryResult);
-    expect(hostComponentQueryResult.getResultTree()).andReturn(hostComponentResultTree);
-    hostComponentResultTree.setName("host_components");
-    hostComponentResultTree.setProperty("isCollection", "false");
+    expect(hostComponentQueryResult.getResultTree()).andReturn(hostComponentResultNode);
+
 
-    replay(m_controller, result, tree, componentNode, componentResourceDef, hostComponentResourceDef, componentSchema, componentResource,
-        hostComponentQuery, hostComponentQueryResult, hostComponentResultTree);
+    replay(m_controller, result, componentResourceDef, hostComponentResourceDef, componentSchema, componentResource,
+        hostComponentQuery, hostComponentQueryResult);
 
     QueryImpl query = new TestQuery(componentResourceDef, result);
     query.execute();
 
-    verify(m_controller, result, tree, componentNode, componentResourceDef, hostComponentResourceDef, componentSchema, componentResource,
-        hostComponentQuery, hostComponentQueryResult, hostComponentResultTree);
+    verify(m_controller, result, componentResourceDef, hostComponentResourceDef, componentSchema, componentResource,
+        hostComponentQuery, hostComponentQueryResult);
+
+    assertEquals(1, tree.getChildren().size());
+    TreeNode<Resource> componentNode = tree.getChild("Component:1");
+    assertEquals("Component:1", componentNode.getName());
+    assertEquals(componentResource, componentNode.getObject());
+    assertEquals(1, componentNode.getChildren().size());
+    assertSame(hostComponentResultNode, componentNode.getChild("host_components"));
+    assertEquals("false", hostComponentResultNode.getProperty("isCollection"));
   }
 
   @Test
   public void testExecute__Component_collection_noSpecifiedProps() throws Exception {
     Result result = createStrictMock(Result.class);
-    TreeNode<Resource> tree = createStrictMock(TreeNode.class);
-    TreeNode<Resource> componentNode = createStrictMock(TreeNode.class);
     ResourceDefinition componentResourceDef = createMock(ResourceDefinition.class);
     Schema componentSchema = createMock(Schema.class);
     Resource componentResource = createStrictMock(Resource.class);
@@ -104,6 +107,7 @@ public class QueryImplTest {
     Map<String, Set<String>> mapProperties = new HashMap<String, Set<String>>();
     mapProperties.put("", Collections.singleton("componentId"));
 
+    TreeNode<Resource> tree = new TreeNodeImpl<Resource>(null, null, null);
     List<Resource> listResources = Collections.singletonList(componentResource);
 
     Map<Resource.Type, String> mapResourceIds = new HashMap<Resource.Type, String>();
@@ -117,13 +121,13 @@ public class QueryImplTest {
         property("componentId", "").equals("componentName").toPredicate();
 
     // expectations
+    expect(componentResource.getType()).andReturn(Resource.Type.Component).anyTimes();
     expect(componentResourceDef.getId()).andReturn(null).atLeastOnce();
     expect(m_controller.getSchema(Resource.Type.Component)).andReturn(componentSchema).atLeastOnce();
     expect(componentSchema.getCategories()).andReturn(mapProperties);
 
     expect(componentSchema.getKeyPropertyId(Resource.Type.Component)).andReturn(componentPropertyId).atLeastOnce();
     expect(result.getResultTree()).andReturn(tree).atLeastOnce();
-    tree.setProperty("isCollection", "true");
 
     expect(componentResourceDef.getType()).andReturn(Resource.Type.Component).atLeastOnce();
     expect(componentResourceDef.getResourceIds()).andReturn(mapResourceIds);
@@ -134,43 +138,44 @@ public class QueryImplTest {
     expect(m_controller.getResources(eq(Resource.Type.Component), eq(PropertyHelper.getReadRequest(Collections.singleton(componentPropertyId))),
         eq(predicate))).andReturn(listResources);
 
-
-    expect(tree.addChild(componentResource, null)).andReturn(componentNode);
-
-    replay(m_controller, result, tree, componentNode, componentResourceDef, componentSchema, componentResource);
+    replay(m_controller, result,componentResourceDef, componentSchema, componentResource);
 
     QueryImpl query = new TestQuery(componentResourceDef, result);
     query.execute();
 
-    verify(m_controller, result, tree, componentNode, componentResourceDef, componentSchema, componentResource);
+    verify(m_controller, result, componentResourceDef, componentSchema, componentResource);
+
+    assertEquals("true", tree.getProperty("isCollection"));
+    assertEquals(1, tree.getChildren().size());
+    TreeNode<Resource> componentNode = tree.getChild("Component:1");
+    assertSame(componentResource, componentNode.getObject());
+    assertEquals(0, componentNode.getChildren().size());
   }
 
   @Test
   public void testExecute__collection_nullInternalPredicate_nullUserPredicate() throws Exception {
     Result result = createStrictMock(Result.class);
-    TreeNode<Resource> tree = createStrictMock(TreeNode.class);
-    TreeNode<Resource> clusterNode = createStrictMock(TreeNode.class);
     ResourceDefinition clusterResourceDef = createMock(ResourceDefinition.class);
     Schema clusterSchema = createMock(Schema.class);
     Resource clusterResource = createStrictMock(Resource.class);
     PropertyId clusterPropertyId = PropertyHelper.getPropertyId("clusterId", "");
 
-
     Map<String, Set<String>> mapProperties = new HashMap<String, Set<String>>();
     mapProperties.put("", Collections.singleton("clusterId"));
 
+    TreeNode<Resource> tree = new TreeNodeImpl<Resource>(null, null, null);
     List<Resource> listResources = Collections.singletonList(clusterResource);
 
     Map<Resource.Type, String> mapResourceIds = new HashMap<Resource.Type, String>();
 
     // expectations
+    expect(clusterResource.getType()).andReturn(Resource.Type.Cluster).anyTimes();
     expect(clusterResourceDef.getId()).andReturn(null).atLeastOnce();
     expect(m_controller.getSchema(Resource.Type.Component)).andReturn(clusterSchema).atLeastOnce();
     expect(clusterSchema.getCategories()).andReturn(mapProperties);
 
     expect(clusterSchema.getKeyPropertyId(Resource.Type.Component)).andReturn(clusterPropertyId).atLeastOnce();
     expect(result.getResultTree()).andReturn(tree).atLeastOnce();
-    tree.setProperty("isCollection", "true");
 
     expect(clusterResourceDef.getType()).andReturn(Resource.Type.Component).atLeastOnce();
     expect(clusterResourceDef.getResourceIds()).andReturn(mapResourceIds);
@@ -179,43 +184,46 @@ public class QueryImplTest {
         (Predicate) isNull())).andReturn(listResources);
 
 
-    expect(tree.addChild(clusterResource, null)).andReturn(clusterNode);
-
-    replay(m_controller, result, tree, clusterNode, clusterResourceDef, clusterSchema, clusterResource);
+    replay(m_controller, result, clusterResourceDef, clusterSchema, clusterResource);
 
     QueryImpl query = new TestQuery(clusterResourceDef, result);
     query.execute();
 
-    verify(m_controller, result, tree, clusterNode, clusterResourceDef, clusterSchema, clusterResource);
+    verify(m_controller, result, clusterResourceDef, clusterSchema, clusterResource);
+
+    assertEquals("true", tree.getProperty("isCollection"));
+    assertEquals(1, tree.getChildren().size());
+    TreeNode<Resource> clusterNode = tree.getChild("Cluster:1");
+    assertSame(clusterResource, clusterNode.getObject());
+    assertEquals(0, clusterNode.getChildren().size());
+
   }
 
   @Test
   public void testExecute__collection_nullInternalPredicate_nonNullUserPredicate() throws Exception {
     Result result = createStrictMock(Result.class);
-    TreeNode<Resource> tree = createStrictMock(TreeNode.class);
-    TreeNode<Resource> clusterNode = createStrictMock(TreeNode.class);
     ResourceDefinition clusterResourceDef = createMock(ResourceDefinition.class);
     Schema clusterSchema = createMock(Schema.class);
     Resource clusterResource = createStrictMock(Resource.class);
     PropertyId clusterPropertyId = PropertyHelper.getPropertyId("clusterId", "");
     Predicate userPredicate = createMock(Predicate.class);
 
-
     Map<String, Set<String>> mapProperties = new HashMap<String, Set<String>>();
     mapProperties.put("", Collections.singleton("clusterId"));
 
+    TreeNode<Resource> tree = new TreeNodeImpl<Resource>(null, null, null);
     List<Resource> listResources = Collections.singletonList(clusterResource);
 
     Map<Resource.Type, String> mapResourceIds = new HashMap<Resource.Type, String>();
 
     // expectations
+    expect(clusterResource.getType()).andReturn(Resource.Type.Cluster).anyTimes();
     expect(clusterResourceDef.getId()).andReturn(null).atLeastOnce();
     expect(m_controller.getSchema(Resource.Type.Component)).andReturn(clusterSchema).atLeastOnce();
     expect(clusterSchema.getCategories()).andReturn(mapProperties);
 
     expect(clusterSchema.getKeyPropertyId(Resource.Type.Component)).andReturn(clusterPropertyId).atLeastOnce();
     expect(result.getResultTree()).andReturn(tree).atLeastOnce();
-    tree.setProperty("isCollection", "true");
 
     expect(clusterResourceDef.getType()).andReturn(Resource.Type.Component).atLeastOnce();
     expect(clusterResourceDef.getResourceIds()).andReturn(mapResourceIds);
@@ -223,23 +231,24 @@ public class QueryImplTest {
     expect(m_controller.getResources(eq(Resource.Type.Component), eq(PropertyHelper.getReadRequest(Collections.singleton(clusterPropertyId))),
         eq(userPredicate))).andReturn(listResources);
 
-
-    expect(tree.addChild(clusterResource, null)).andReturn(clusterNode);
-
-    replay(m_controller, result, tree, clusterNode, clusterResourceDef, clusterSchema, clusterResource, userPredicate);
+    replay(m_controller, result,clusterResourceDef, clusterSchema, clusterResource, userPredicate);
 
     QueryImpl query = new TestQuery(clusterResourceDef, result);
     query.setUserPredicate(userPredicate);
     query.execute();
 
-    verify(m_controller, result, tree, clusterNode, clusterResourceDef, clusterSchema, clusterResource, userPredicate);
+    verify(m_controller, result, clusterResourceDef, clusterSchema, clusterResource, userPredicate);
+
+    assertEquals("true", tree.getProperty("isCollection"));
+    assertEquals(1, tree.getChildren().size());
+    TreeNode<Resource> clusterNode = tree.getChild("Cluster:1");
+    assertSame(clusterResource, clusterNode.getObject());
+    assertEquals(0, clusterNode.getChildren().size());
   }
 
   @Test
   public void testExecute__collection_nonNullInternalPredicate_nonNullUserPredicate() throws Exception {
     Result result = createStrictMock(Result.class);
-    TreeNode<Resource> tree = createStrictMock(TreeNode.class);
-    TreeNode<Resource> componentNode = createStrictMock(TreeNode.class);
     ResourceDefinition componentResourceDef = createMock(ResourceDefinition.class);
     Schema componentSchema = createMock(Schema.class);
     Resource componentResource = createStrictMock(Resource.class);
@@ -248,6 +257,7 @@ public class QueryImplTest {
     Map<String, Set<String>> mapProperties = new HashMap<String, Set<String>>();
     mapProperties.put("", Collections.singleton("componentId"));
 
+    TreeNode<Resource> tree = new TreeNodeImpl<Resource>(null, null, null);
     List<Resource> listResources = Collections.singletonList(componentResource);
 
     Map<Resource.Type, String> mapResourceIds = new HashMap<Resource.Type, String>();
@@ -267,13 +277,13 @@ public class QueryImplTest {
     Predicate predicate = new AndPredicate((BasePredicate) internalPredicate, (BasePredicate) userPredicate);
 
     // expectations
+    expect(componentResource.getType()).andReturn(Resource.Type.Component).anyTimes();
     expect(componentResourceDef.getId()).andReturn(null).atLeastOnce();
     expect(m_controller.getSchema(Resource.Type.Component)).andReturn(componentSchema).atLeastOnce();
     expect(componentSchema.getCategories()).andReturn(mapProperties);
 
     expect(componentSchema.getKeyPropertyId(Resource.Type.Component)).andReturn(componentPropertyId).atLeastOnce();
     expect(result.getResultTree()).andReturn(tree).atLeastOnce();
-    tree.setProperty("isCollection", "true");
 
     expect(componentResourceDef.getType()).andReturn(Resource.Type.Component).atLeastOnce();
     expect(componentResourceDef.getResourceIds()).andReturn(mapResourceIds);
@@ -284,16 +294,19 @@ public class QueryImplTest {
     expect(m_controller.getResources(eq(Resource.Type.Component), eq(PropertyHelper.getReadRequest(Collections.singleton(componentPropertyId))),
         eq(predicate))).andReturn(listResources);
 
-
-    expect(tree.addChild(componentResource, null)).andReturn(componentNode);
-
-    replay(m_controller, result, tree, componentNode, componentResourceDef, componentSchema, componentResource);
+    replay(m_controller, result, componentResourceDef, componentSchema, componentResource);
 
     QueryImpl query = new TestQuery(componentResourceDef, result);
     query.setUserPredicate(userPredicate);
     query.execute();
 
-    verify(m_controller, result, tree, componentNode, componentResourceDef, componentSchema, componentResource);
+    verify(m_controller, result, componentResourceDef, componentSchema, componentResource);
+
+    assertEquals("true", tree.getProperty("isCollection"));
+    assertEquals(1, tree.getChildren().size());
+    TreeNode<Resource> componentNode = tree.getChild("Component:1");
+    assertSame(componentResource, componentNode.getObject());
+    assertEquals(0, componentNode.getChildren().size());
   }
 
   @Test
@@ -313,12 +326,12 @@ public class QueryImplTest {
     replay(m_controller, resource, schema);
 
     Query query = new TestQuery(resource, null);
-    query.addProperty("category", "property");
+    query.addProperty("category", "property", null);
 
     assertEquals(1, query.getProperties().size());
     assertEquals(Collections.singleton("property"), query.getProperties().get("category"));
 
-    query.addProperty(null, "property2");
+    query.addProperty(null, "property2", null);
 
     assertEquals(2, query.getProperties().size());
     assertEquals(Collections.singleton("property2"), query.getProperties().get(null));
@@ -336,7 +349,11 @@ public class QueryImplTest {
     setProps.add("property");
     setProps.add("property2");
     mapSchemaProps.put("category", setProps);
-    mapSchemaProps.put(null, Collections.singleton("property3"));
+    Set<String> setInnerProps = new HashSet<String>();
+    setInnerProps.add("property3");
+    setInnerProps.add("property4");
+    mapSchemaProps.put("category/nestedCategory", setInnerProps);
+    mapSchemaProps.put(null, Collections.singleton("property5"));
 
     //expectations
     expect(resource.getType()).andReturn(Resource.Type.Service).atLeastOnce();
@@ -346,14 +363,91 @@ public class QueryImplTest {
     replay(m_controller, resource, schema);
 
     Query query = new TestQuery(resource, null);
-    query.addProperty(null, "category");
+    query.addProperty(null, "category", null);
 
-    assertEquals(1, query.getProperties().size());
-    Set<String> setResultProps = query.getProperties().get("category");
+    Map<String, Set<String>> mapProperties = query.getProperties();
+    assertEquals(2, mapProperties.size());
+    Set<String> setResultProps = mapProperties.get("category");
     assertEquals(2, setResultProps.size());
     assertTrue(setResultProps.contains("property"));
     assertTrue(setResultProps.contains("property2"));
 
+    setResultProps = mapProperties.get("category/nestedCategory");
+    assertEquals(2, setResultProps.size());
+    assertTrue(setResultProps.contains("property3"));
+    assertTrue(setResultProps.contains("property4"));
+
+    verify(m_controller, resource, schema);
+  }
+
+  @Test
+  public void testAddProperty__localSubCategory() {
+    ResourceDefinition resource = createMock(ResourceDefinition.class);
+    Schema schema = createMock(Schema.class);
+
+    Map<String, Set<String>> mapSchemaProps = new HashMap<String, Set<String>>();
+    Set<String> setProps = new HashSet<String>();
+    setProps.add("property");
+    setProps.add("property2");
+    mapSchemaProps.put("category", setProps);
+    Set<String> setInnerProps = new HashSet<String>();
+    setInnerProps.add("property3");
+    setInnerProps.add("property4");
+    mapSchemaProps.put("category/nestedCategory", setInnerProps);
+    mapSchemaProps.put(null, Collections.singleton("property5"));
+
+    //expectations
+    expect(resource.getType()).andReturn(Resource.Type.Service).atLeastOnce();
+    expect(m_controller.getSchema(Resource.Type.Service)).andReturn(schema);
+    expect(schema.getCategories()).andReturn(mapSchemaProps);
+
+    replay(m_controller, resource, schema);
+
+    Query query = new TestQuery(resource, null);
+    query.addProperty("category", "nestedCategory", null);
+
+    Map<String, Set<String>> mapProperties = query.getProperties();
+    assertEquals(1, mapProperties.size());
+    Set<String >setResultProps = mapProperties.get("category/nestedCategory");
+    assertEquals(2, setResultProps.size());
+    assertTrue(setResultProps.contains("property3"));
+    assertTrue(setResultProps.contains("property4"));
+
+    verify(m_controller, resource, schema);
+  }
+
+  @Test
+  public void testAddProperty__localCategorySubPropsOnly() {
+    ResourceDefinition resource = createMock(ResourceDefinition.class);
+    Schema schema = createMock(Schema.class);
+
+    Map<String, Set<String>> mapSchemaProps = new HashMap<String, Set<String>>();
+    Set<String> setInnerProps = new HashSet<String>();
+    setInnerProps.add("property3");
+    setInnerProps.add("property4");
+    mapSchemaProps.put("category/nestedCategory", setInnerProps);
+    mapSchemaProps.put(null, Collections.singleton("property5"));
+
+    //expectations
+    expect(resource.getType()).andReturn(Resource.Type.Service).atLeastOnce();
+    expect(m_controller.getSchema(Resource.Type.Service)).andReturn(schema);
+    expect(schema.getCategories()).andReturn(mapSchemaProps);
+
+    replay(m_controller, resource, schema);
+
+    Query query = new TestQuery(resource, null);
+    query.addProperty(null, "category", null);
+
+    Map<String, Set<String>> mapProperties = query.getProperties();
+    assertEquals(2, mapProperties.size());
+
+    assertTrue(mapProperties.get("category").isEmpty());
+
+    Set<String> setResultProps = mapProperties.get("category/nestedCategory");
+    assertEquals(2, setResultProps.size());
+    assertTrue(setResultProps.contains("property3"));
+    assertTrue(setResultProps.contains("property4"));
+
     verify(m_controller, resource, schema);
   }
 
@@ -379,7 +473,7 @@ public class QueryImplTest {
     replay(m_controller, resource, subResource, schema);
 
     Query query = new TestQuery(resource, null);
-    query.addProperty(null, "components");
+    query.addProperty(null, "components", null);
 
     verify(m_controller, resource, subResource, schema);
   }

Modified: incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/services/RequestImplTest.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/services/RequestImplTest.java?rev=1401562&r1=1401561&r2=1401562&view=diff
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/services/RequestImplTest.java (original)
+++ incubator/ambari/branches/AMBARI-666/ambari-server/src/test/java/org/apache/ambari/server/api/services/RequestImplTest.java Wed Oct 24 06:05:32 2012
@@ -1,18 +1,24 @@
 package org.apache.ambari.server.api.services;
 
 import org.apache.ambari.server.api.resources.ResourceDefinition;
+import org.apache.ambari.server.controller.internal.TemporalInfoImpl;
 import org.apache.ambari.server.controller.predicate.*;
 import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.PropertyId;
+import org.apache.ambari.server.controller.spi.TemporalInfo;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.junit.Test;
 
 import static org.easymock.EasyMock.*;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.UriInfo;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -42,6 +48,55 @@ public class RequestImplTest {
     assertEquals(expectedPredicate, predicate);
   }
 
+  @Test
+  public void testGetFields() {
+    UriInfo uriInfo = createMock(UriInfo.class);
+    MultivaluedMap<String, String> mapQueryParams = createMock(MultivaluedMap.class);
+    String fields = "prop,category/prop1,category2/category3/prop2[1,2,3],prop3[4,5,6],category4[7,8,9],sub-resource/*[10,11,12],finalProp";
+
+    expect(uriInfo.getQueryParameters()).andReturn(mapQueryParams);
+    expect(mapQueryParams.getFirst("fields")).andReturn(fields);
+
+    replay(uriInfo, mapQueryParams);
+
+    Request request =  new TestRequest(null, null, uriInfo, Request.Type.GET, null);
+    Map<PropertyId, TemporalInfo> mapFields = request.getFields();
+
+    assertEquals(7, mapFields.size());
+
+    PropertyId prop = PropertyHelper.getPropertyId("prop", null, false);
+    assertTrue(mapFields.containsKey(prop));
+    assertNull(mapFields.get(prop));
+
+    PropertyId prop1 = PropertyHelper.getPropertyId("prop1", "category", false);
+    assertTrue(mapFields.containsKey(prop1));
+    assertNull(mapFields.get(prop1));
+
+    PropertyId prop2 = PropertyHelper.getPropertyId("prop2", "category2/category3", true);
+    assertTrue(mapFields.containsKey(prop2));
+    assertEquals(new TemporalInfoImpl(1, 2, 3), mapFields.get(prop2));
+
+    PropertyId prop3 = PropertyHelper.getPropertyId("prop3", null, true);
+    assertTrue(mapFields.containsKey(prop3));
+    assertEquals(new TemporalInfoImpl(4, 5, 6), mapFields.get(prop3));
+
+    PropertyId category4 = PropertyHelper.getPropertyId("category4", null, true);
+    assertTrue(mapFields.containsKey(category4));
+    assertEquals(new TemporalInfoImpl(7, 8, 9), mapFields.get(category4));
+
+    PropertyId subResource = PropertyHelper.getPropertyId("*", "sub-resource", true);
+    assertTrue(mapFields.containsKey(subResource));
+    assertEquals(new TemporalInfoImpl(10, 11, 12), mapFields.get(subResource));
+
+    PropertyId finalProp = PropertyHelper.getPropertyId("finalProp", null, false);
+    assertTrue(mapFields.containsKey(finalProp));
+    assertNull(mapFields.get(finalProp));
+
+
+    verify(uriInfo, mapQueryParams);
+  }
+
+
   private class TestRequest extends RequestImpl {
     private TestRequest(HttpHeaders headers, String body, UriInfo uriInfo, Type requestType, ResourceDefinition resourceDefinition) {
       super(headers, body, uriInfo, requestType, resourceDefinition);