소스 검색

AMBARI-1406. Provide API support for including query string in http message body

git-svn-id: https://svn.apache.org/repos/asf/incubator/ambari/trunk@1455293 13f79535-47bb-0310-9956-ffa450edef68
John Speidel 12 년 전
부모
커밋
5357f39239
50개의 변경된 파일2919개의 추가작업 그리고 2259개의 파일을 삭제
  1. 2 0
      CHANGES.txt
  2. 58 18
      ambari-server/src/main/java/org/apache/ambari/server/api/handlers/BaseManagementHandler.java
  3. 2 2
      ambari-server/src/main/java/org/apache/ambari/server/api/handlers/CreateHandler.java
  4. 3 3
      ambari-server/src/main/java/org/apache/ambari/server/api/handlers/DeleteHandler.java
  5. 92 28
      ambari-server/src/main/java/org/apache/ambari/server/api/handlers/QueryCreateHandler.java
  6. 8 6
      ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java
  7. 0 51
      ambari-server/src/main/java/org/apache/ambari/server/api/handlers/RequestHandlerFactory.java
  8. 3 3
      ambari-server/src/main/java/org/apache/ambari/server/api/handlers/UpdateHandler.java
  9. 90 35
      ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseRequest.java
  10. 26 33
      ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseService.java
  11. 7 1
      ambari-server/src/main/java/org/apache/ambari/server/api/services/DeleteRequest.java
  12. 8 1
      ambari-server/src/main/java/org/apache/ambari/server/api/services/GetRequest.java
  13. 85 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/NamedPropertySet.java
  14. 7 1
      ambari-server/src/main/java/org/apache/ambari/server/api/services/PostRequest.java
  15. 7 1
      ambari-server/src/main/java/org/apache/ambari/server/api/services/PutRequest.java
  16. 7 12
      ambari-server/src/main/java/org/apache/ambari/server/api/services/QueryPostRequest.java
  17. 14 22
      ambari-server/src/main/java/org/apache/ambari/server/api/services/Request.java
  18. 124 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestBody.java
  19. 2 2
      ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestFactory.java
  20. 43 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/BodyParseException.java
  21. 0 73
      ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/JsonPropertyParser.java
  22. 116 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/JsonRequestBodyParser.java
  23. 15 6
      ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/RequestBodyParser.java
  24. 16 4
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HttpProxyPropertyProvider.java
  25. 9 7
      ambari-server/src/test/java/org/apache/ambari/server/api/TestSuite.java
  26. 63 24
      ambari-server/src/test/java/org/apache/ambari/server/api/handlers/CreateHandlerTest.java
  27. 69 25
      ambari-server/src/test/java/org/apache/ambari/server/api/handlers/DeleteHandlerTest.java
  28. 378 43
      ambari-server/src/test/java/org/apache/ambari/server/api/handlers/QueryCreateHandlerTest.java
  29. 1 27
      ambari-server/src/test/java/org/apache/ambari/server/api/handlers/ReadHandlerTest.java
  30. 68 24
      ambari-server/src/test/java/org/apache/ambari/server/api/handlers/UpdateHandlerTest.java
  31. 38 43
      ambari-server/src/test/java/org/apache/ambari/server/api/services/ActionServiceTest.java
  32. 258 36
      ambari-server/src/test/java/org/apache/ambari/server/api/services/BaseRequestTest.java
  33. 129 46
      ambari-server/src/test/java/org/apache/ambari/server/api/services/BaseServiceTest.java
  34. 52 150
      ambari-server/src/test/java/org/apache/ambari/server/api/services/ClusterServiceTest.java
  35. 64 217
      ambari-server/src/test/java/org/apache/ambari/server/api/services/ComponentServiceTest.java
  36. 32 28
      ambari-server/src/test/java/org/apache/ambari/server/api/services/ConfigurationServiceTest.java
  37. 18 16
      ambari-server/src/test/java/org/apache/ambari/server/api/services/DeleteRequestTest.java
  38. 18 15
      ambari-server/src/test/java/org/apache/ambari/server/api/services/GetRequestTest.java
  39. 64 219
      ambari-server/src/test/java/org/apache/ambari/server/api/services/HostComponentServiceTest.java
  40. 64 242
      ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java
  41. 68 0
      ambari-server/src/test/java/org/apache/ambari/server/api/services/NamedPropertySetTest.java
  42. 18 16
      ambari-server/src/test/java/org/apache/ambari/server/api/services/PostRequestTest.java
  43. 18 16
      ambari-server/src/test/java/org/apache/ambari/server/api/services/PutRequestTest.java
  44. 65 0
      ambari-server/src/test/java/org/apache/ambari/server/api/services/QueryPostRequestTest.java
  45. 63 0
      ambari-server/src/test/java/org/apache/ambari/server/api/services/RequestBodyTest.java
  46. 64 195
      ambari-server/src/test/java/org/apache/ambari/server/api/services/ServiceServiceTest.java
  47. 63 428
      ambari-server/src/test/java/org/apache/ambari/server/api/services/StacksServiceTest.java
  48. 45 0
      ambari-server/src/test/java/org/apache/ambari/server/api/services/parsers/BodyParseExceptionTest.java
  49. 0 140
      ambari-server/src/test/java/org/apache/ambari/server/api/services/parsers/JsonPropertyParserTest.java
  50. 455 0
      ambari-server/src/test/java/org/apache/ambari/server/api/services/parsers/JsonRequestBodyParserTest.java

+ 2 - 0
CHANGES.txt

@@ -15,6 +15,8 @@ Trunk (unreleased changes):
  AMBARI-1602. Edit User - drop the requirement to specify the old 
  password. (swagle)
 
+ AMBARI-1406. Provide API support for including query string in http message body. (jspeidel)
+
  AMBARI-1592. Change how configurations are propagated (ncole)
 
  AMBARI-1593. Change host override JSON to include version tag (ncole)

+ 58 - 18
ambari-server/src/main/java/org/apache/ambari/server/api/handlers/BaseManagementHandler.java

@@ -18,12 +18,11 @@
 
 package org.apache.ambari.server.api.handlers;
 
-import org.apache.ambari.server.api.predicate.InvalidQueryException;
 import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.api.services.NamedPropertySet;
 import org.apache.ambari.server.api.services.Request;
 import org.apache.ambari.server.api.services.Result;
 import org.apache.ambari.server.api.services.ResultImpl;
-import org.apache.ambari.server.api.services.ResultStatus;
 import org.apache.ambari.server.api.services.persistence.PersistenceManager;
 import org.apache.ambari.server.api.services.persistence.PersistenceManagerImpl;
 import org.apache.ambari.server.api.util.TreeNode;
@@ -35,6 +34,7 @@ import org.apache.ambari.server.controller.utilities.ClusterControllerHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
@@ -54,29 +54,32 @@ public abstract class BaseManagementHandler implements RequestHandler {
    */
   PersistenceManager m_pm = new PersistenceManagerImpl(getClusterController());
 
+  /**
+   * Constructor.
+   */
   protected BaseManagementHandler() {
   }
 
+
+  @Override
   public Result handleRequest(Request request) {
-    ResourceInstance resource = request.getResource();
-    Predicate queryPredicate;
-    try {
-      queryPredicate = request.getQueryPredicate();
-    } catch (InvalidQueryException e) {
-      return new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST,
-          "Invalid Request: " + e.getMessage()));
-    }
+    ResourceInstance resource       = request.getResource();
+    Predicate        queryPredicate = request.getQueryPredicate();
+
     if (queryPredicate != null) {
       resource.getQuery().setUserPredicate(queryPredicate);
     }
 
-    return handleRequest(resource, request.getHttpBodyProperties());
-  }
-
-  protected Result handleRequest(ResourceInstance resource, Set<Map<String, Object>> setProperties) {
-    return persist(resource, setProperties);
+    return persist(resource, getHttpBodyProperties(request));
   }
 
+  /**
+   * Create a result from a request status.
+   *
+   * @param requestStatus  the request stats to build the result from.
+   *
+   * @return  a Result instance for the provided request status
+   */
   protected Result createResult(RequestStatus requestStatus) {
 
     boolean            isSynchronous = requestStatus.getStatus() == RequestStatus.Status.Complete;
@@ -98,18 +101,55 @@ public abstract class BaseManagementHandler implements RequestHandler {
         resourcesNode.addChild(resource, resource.getType() + ":" + count++);
       }
     }
-
     return result;
   }
 
-  //todo: controller should be injected
+  /**
+   * Obtain a set of property maps from the request.
+   * Convenience method that converts a Set<NamedPropertySet> from the request to a Set<Map<String, Object>>.
+   *
+   * @param request  the current request
+   *
+   * @return  a set of property maps for the request
+   */
+  protected Set<Map<String, Object>> getHttpBodyProperties(Request request) {
+    Set<NamedPropertySet> setNamedProps = request.getHttpBodyProperties();
+    Set<Map<String, Object>> setProps = new HashSet<Map<String, Object>>(setNamedProps.size());
+
+    for (NamedPropertySet namedProps : setNamedProps) {
+      setProps.add(namedProps.getProperties());
+    }
+
+    return setProps;
+  }
+
+  //todo: inject ClusterController, PersistenceManager
+
+  /**
+   * Get the cluster controller instance.
+   *
+   * @return cluster controller
+   */
   protected ClusterController getClusterController() {
     return ClusterControllerHelper.getClusterController();
   }
 
+  /**
+   * Get the persistence manager instance.
+   *
+   * @return persistence manager
+   */
   protected PersistenceManager getPersistenceManager() {
     return m_pm;
   }
 
-  protected abstract Result persist(ResourceInstance r, Set<Map<String, Object>> properties);
+  /**
+   * Persist the operation to the back end.
+   *
+   * @param request           the requests resource instance
+   * @param setProperties  request properties
+   *
+   * @return the result of the persist operation
+   */
+  protected abstract Result persist(ResourceInstance request, Set<Map<String, Object>> setProperties);
 }

+ 2 - 2
ambari-server/src/main/java/org/apache/ambari/server/api/handlers/CreateHandler.java

@@ -33,10 +33,10 @@ import java.util.Set;
 public class CreateHandler extends BaseManagementHandler {
 
   @Override
-  protected Result persist(ResourceInstance r, Set<Map<String, Object>> properties) {
+  protected Result persist(ResourceInstance request, Set<Map<String, Object>> setProperties) {
     Result result;
     try {
-      RequestStatus status = getPersistenceManager().create(r, properties);
+      RequestStatus status = getPersistenceManager().create(request, setProperties);
 
       result = createResult(status);
 

+ 3 - 3
ambari-server/src/main/java/org/apache/ambari/server/api/handlers/DeleteHandler.java

@@ -33,10 +33,10 @@ import java.util.Set;
 public class DeleteHandler extends BaseManagementHandler implements RequestHandler {
 
   @Override
-  protected Result persist(ResourceInstance r, Set<Map<String, Object>> properties) {
+  protected Result persist(ResourceInstance request, Set<Map<String, Object>> setProperties) {
     Result result;
       try {
-        RequestStatus status = getPersistenceManager().delete(r, properties);
+        RequestStatus status = getPersistenceManager().delete(request, setProperties);
         result = createResult(status);
 
         if (result.isSynchronous()) {
@@ -49,7 +49,7 @@ public class DeleteHandler extends BaseManagementHandler implements RequestHandl
       } catch (NoSuchParentResourceException e) {
         result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.NOT_FOUND, e));
       } catch (NoSuchResourceException e) {
-        if (r.isCollectionResource()) {
+        if (request.isCollectionResource()) {
           //todo: The query didn't match any resource so no resources were updated.
           //todo: 200 may be ok but we need to return a collection
           //todo: of resources that were updated.

+ 92 - 28
ambari-server/src/main/java/org/apache/ambari/server/api/handlers/QueryCreateHandler.java

@@ -22,10 +22,8 @@ package org.apache.ambari.server.api.handlers;
 import org.apache.ambari.server.api.resources.ResourceInstance;
 import org.apache.ambari.server.api.resources.ResourceInstanceFactory;
 import org.apache.ambari.server.api.resources.ResourceInstanceFactoryImpl;
+import org.apache.ambari.server.api.services.*;
 import org.apache.ambari.server.api.services.Request;
-import org.apache.ambari.server.api.services.ResultStatus;
-import org.apache.ambari.server.api.services.Result;
-import org.apache.ambari.server.api.services.ResultImpl;
 import org.apache.ambari.server.api.util.TreeNode;
 import org.apache.ambari.server.controller.spi.*;
 
@@ -44,30 +42,55 @@ public class QueryCreateHandler extends BaseManagementHandler {
     if (queryResult.getStatus().isErrorState() ||
         queryResult.getResultTree().getChildren().isEmpty()) {
 
-      //return the query result if result has error state or contains no resources
-      //todo: For case where no resources are returned, will return 200 ok.
-      //todo: What is the appropriate status code?
+      //if query result has error state or contains no resources return it
+      // currently returns 200 for case where query returns no rows
       return queryResult;
     }
 
-    ResourceInstance resource = request.getResource();
-    Resource.Type createType = getCreateType(request.getHttpBody(), resource);
-    Set<Map<String, Object>> setProperties = buildCreateSet(request, queryResult, createType);
+    Map<Resource.Type, Set<Map<String, Object>>> mapProperties;
+    try {
+      mapProperties = buildCreateSet(request, queryResult);
+    } catch (IllegalArgumentException e) {
+      return createInvalidRequestResult(e.getMessage());
+    }
+
+    if (mapProperties.size() != 1) {
+      return createInvalidRequestResult(mapProperties.size() == 0 ?
+          "A minimum of one sub-resource must be specified for creation." :
+          "Multiple sub-resource types may not be created in the same request.");
+    }
+
+    Map.Entry<Resource.Type, Set<Map<String, Object>>> entry = mapProperties.entrySet().iterator().next();
     ResourceInstance createResource = getResourceFactory().createResource(
-        createType, request.getResource().getIds());
+        entry.getKey(), request.getResource().getIds());
 
-    return super.handleRequest(createResource, setProperties);
+    return persist(createResource, entry.getValue());
   }
 
-  private Set<Map<String, Object>> buildCreateSet(Request request, Result queryResult, Resource.Type createType) {
-    Set<Map<String, Object>> setRequestProps = request.getHttpBodyProperties();
-    Set<Map<String, Object>> setCreateProps = new HashSet<Map<String, Object>>(setRequestProps.size());
+  /**
+   * Build the property set for all sub-resource to be created.
+   * This includes determining the sub-resource type and creating a property set for each matching parent.
+   *
+   * @param request      the current request
+   * @param queryResult  the result of the query for matching parents
+   *
+   * @return a map of sub-resource types to be created and their associated properties
+   *
+   * @throws IllegalArgumentException  if no sub-resource type was specified or it is not a valid
+   *                                   sub-resource of the parent.
+   */
+  private Map<Resource.Type, Set<Map<String, Object>>> buildCreateSet(Request request, Result queryResult)
+    throws IllegalArgumentException {
+
+    Set<NamedPropertySet> setRequestProps = request.getHttpBodyProperties();
+
+    HashMap<Resource.Type, Set<Map<String, Object>>> mapProps =
+        new HashMap<Resource.Type, Set<Map<String, Object>>>();
 
     ResourceInstance  resource            = request.getResource();
     Resource.Type     type                = resource.getResourceDefinition().getType();
     ClusterController controller          = getClusterController();
     String            resourceKeyProperty = controller.getSchema(type).getKeyPropertyId(type);
-    String            createKeyProperty   = controller.getSchema(createType).getKeyPropertyId(type);
 
     TreeNode<Resource> tree = queryResult.getResultTree();
     Collection<TreeNode<Resource>> treeChildren = tree.getChildren();
@@ -75,29 +98,61 @@ public class QueryCreateHandler extends BaseManagementHandler {
       Resource r = node.getObject();
       Object keyVal = r.getPropertyValue(resourceKeyProperty);
 
-      for (Map<String, Object> mapProps : setRequestProps) {
-        Map<String, Object> mapResourceProps = new HashMap<String, Object>(mapProps);
-        mapResourceProps.put(createKeyProperty, keyVal);
+      for (NamedPropertySet namedProps : setRequestProps) {
+        Map<String, Object> mapResourceProps = new HashMap<String, Object>(namedProps.getProperties());
+        Resource.Type createType = getCreateType(resource, namedProps.getName());
+        mapResourceProps.put(controller.getSchema(createType).
+            getKeyPropertyId(resource.getResourceDefinition().getType()), keyVal);
+        Set<Map<String, Object>> setCreateProps = mapProps.get(createType);
+        if (setCreateProps == null) {
+          setCreateProps = new HashSet<Map<String, Object>>();
+          mapProps.put(createType, setCreateProps);
+        }
         setCreateProps.add(mapResourceProps);
       }
     }
-    return setCreateProps;
+    return mapProps;
   }
 
-  private Resource.Type getCreateType(String requestBody, ResourceInstance resource) {
-    int startIdx = requestBody.indexOf("\"") + 1;
-    int endIdx = requestBody.indexOf("\"", startIdx + 1);
+  /**
+   * Determine the sub-resurce type(s) to be created.
+   *
+   * @param resource         the requests resource instance
+   * @param subResourceName  the name of the sub-resource to be created
+   * @return  the resource type
+   *
+   * @throws IllegalArgumentException  if the specified sub-resource name is empty or it is not a valid
+   *                                   sub-resource of the parent.
+   */
+  private Resource.Type getCreateType(ResourceInstance resource, String subResourceName) throws IllegalArgumentException{
+    if (subResourceName == null || subResourceName.equals("")) {
+      throw new IllegalArgumentException("A sub-resource name must be supplied.");
+    }
+    ResourceInstance res = resource.getSubResources().get(subResourceName);
+
+    if (res == null) {
+      throw new IllegalArgumentException("The specified sub-resource name is not valid: '" + subResourceName + "'.");
+    }
+
+    return res.getResourceDefinition().getType();
+  }
 
-    ResourceInstance res =  resource.getSubResources().get(requestBody.substring(startIdx, endIdx));
-    return res == null ? null : res.getResourceDefinition().getType();
+  /**
+   * Convenience method to create a result for invalid requests.
+   *
+   * @param msg  message indicating why the request is invalid
+   *
+   * @return  a request with a 400 status and msg set
+   */
+  private Result createInvalidRequestResult(String msg) {
+    return new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST, "Invalid Request: " + msg));
   }
 
   @Override
-  protected Result persist(ResourceInstance r, Set<Map<String, Object>> properties) {
+  protected Result persist(ResourceInstance request, Set<Map<String, Object>> setProperties) {
     Result result;
     try {
-      RequestStatus status = getPersistenceManager().create(r, properties);
-
+      RequestStatus status = getPersistenceManager().create(request, setProperties);
       result = createResult(status);
 
       if (result.isSynchronous()) {
@@ -105,7 +160,6 @@ public class QueryCreateHandler extends BaseManagementHandler {
       } else {
         result.setResultStatus(new ResultStatus(ResultStatus.STATUS.ACCEPTED));
       }
-
     } catch (UnsupportedPropertyException e) {
       result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST, e));
     } catch (ResourceAlreadyExistsException e) {
@@ -119,10 +173,20 @@ public class QueryCreateHandler extends BaseManagementHandler {
     return result;
   }
 
+  /**
+   * Get the resource factory instance.
+   * @return  a factory for creating resource instances
+   */
   protected ResourceInstanceFactory getResourceFactory() {
+    //todo: inject
     return new ResourceInstanceFactoryImpl();
   }
 
+  /**
+   * Read handler instance.  Used for obtaining matching parents which match the query.
+   *
+   * @return  read handler instance
+   */
   protected RequestHandler getReadHandler() {
     return m_readHandler;
   }

+ 8 - 6
ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java

@@ -18,7 +18,6 @@
 
 package org.apache.ambari.server.api.handlers;
 
-import org.apache.ambari.server.api.predicate.InvalidQueryException;
 import org.apache.ambari.server.api.services.Request;
 import org.apache.ambari.server.api.services.ResultImpl;
 import org.apache.ambari.server.api.services.ResultStatus;
@@ -78,10 +77,7 @@ public class ReadHandler implements RequestHandler {
     } catch (IllegalArgumentException e) {
       result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST,
           "Invalid Request: " + e.getMessage()));
-    } catch (InvalidQueryException e) {
-      result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST,
-          "Invalid Request: " + e.getMessage()));
-    } catch (RuntimeException e) {
+    }  catch (RuntimeException e) {
       if (LOG.isErrorEnabled()) {
         LOG.error("Caught a runtime exception executing a query", e);
       }
@@ -91,7 +87,13 @@ public class ReadHandler implements RequestHandler {
     return result;
   }
 
-  private void addFieldsToQuery(Request request, Query query) throws IllegalArgumentException {
+  /**
+   * Add partial response fields to the provided query.
+   *
+   * @param request  the current request
+   * @param query    the associated query   *
+   */
+  private void addFieldsToQuery(Request request, Query query) {
     //Partial response
     for (Map.Entry<String, TemporalInfo> entry : request.getFields().entrySet()) {
       // Iterate over map and add props/temporalInfo

+ 0 - 51
ambari-server/src/main/java/org/apache/ambari/server/api/handlers/RequestHandlerFactory.java

@@ -1,51 +0,0 @@
-/**
- * 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.api.handlers;
-
-import org.apache.ambari.server.api.services.Request;
-
-/**
- * Factory for {@link RequestHandler}
- * Returns the appropriate request handler based on the request.
- */
-public class RequestHandlerFactory {
-  /**
-   * Return an instance of the correct request handler based on the request type.
-   *
-   * @param requestType the request type.  Is one of {@link Request.Type}
-   * @return a request handler for the request
-   */
-  public RequestHandler getRequestHandler(Request.Type requestType) {
-    switch (requestType) {
-      case GET:
-        return new ReadHandler();
-      case POST:
-        return new CreateHandler();
-      case PUT:
-        return new UpdateHandler();
-      case DELETE:
-        return new DeleteHandler();
-      case QUERY_POST:
-        return new QueryCreateHandler();
-      default:
-        //todo:
-        throw new UnsupportedOperationException("Unsupported Request Type: " + requestType);
-    }
-  }
-}

+ 3 - 3
ambari-server/src/main/java/org/apache/ambari/server/api/handlers/UpdateHandler.java

@@ -32,10 +32,10 @@ import java.util.Set;
 public class UpdateHandler extends BaseManagementHandler {
 
   @Override
-  protected Result persist(ResourceInstance r, Set<Map<String, Object>> properties) {
+  protected Result persist(ResourceInstance request, Set<Map<String, Object>> setProperties) {
     Result result;
     try {
-      RequestStatus status = getPersistenceManager().update(r, properties);
+      RequestStatus status = getPersistenceManager().update(request, setProperties);
 
       result = createResult(status);
       if (result.isSynchronous()) {
@@ -49,7 +49,7 @@ public class UpdateHandler extends BaseManagementHandler {
     } catch (NoSuchParentResourceException e) {
       result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.NOT_FOUND, e));
     } catch (NoSuchResourceException e) {
-      if (r.isCollectionResource()) {
+      if (request.isCollectionResource()) {
         //todo: what is the correct status code here.  The query didn't match any resource
         //todo: so no resource were updated.  200 may be ok but we would need to return a collection
         //todo: of resources that were updated.

+ 90 - 35
ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseRequest.java

@@ -18,16 +18,15 @@
 
 package org.apache.ambari.server.api.services;
 
+import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.predicate.InvalidQueryException;
 import org.apache.ambari.server.api.predicate.PredicateCompiler;
 import org.apache.ambari.server.api.resources.*;
-import org.apache.ambari.server.api.services.parsers.JsonPropertyParser;
-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.spi.Predicate;
 import org.apache.ambari.server.controller.spi.TemporalInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.UriInfo;
@@ -55,29 +54,58 @@ public abstract class BaseRequest implements Request {
   /**
    * Http Body
    */
-  private String m_body;
+  private RequestBody m_body;
 
+  /**
+   * Query Predicate
+   */
+  private Predicate m_predicate;
 
   /**
    * Associated resource definition
    */
   private ResourceInstance m_resource;
 
+  /**
+   *  Logger instance.
+   */
+  private final static Logger LOG = LoggerFactory.getLogger(Request.class);
+
 
   /**
    * Constructor.
    *
-   * @param headers      http headers
-   * @param body         http body
-   * @param uriInfo      uri information
-   * @param resource     associated resource definition
+   * @param headers           http headers
+   * @param body              http body
+   * @param uriInfo           uri information
+   * @param resource          associated resource definition
+   *
    */
-  public BaseRequest(HttpHeaders headers, String body, UriInfo uriInfo, ResourceInstance resource) {
+  public BaseRequest(HttpHeaders headers, RequestBody body, UriInfo uriInfo, ResourceInstance resource) {
+    m_headers     = headers;
+    m_uriInfo     = uriInfo;
+    m_resource    = resource;
+    m_body        = body;
+  }
+
+  @Override
+  public Result process() {
+    LOG.info("Handling API Request: '" + getURI() + "'");
 
-    m_headers  = headers;
-    m_body     = body;
-    m_uriInfo  = uriInfo;
-    m_resource = resource;
+    Result result;
+    try {
+      parseQueryPredicate();
+      result = getRequestHandler().handleRequest(this);
+    } catch (InvalidQueryException e) {
+      result =  new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST,
+          "Unable to compile query predicate: " + e.getMessage()));
+    }
+
+    if (! result.getStatus().isErrorState()) {
+      getResultPostProcessor().process(result);
+    }
+
+    return result;
   }
 
   @Override
@@ -96,16 +124,12 @@ public abstract class BaseRequest implements Request {
 
   @Override
   public int getAPIVersion() {
-    return 0;
+    return 1;
   }
 
   @Override
-  public Predicate getQueryPredicate() throws InvalidQueryException {
-    String uri     = getURI();
-    int    qsBegin = uri.indexOf("?");
-
-    return (qsBegin == -1) ? null :
-        getPredicateCompiler().compile(uri.substring(qsBegin + 1));
+  public Predicate getQueryPredicate() {
+    return m_predicate;
   }
 
   @Override
@@ -158,29 +182,60 @@ public abstract class BaseRequest implements Request {
 
   @Override
   public String getHttpBody() {
-    return m_body;
-  }
-
-  @Override
-  public Set<Map<String, Object>> getHttpBodyProperties() {
-    return getHttpBodyParser().parse(getHttpBody());
+    return m_body.getBody();
   }
 
   @Override
-  public ResultSerializer getResultSerializer() {
-    return new JsonSerializer();
+  public Set<NamedPropertySet> getHttpBodyProperties() {
+    return m_body.getPropertySets();
   }
 
-  @Override
-  public ResultPostProcessor getResultPostProcessor() {
+  /**
+   * Obtain the result post processor for the request.
+   *
+   * @return the result post processor
+   */
+  protected ResultPostProcessor getResultPostProcessor() {
+    //todo: inject
     return new ResultPostProcessorImpl(this);
   }
 
-  protected RequestBodyParser getHttpBodyParser() {
-    return new JsonPropertyParser();
-  }
-
+  /**
+   * Obtain the predicate compiler which is used to compile the query string into
+   * a predicate.
+   *
+   * @return the predicate compiler
+   */
   protected PredicateCompiler getPredicateCompiler() {
     return new PredicateCompiler();
   }
+
+
+  /**
+   * Parse the query string and compile it into a predicate.
+   * The query string may have already been extracted from the http body.
+   * If the query string didn't exist in the body use the query string in the URL.
+   *
+   * @throws InvalidQueryException  if unable to parse a non-null query string into a predicate
+   */
+  private void parseQueryPredicate() throws InvalidQueryException {
+    String queryString = m_body.getQueryString();
+    if (queryString == null) {
+      String uri     = getURI();
+      int    qsBegin = uri.indexOf("?");
+
+      queryString = (qsBegin == -1) ? null : uri.substring(qsBegin + 1);
+    }
+
+    if (queryString != null) {
+      m_predicate = getPredicateCompiler().compile(queryString);
+    }
+  }
+
+  /**
+   * Obtain the underlying request handler for the request.
+   *
+   * @return  the request handler
+   */
+  protected abstract RequestHandler getRequestHandler();
 }

+ 26 - 33
ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseService.java

@@ -18,15 +18,15 @@
 
 package org.apache.ambari.server.api.services;
 
-import org.apache.ambari.server.api.handlers.RequestHandler;
-import org.apache.ambari.server.api.handlers.RequestHandlerFactory;
 import org.apache.ambari.server.api.resources.ResourceInstance;
 import org.apache.ambari.server.api.resources.ResourceInstanceFactory;
 import org.apache.ambari.server.api.resources.ResourceInstanceFactoryImpl;
+import org.apache.ambari.server.api.services.parsers.BodyParseException;
+import org.apache.ambari.server.api.services.parsers.JsonRequestBodyParser;
+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.spi.Resource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
@@ -44,19 +44,14 @@ public abstract class BaseService {
   private ResourceInstanceFactory m_resourceFactory = new ResourceInstanceFactoryImpl();
 
   /**
-   * Factory for creating request handlers.
+   * Result serializer.
    */
-  private RequestHandlerFactory m_handlerFactory = new RequestHandlerFactory();
+  private ResultSerializer m_serializer = new JsonSerializer();
 
-  /**
-   *  Logger instance.
-   */
-  private final static Logger LOG = LoggerFactory.getLogger(BaseService.class);
 
   /**
    * All requests are funneled through this method so that common logic can be executed.
-   * This consists of creating a {@link Request} instance, invoking the correct {@link RequestHandler} and
-   * applying the proper {@link ResultSerializer} to the result.
+   * Creates a request instance and invokes it's process method.
    *
    * @param headers      http headers
    * @param body         http body
@@ -66,21 +61,22 @@ public abstract class BaseService {
    *
    * @return the response of the operation in serialized form
    */
-  protected Response handleRequest(HttpHeaders headers, String body, UriInfo uriInfo, Request.Type requestType,
-                                   ResourceInstance resource) {
+  protected Response handleRequest(HttpHeaders headers, String body, UriInfo uriInfo,
+                                   Request.Type requestType, ResourceInstance resource) {
 
-    Request request = getRequestFactory().createRequest(
-        headers, body, uriInfo, requestType, resource);
+    Result result;
+    try {
+      RequestBody requestBody = getBodyParser().parse(body);
+      Request request = getRequestFactory().createRequest(
+          headers, requestBody, uriInfo, requestType, resource);
 
-    LOG.info("Handling API Request: '" + request.getURI() + "'");
-
-    Result result = getRequestHandler(request.getRequestType()).handleRequest(request);
-    if (! result.getStatus().isErrorState()) {
-      request.getResultPostProcessor().process(result);
+      result = request.process();
+    } catch (BodyParseException e) {
+      result =  new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST, e.getMessage()));
     }
 
     return Response.status(result.getStatus().getStatusCode()).entity(
-        request.getResultSerializer().serialize(result)).build();
+        getResultSerializer().serialize(result)).build();
   }
 
   /**
@@ -92,17 +88,6 @@ public abstract class BaseService {
     return new RequestFactory();
   }
 
-  /**
-   * Obtain the appropriate RequestHandler for the request.
-   *
-   * @param requestType  the request type
-   *
-   * @return the request handler to invoke
-   */
-  RequestHandler getRequestHandler(Request.Type requestType) {
-    return m_handlerFactory.getRequestHandler(requestType);
-  }
-
   /**
    * Create a resource instance.
    *
@@ -114,4 +99,12 @@ public abstract class BaseService {
   ResourceInstance createResource(Resource.Type type, Map<Resource.Type, String> mapIds) {
     return m_resourceFactory.createResource(type, mapIds);
   }
+
+  protected ResultSerializer getResultSerializer() {
+    return m_serializer;
+  }
+
+  protected RequestBodyParser getBodyParser() {
+    return new JsonRequestBodyParser();
+  }
 }

+ 7 - 1
ambari-server/src/main/java/org/apache/ambari/server/api/services/DeleteRequest.java

@@ -18,6 +18,8 @@
 
 package org.apache.ambari.server.api.services;
 
+import org.apache.ambari.server.api.handlers.DeleteHandler;
+import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.resources.ResourceInstance;
 
 import javax.ws.rs.core.HttpHeaders;
@@ -35,7 +37,7 @@ public class DeleteRequest extends BaseRequest {
    * @param uriInfo     uri information
    * @param resource    associated resource definition
    */
-  public DeleteRequest(HttpHeaders headers, String body, UriInfo uriInfo, ResourceInstance resource) {
+  public DeleteRequest(HttpHeaders headers, RequestBody body, UriInfo uriInfo, ResourceInstance resource) {
     super(headers, body, uriInfo, resource);
   }
 
@@ -44,4 +46,8 @@ public class DeleteRequest extends BaseRequest {
     return Type.DELETE;
   }
 
+  @Override
+  protected RequestHandler getRequestHandler() {
+    return new DeleteHandler();
+  }
 }

+ 8 - 1
ambari-server/src/main/java/org/apache/ambari/server/api/services/GetRequest.java

@@ -18,6 +18,8 @@
 
 package org.apache.ambari.server.api.services;
 
+import org.apache.ambari.server.api.handlers.ReadHandler;
+import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.resources.ResourceInstance;
 
 import javax.ws.rs.core.HttpHeaders;
@@ -35,7 +37,7 @@ public class GetRequest extends BaseRequest {
    * @param uriInfo     uri information
    * @param resource    associated resource definition
    */
-  public GetRequest(HttpHeaders headers, String body, UriInfo uriInfo, ResourceInstance resource) {
+  public GetRequest(HttpHeaders headers, RequestBody body, UriInfo uriInfo, ResourceInstance resource) {
     super(headers, body, uriInfo, resource);
   }
 
@@ -43,4 +45,9 @@ public class GetRequest extends BaseRequest {
   public Type getRequestType() {
     return Type.GET;
   }
+
+  @Override
+  protected RequestHandler getRequestHandler() {
+    return new ReadHandler();
+  }
 }

+ 85 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/NamedPropertySet.java

@@ -0,0 +1,85 @@
+/**
+ * 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.api.services;
+
+import java.util.Map;
+
+/**
+ * A named set of related properties.
+ */
+public class NamedPropertySet {
+  /**
+   * The name of this set of properties.
+   */
+  private String m_name;
+
+  /**
+   * Property name/value pairs.
+   */
+  private Map<String, Object> m_mapProperties;
+
+
+  /**
+   * Constructor.
+   *
+   * @param name           name of this property set
+   * @param mapProperties  associated properties
+   */
+  public NamedPropertySet(String name, Map<String, Object> mapProperties) {
+    m_name = name;
+    m_mapProperties = mapProperties;
+  }
+
+  /**
+   * Obtain the name of this property set.
+   *
+   * @return the name of this property set
+   */
+  public String getName() {
+    return m_name;
+  }
+
+  /**
+   * Obtain the associated properties.
+   *
+   * @return  the associated properties
+   */
+  public Map<String, Object> getProperties() {
+    return m_mapProperties;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    NamedPropertySet that = (NamedPropertySet) o;
+
+    return (m_mapProperties == null ? that.m_mapProperties == null : m_mapProperties.equals(that.m_mapProperties)) &&
+        (m_name == null ? that.m_name == null : m_name.equals(that.m_name));
+
+  }
+
+  @Override
+  public int hashCode() {
+    int result = m_name != null ? m_name.hashCode() : 0;
+    result = 31 * result + (m_mapProperties != null ? m_mapProperties.hashCode() : 0);
+    return result;
+  }
+}

+ 7 - 1
ambari-server/src/main/java/org/apache/ambari/server/api/services/PostRequest.java

@@ -18,6 +18,8 @@
 
 package org.apache.ambari.server.api.services;
 
+import org.apache.ambari.server.api.handlers.CreateHandler;
+import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.resources.ResourceInstance;
 
 import javax.ws.rs.core.HttpHeaders;
@@ -35,7 +37,7 @@ public class PostRequest extends BaseRequest {
    * @param uriInfo     uri information
    * @param resource    associated resource definition
    */
-  public PostRequest(HttpHeaders headers, String body, UriInfo uriInfo, ResourceInstance resource) {
+  public PostRequest(HttpHeaders headers, RequestBody body, UriInfo uriInfo, ResourceInstance resource) {
     super(headers, body, uriInfo, resource);
   }
 
@@ -44,4 +46,8 @@ public class PostRequest extends BaseRequest {
     return Type.POST;
   }
 
+  @Override
+  protected RequestHandler getRequestHandler() {
+    return new CreateHandler();
+  }
 }

+ 7 - 1
ambari-server/src/main/java/org/apache/ambari/server/api/services/PutRequest.java

@@ -18,6 +18,8 @@
 
 package org.apache.ambari.server.api.services;
 
+import org.apache.ambari.server.api.handlers.RequestHandler;
+import org.apache.ambari.server.api.handlers.UpdateHandler;
 import org.apache.ambari.server.api.resources.ResourceInstance;
 
 import javax.ws.rs.core.HttpHeaders;
@@ -35,7 +37,7 @@ public class PutRequest extends BaseRequest {
    * @param uriInfo     uri information
    * @param resource    associated resource definition
    */
-  public PutRequest(HttpHeaders headers, String body, UriInfo uriInfo, ResourceInstance resource) {
+  public PutRequest(HttpHeaders headers, RequestBody body, UriInfo uriInfo, ResourceInstance resource) {
     super(headers, body, uriInfo, resource);
   }
 
@@ -44,4 +46,8 @@ public class PutRequest extends BaseRequest {
     return Type.PUT;
   }
 
+  @Override
+  protected RequestHandler getRequestHandler() {
+    return new UpdateHandler();
+  }
 }

+ 7 - 12
ambari-server/src/main/java/org/apache/ambari/server/api/services/QueryPostRequest.java

@@ -19,12 +19,12 @@
 
 package org.apache.ambari.server.api.services;
 
+import org.apache.ambari.server.api.handlers.QueryCreateHandler;
+import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.resources.ResourceInstance;
 
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.UriInfo;
-import java.util.Map;
-import java.util.Set;
 
 /**
  * Request for creating sub-resources of instances based on a query.
@@ -38,22 +38,17 @@ public class QueryPostRequest extends PostRequest {
    * @param uriInfo      uri information
    * @param resource     associated resource instance
    */
-  public QueryPostRequest(HttpHeaders headers, String body, UriInfo uriInfo, ResourceInstance resource) {
+  public QueryPostRequest(HttpHeaders headers, RequestBody body, UriInfo uriInfo, ResourceInstance resource) {
     super(headers, body, uriInfo, resource);
   }
 
   @Override
-  public Set<Map<String, Object>> getHttpBodyProperties() {
-    String httpBody = getHttpBody();
-    //strip array name
-    int startIdx = httpBody.indexOf("[");
-    int endIdx = httpBody.lastIndexOf("]");
-
-    return getHttpBodyParser().parse(httpBody.substring(startIdx, endIdx + 1));
+  public Type getRequestType() {
+    return Type.QUERY_POST;
   }
 
   @Override
-  public Type getRequestType() {
-    return Type.QUERY_POST;
+  protected RequestHandler getRequestHandler() {
+    return new QueryCreateHandler();
   }
 }

+ 14 - 22
ambari-server/src/main/java/org/apache/ambari/server/api/services/Request.java

@@ -18,10 +18,8 @@
 
 package org.apache.ambari.server.api.services;
 
-import org.apache.ambari.server.api.predicate.InvalidQueryException;
 import org.apache.ambari.server.api.resources.ResourceDefinition;
 import org.apache.ambari.server.api.resources.ResourceInstance;
-import org.apache.ambari.server.api.services.serializers.ResultSerializer;
 import org.apache.ambari.server.controller.spi.Predicate;
 import org.apache.ambari.server.controller.spi.TemporalInfo;
 
@@ -45,6 +43,13 @@ public interface Request {
     QUERY_POST
   }
 
+  /**
+   * Process the request.
+   *
+   * @return the result
+   */
+  public Result process();
+
   /**
    * Obtain the resource definition which corresponds to the resource being operated on by the request.
    * The resource definition provides information about the resource type;
@@ -76,13 +81,12 @@ public interface Request {
 
   /**
    * Obtain the query predicate that was built from the user provided predicate fields in the query string.
-   * If multiple predicates are supplied, then they will be combined using the appropriate grouping predicate
-   * such as 'AND'.
+   * If multiple predicates are supplied, then they will be combined using the appropriate logical grouping
+   * predicate such as 'AND'.
    *
    * @return the user defined predicate
-   * @throws InvalidQueryException if the query syntax is invalid
    */
-  public Predicate getQueryPredicate() throws InvalidQueryException;
+  public Predicate getQueryPredicate();
 
   /**
    * Obtain the partial response fields and associated temporal information which were provided
@@ -92,21 +96,6 @@ public interface Request {
    */
   public Map<String, TemporalInfo> getFields();
 
-  /**
-   * Obtain the result serializer for the request. The default serializer is of type JSON.
-   *
-   * @return the result serializer for the request
-   */
-  public ResultSerializer getResultSerializer();
-
-  /**
-   * Obtain the processor which processes the result returned from the request handler.
-   * The post processor adds additional information such as href fields to the result.
-   *
-   * @return the result processor associated with the request
-   */
-  public ResultPostProcessor getResultPostProcessor();
-
   /**
    * Obtain the http headers associated with the request.
    *
@@ -116,6 +105,9 @@ public interface Request {
 
   /**
    * Obtain the http body associated with the request.
+   * If query or partial response fields exist in the original body,
+   * they are not included in the returned body.  Query and partial
+   * response data are available via the corresponding getters.
    *
    * @return the http body
    */
@@ -126,5 +118,5 @@ public interface Request {
    *
    * @return a set of maps containing the properties contained in the http body
    */
-  public Set<Map<String, Object>> getHttpBodyProperties();
+  public Set<NamedPropertySet> getHttpBodyProperties();
 }

+ 124 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestBody.java

@@ -0,0 +1,124 @@
+/**
+ * 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.api.services;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Represents the http body of the request.
+ */
+public class RequestBody {
+
+  /**
+   * The associated query.
+   */
+  private String m_query;
+
+  /**
+   * The associated partial response fields.
+   */
+  private String m_fields;
+
+  /**
+   * The body properties.
+   */
+  private Set<NamedPropertySet> m_propertySets = new HashSet<NamedPropertySet>();
+
+  /**
+   * The request body.  Query and partial response data is stripped before setting.
+   */
+  private String m_body;
+
+
+  /**
+   * Set the query string.
+   *
+   * @param query the query string from the body
+   */
+  public void setQueryString(String query) {
+    m_query = query;
+  }
+
+  /**
+   * Obtain that query that was specified in the body.
+   *
+   * @return the query from the body or null if no query was present in the body
+   */
+  public String getQueryString() {
+    return m_query;
+  }
+
+  /**
+   * Set the partial response fields from the body.
+   *
+   * @param fields  the partial response fields
+   */
+  public void setPartialResponseFields(String fields) {
+    m_fields = fields;
+  }
+
+  /**
+   * Obtain the partial response fields that were specified in the body.
+   *
+   * @return  the partial response fields or null if not specified in the body
+   */
+  public String getPartialResponseFields() {
+    return m_fields;
+  }
+
+  /**
+   * Add a property set.
+   * A property set is a set of related properties and values.
+   * For example, if the body contained properties for three different resources to
+   * be created, each would be represented as distinct property set.
+   *
+   * @param propertySet  the property set to add
+   */
+  public void addPropertySet(NamedPropertySet propertySet) {
+    m_propertySets.add(propertySet);
+  }
+
+  /**
+   * Obtain all property sets or an empty set if no properties were specified in the body.
+   *
+   * @return  all property sets or an empty set
+   */
+  public Set<NamedPropertySet> getPropertySets() {
+    return m_propertySets;
+  }
+
+  /**
+   * Set the body from the request.
+   *
+   * @param body the request body
+   */
+  public void setBody(String body) {
+    m_body = body;
+  }
+
+  /**
+   * Obtain the request body.
+   *
+   * @return the request body
+   */
+  public String getBody() {
+    return m_body;
+  }
+}

+ 2 - 2
ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestFactory.java

@@ -37,7 +37,7 @@ public class RequestFactory {
    *
    * @return a new Request instance
    */
-  public Request createRequest(HttpHeaders headers, String body, UriInfo uriInfo, Request.Type requestType,
+  public Request createRequest(HttpHeaders headers, RequestBody body, UriInfo uriInfo, Request.Type requestType,
                                ResourceInstance resource) {
     switch (requestType) {
       case GET:
@@ -47,7 +47,7 @@ public class RequestFactory {
       case DELETE:
         return new DeleteRequest(headers, body, uriInfo, resource);
       case POST:
-        return (uriInfo.getQueryParameters().isEmpty() || body == null) ?
+        return ((uriInfo.getQueryParameters().isEmpty() && body.getQueryString() == null) || body == null) ?
             new PostRequest(headers, body, uriInfo, resource) :
             new QueryPostRequest(headers, body, uriInfo, resource);
       default:

+ 43 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/BodyParseException.java

@@ -0,0 +1,43 @@
+/**
+ * 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.api.services.parsers;
+
+/**
+ * Exception indicating that a failure occurred while parsing the request body.
+ */
+public class BodyParseException extends Exception {
+  /**
+   * Create exception by specifying the entire message.
+   *
+   * @param msg  the message to set
+   */
+  public BodyParseException(String msg) {
+    super(msg);
+  }
+
+  /**
+   * Create exception with messaged based on an exception.
+   *
+   * @param e  the exception to base the msg on
+   */
+  public BodyParseException(Exception e) {
+    super("Invalid Request: Malformed Request Body.  An exception occurred parsing the request body: "
+        + e.getMessage());
+  }
+}

+ 0 - 73
ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/JsonPropertyParser.java

@@ -1,73 +0,0 @@
-/**
- * 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.api.services.parsers;
-
-import org.apache.ambari.server.controller.utilities.PropertyHelper;
-import org.codehaus.jackson.JsonNode;
-import org.codehaus.jackson.map.ObjectMapper;
-
-import java.io.IOException;
-import java.util.*;
-
-/**
- * JSON parser which parses a JSON string into a map of properties and values.
- */
-public class JsonPropertyParser implements RequestBodyParser {
-  private Set<Map<String, Object>> m_setProperties = new HashSet<Map<String, Object>>();
-
-
-  @Override
-  public Set<Map<String, Object>> parse(String s) {
-
-    ObjectMapper mapper = new ObjectMapper();
-
-    if (s != null && ! s.isEmpty()) {
-      s = ensureArrayFormat(s);
-      try {
-        JsonNode[] nodes = mapper.readValue(s, JsonNode[].class);
-        for(JsonNode node : nodes) {
-          Map<String, Object> mapProperties = new HashMap<String, Object>();
-          processNode(node, "", mapProperties);
-          m_setProperties.add(mapProperties);
-        }
-      } catch (IOException e) {
-        throw new RuntimeException("Unable to parse json: " + e, e);
-      }
-    }
-    return m_setProperties;
-  }
-
-  private void processNode(JsonNode node, String path, Map<String, Object> mapProperties) {
-    Iterator<String> iter = node.getFieldNames();
-    String name;
-    while (iter.hasNext()) {
-      name = iter.next();
-      JsonNode child = node.get(name);
-      if (child.isContainerNode()) {
-        processNode(child, path.isEmpty() ? name : path + '.' + name, mapProperties);
-      } else {
-        mapProperties.put(PropertyHelper.getPropertyId(path, name), child.asText());
-      }
-    }
-  }
-
-  private String ensureArrayFormat(String s) {
-    return s.startsWith("[") ? s : '[' + s + ']';
-  }
-}

+ 116 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/JsonRequestBodyParser.java

@@ -0,0 +1,116 @@
+/**
+ * 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.api.services.parsers;
+
+import org.apache.ambari.server.api.services.NamedPropertySet;
+import org.apache.ambari.server.api.services.RequestBody;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * JSON parser which parses a JSON string into a map of properties and values.
+ */
+public class JsonRequestBodyParser implements RequestBodyParser {
+  /**
+   *  Logger instance.
+   */
+  private final static Logger LOG = LoggerFactory.getLogger(JsonRequestBodyParser.class);
+
+  private String m_body;
+
+  @Override
+  public RequestBody parse(String s) throws BodyParseException {
+    m_body = s;
+    RequestBody body = new RequestBody();
+
+    if (s != null && s.length() != 0) {
+      s = ensureArrayFormat(s);
+      ObjectMapper mapper = new ObjectMapper();
+      try {
+        JsonNode root = mapper.readTree(s);
+
+        Iterator<JsonNode> iter = root.getElements();
+        while (iter.hasNext()) {
+          Map<String, Object> mapProperties = new HashMap<String, Object>();
+          NamedPropertySet propertySet = new NamedPropertySet("", mapProperties);
+          JsonNode node = iter.next();
+          processNode(node, "", propertySet, body);
+
+          String query = (String) mapProperties.remove(QUERY_FIELD_PATH);
+          if (query != null) {
+            body.setQueryString(query);
+          }
+          if (propertySet.getProperties().size() != 0) {
+            body.addPropertySet(propertySet);
+          }
+        }
+
+        if (body.getPropertySets().size() != 0) {
+          body.setBody(m_body);
+        }
+      } catch (IOException e) {
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Caught exception parsing msg body.");
+          LOG.debug("Message Body: " + s, e);
+        }
+        throw new BodyParseException(e);
+      }
+    }
+    return body;
+  }
+
+  private void processNode(JsonNode node, String path, NamedPropertySet propertySet, RequestBody body) {
+    Iterator<String> iter = node.getFieldNames();
+    String name;
+    while (iter.hasNext()) {
+      name = iter.next();
+      JsonNode child = node.get(name);
+      if (child.isArray()) {
+        //array
+        Iterator<JsonNode> arrayIter = child.getElements();
+        while (arrayIter.hasNext()) {
+          NamedPropertySet arrayPropertySet = new NamedPropertySet(name, new HashMap<String, Object>());
+          processNode(arrayIter.next(), "", arrayPropertySet, body);
+          body.addPropertySet(arrayPropertySet);
+        }
+      } else if (child.isContainerNode()) {
+        // object
+        if (name.equals(BODY_TITLE)) {
+          name = "";
+          m_body = child.toString();
+        }
+        processNode(child, path.isEmpty() ? name : path + '/' + name, propertySet, body);
+      } else {
+        // field
+       propertySet.getProperties().put(PropertyHelper.getPropertyId(
+           path.equals(BODY_TITLE) ? "" : path, name), child.asText());
+      }
+    }
+  }
+
+  private String ensureArrayFormat(String s) {
+    return s.startsWith("[") ? s : '[' + s + ']';
+  }
+}

+ 15 - 6
ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/RequestBodyParser.java

@@ -18,20 +18,29 @@
 
 package org.apache.ambari.server.api.services.parsers;
 
-import java.util.Map;
-import java.util.Set;
+import org.apache.ambari.server.api.services.RequestBody;
 
 /**
  * Parse the provided String into a map of properties and associated values.
  */
 public interface RequestBodyParser {
   /**
-   * Parse the provided string into a map of properties and values.
-   * The key contains both the category hierarchy and the property name.
+   * Path to the query property.
+   */
+  public static final String QUERY_FIELD_PATH = "RequestInfo/query";
+
+  /**
+   * Path to the body object.
+   */
+  public static final String BODY_TITLE = "Body";
+
+  /**
+   * Parse the provided string into a request body which contains all properties in the string.
+   *
    *
    * @param s  the string body to be parsed
    *
-   * @return a set of maps of properties or an empty set if no properties exist
+   * @return RequestBody instance containing all properties in the string
    */
-  public Set<Map<String, Object>> parse(String s);
+  public RequestBody parse(String s) throws BodyParseException;
 }

+ 16 - 4
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HttpProxyPropertyProvider.java

@@ -24,7 +24,10 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
-import org.apache.ambari.server.api.services.parsers.JsonPropertyParser;
+import org.apache.ambari.server.api.services.NamedPropertySet;
+import org.apache.ambari.server.api.services.RequestBody;
+import org.apache.ambari.server.api.services.parsers.BodyParseException;
+import org.apache.ambari.server.api.services.parsers.JsonRequestBodyParser;
 import org.apache.ambari.server.controller.spi.Predicate;
 import org.apache.ambari.server.controller.spi.PropertyProvider;
 import org.apache.ambari.server.controller.spi.Request;
@@ -110,12 +113,21 @@ public class HttpProxyPropertyProvider extends BaseProvider implements PropertyP
     InputStream in = null;
     try {
       in = streamProvider.readFrom(url);
-      r.setProperty(propertyIdToSet, new JsonPropertyParser().parse(IOUtils.toString(in, "UTF-8")));
+      //todo: should not use JsonRequestBodyParser as this is intended only for parsing http bodies.
+      RequestBody body = (new JsonRequestBodyParser().parse(IOUtils.toString(in, "UTF-8")));
+      Set<NamedPropertySet> setNamedProps = body.getPropertySets();
+      Set<Map<String,Object>> setProps = new HashSet<Map<String, Object>>(setNamedProps.size());
+      for (NamedPropertySet ps : setNamedProps) {
+        setProps.add(ps.getProperties());
+      }
+      r.setProperty(propertyIdToSet, setProps);
     }
     catch (IOException ioe) {
+      //todo: should not eat exception
       LOG.error("Error reading HTTP response from " + url);
-    }
-    finally {
+    } catch (BodyParseException e) {
+      LOG.error("Error Parsing Json.", e);
+    } finally {
       if (null != in) {
         try {
           in.close();

+ 9 - 7
ambari-server/src/test/java/org/apache/ambari/server/api/TestSuite.java

@@ -29,7 +29,8 @@ import org.apache.ambari.server.api.predicate.operators.*;
 import org.apache.ambari.server.api.query.QueryImplTest;
 import org.apache.ambari.server.api.resources.ResourceInstanceImplTest;
 import org.apache.ambari.server.api.services.*;
-import org.apache.ambari.server.api.services.parsers.JsonPropertyParserTest;
+import org.apache.ambari.server.api.services.parsers.BodyParseExceptionTest;
+import org.apache.ambari.server.api.services.parsers.JsonRequestBodyParserTest;
 import org.apache.ambari.server.api.services.serializers.JsonSerializerTest;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
@@ -37,12 +38,13 @@ import org.junit.runners.Suite;
 @RunWith(Suite.class)
 @Suite.SuiteClasses({ClusterServiceTest.class, HostServiceTest.class, ServiceServiceTest.class,
     ComponentServiceTest.class, HostComponentServiceTest.class, ReadHandlerTest.class, QueryImplTest.class,
-    JsonPropertyParserTest.class, CreateHandlerTest.class, UpdateHandlerTest.class, DeleteHandlerTest.class,
+    JsonRequestBodyParserTest.class, CreateHandlerTest.class, UpdateHandlerTest.class, DeleteHandlerTest.class,
     PersistenceManagerImplTest.class, GetRequestTest.class, PutRequestTest.class, PostRequestTest.class,
-    DeleteRequestTest.class, JsonSerializerTest.class, QueryCreateHandlerTest.class, ResourceInstanceImplTest.class,
-    QueryLexerTest.class, QueryParserTest.class, IsEmptyOperatorTest.class, InOperatorTest.class,
-    AndOperatorTest.class, OrOperatorTest.class, EqualsOperatorTest.class, GreaterEqualsOperatorTest.class,
-    GreaterOperatorTest.class, LessEqualsOperatorTest.class, LessEqualsOperatorTest.class, NotEqualsOperatorTest.class,
-    NotOperatorTest.class})
+    DeleteRequestTest.class, QueryPostRequestTest.class, JsonSerializerTest.class, QueryCreateHandlerTest.class,
+    ResourceInstanceImplTest.class, QueryLexerTest.class, QueryParserTest.class, IsEmptyOperatorTest.class,
+    InOperatorTest.class,AndOperatorTest.class, OrOperatorTest.class, EqualsOperatorTest.class,
+    GreaterEqualsOperatorTest.class, GreaterOperatorTest.class, LessEqualsOperatorTest.class,
+    LessEqualsOperatorTest.class, NotEqualsOperatorTest.class, NotOperatorTest.class, RequestBodyTest.class,
+    NamedPropertySetTest.class, BodyParseExceptionTest.class})
 public class TestSuite {
 }

+ 63 - 24
ambari-server/src/test/java/org/apache/ambari/server/api/handlers/CreateHandlerTest.java

@@ -18,8 +18,8 @@
 
 package org.apache.ambari.server.api.handlers;
 
-import org.apache.ambari.server.api.predicate.InvalidQueryException;
 import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.api.services.NamedPropertySet;
 import org.apache.ambari.server.api.services.ResultStatus;
 import org.apache.ambari.server.api.services.persistence.PersistenceManager;
 import org.apache.ambari.server.api.services.Request;
@@ -27,7 +27,6 @@ import org.apache.ambari.server.api.services.Result;
 import org.apache.ambari.server.api.util.TreeNode;
 import org.apache.ambari.server.controller.spi.RequestStatus;
 import org.apache.ambari.server.controller.spi.Resource;
-import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.junit.Test;
 
 import java.util.*;
@@ -40,6 +39,57 @@ import static org.junit.Assert.*;
  */
 public class CreateHandlerTest {
 
+  @Test
+  public void testHandleRequest__Synchronous_NoPropsInBody() throws Exception {
+    Request request = createNiceMock(Request.class);
+    ResourceInstance resource = createNiceMock(ResourceInstance.class);
+    PersistenceManager pm = createStrictMock(PersistenceManager.class);
+    RequestStatus status = createNiceMock(RequestStatus.class);
+    Resource resource1 = createNiceMock(Resource.class);
+    Resource resource2 = createNiceMock(Resource.class);
+
+
+    Set<Resource> setResources = new HashSet<Resource>();
+    setResources.add(resource1);
+    setResources.add(resource2);
+
+    // expectations
+    expect(request.getResource()).andReturn(resource).atLeastOnce();
+    expect(request.getQueryPredicate()).andReturn(null).atLeastOnce();
+    expect(request.getHttpBodyProperties()).andReturn(new HashSet<NamedPropertySet>()).atLeastOnce();
+
+    expect(pm.create(eq(resource), eq(new HashSet<Map<String, Object>>()))).andReturn(status);
+    expect(status.getStatus()).andReturn(RequestStatus.Status.Complete);
+    expect(status.getAssociatedResources()).andReturn(setResources);
+    expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
+    expect(resource2.getType()).andReturn(Resource.Type.Cluster).anyTimes();
+
+    replay(request, resource, pm, status, resource1, resource2);
+
+    Result result = new TestCreateHandler(pm).handleRequest(request);
+
+    assertNotNull(result);
+    TreeNode<Resource> tree = result.getResultTree();
+    assertEquals(1, tree.getChildren().size());
+    TreeNode<Resource> resourcesNode = tree.getChild("resources");
+    assertEquals(2, resourcesNode.getChildren().size());
+    boolean foundResource1 = false;
+    boolean foundResource2 = false;
+    for(TreeNode<Resource> child : resourcesNode.getChildren()) {
+      Resource r = child.getObject();
+      if (r == resource1 && ! foundResource1) {
+        foundResource1 = true;
+      } else if (r == resource2 && ! foundResource2) {
+        foundResource2 = true;
+      } else {
+        fail();
+      }
+    }
+
+    assertEquals(ResultStatus.STATUS.CREATED, result.getStatus().getStatus());
+    verify(request, resource, pm, status, resource1, resource2);
+  }
+
   @Test
   public void testHandleRequest__Synchronous() throws Exception {
     Request request = createNiceMock(Request.class);
@@ -49,7 +99,14 @@ public class CreateHandlerTest {
     Resource resource1 = createNiceMock(Resource.class);
     Resource resource2 = createNiceMock(Resource.class);
 
-    Set<Map<String, Object>> setResourceProperties = new HashSet<Map<String, Object>>();
+    Set<NamedPropertySet> setResourceProperties = new HashSet<NamedPropertySet>();
+    Map<String, Object> mapProps = new HashMap<String, Object>();
+    mapProps.put("foo", "bar");
+    NamedPropertySet namedPropSet = new NamedPropertySet("name", mapProps);
+    setResourceProperties.add(namedPropSet);
+
+    Set<Map<String, Object>> setProps = new HashSet<Map<String, Object>>();
+    setProps.add(mapProps);
 
     Set<Resource> setResources = new HashSet<Resource>();
     setResources.add(resource1);
@@ -60,7 +117,7 @@ public class CreateHandlerTest {
     expect(request.getQueryPredicate()).andReturn(null).atLeastOnce();
     expect(request.getHttpBodyProperties()).andReturn(setResourceProperties).atLeastOnce();
 
-    expect(pm.create(resource, setResourceProperties)).andReturn(status);
+    expect(pm.create(eq(resource), eq(setProps))).andReturn(status);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Complete);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
@@ -102,7 +159,6 @@ public class CreateHandlerTest {
     Resource resource2 = createNiceMock(Resource.class);
     Resource requestResource = createNiceMock(Resource.class);
 
-    Set<Map<String, Object>> setResourceProperties = new HashSet<Map<String, Object>>();
 
     Set<Resource> setResources = new HashSet<Resource>();
     setResources.add(resource1);
@@ -110,10 +166,10 @@ public class CreateHandlerTest {
 
     // expectations
     expect(request.getResource()).andReturn(resource);
-    expect(request.getHttpBodyProperties()).andReturn(setResourceProperties);
+    expect(request.getHttpBodyProperties()).andReturn(new HashSet<NamedPropertySet>()).atLeastOnce();
     expect(request.getQueryPredicate()).andReturn(null).atLeastOnce();
 
-    expect(pm.create(resource, setResourceProperties)).andReturn(status);
+    expect(pm.create(eq(resource), eq(new HashSet<Map<String, Object>>()))).andReturn(status);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Accepted);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
@@ -163,21 +219,4 @@ public class CreateHandlerTest {
       return m_testPm;
     }
   }
-
-  @Test
-  public void testHandleRequest__InvalidQuery() throws Exception {
-    Request request = createNiceMock(Request.class);
-    ResourceInstance resource = createNiceMock(ResourceInstance.class);
-    Exception e = new InvalidQueryException("test exception");
-
-    expect(request.getResource()).andReturn(resource);
-    expect(request.getQueryPredicate()).andThrow(e);
-    replay(request, resource);
-
-    Result result = new CreateHandler().handleRequest(request);
-    assertEquals(ResultStatus.STATUS.BAD_REQUEST, result.getStatus().getStatus());
-    assertTrue(result.getStatus().getMessage().contains(e.getMessage()));
-
-    verify(request, resource);
-  }
 }

+ 69 - 25
ambari-server/src/test/java/org/apache/ambari/server/api/handlers/DeleteHandlerTest.java

@@ -18,9 +18,9 @@ package org.apache.ambari.server.api.handlers;
  * limitations under the License.
  */
 
-import org.apache.ambari.server.api.predicate.InvalidQueryException;
 import org.apache.ambari.server.api.query.Query;
 import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.api.services.NamedPropertySet;
 import org.apache.ambari.server.api.services.ResultStatus;
 import org.apache.ambari.server.api.services.persistence.PersistenceManager;
 import org.apache.ambari.server.api.services.Request;
@@ -43,7 +43,7 @@ import static org.junit.Assert.assertEquals;
 public class DeleteHandlerTest {
 
   @Test
-  public void testHandleRequest__Synchronous() throws Exception {
+  public void testHandleRequest__Synchronous_NoPropsInBody() throws Exception {
     Request request = createMock(Request.class);
     ResourceInstance resource = createMock(ResourceInstance.class);
     PersistenceManager pm = createStrictMock(PersistenceManager.class);
@@ -53,12 +53,74 @@ public class DeleteHandlerTest {
     Predicate userPredicate = createNiceMock(Predicate.class);
     Query query = createNiceMock(Query.class);
 
-    Set<Map<String, Object>> setResourceProperties = new HashSet<Map<String, Object>>();
+    Set<Resource> setResources = new HashSet<Resource>();
+    setResources.add(resource1);
+    setResources.add(resource2);
+
+    // expectations
+    expect(request.getResource()).andReturn(resource).atLeastOnce();
+    expect(request.getHttpBodyProperties()).andReturn(new HashSet<NamedPropertySet>()).atLeastOnce();
+
+    expect(request.getQueryPredicate()).andReturn(userPredicate).atLeastOnce();
+    expect(resource.getQuery()).andReturn(query).atLeastOnce();
+    query.setUserPredicate(userPredicate);
+
+    expect(pm.delete(eq(resource), eq(new HashSet<Map<String, Object>>()))).andReturn(status);
+    expect(status.getStatus()).andReturn(RequestStatus.Status.Complete);
+    expect(status.getAssociatedResources()).andReturn(setResources);
+    expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
+    expect(resource2.getType()).andReturn(Resource.Type.Cluster).anyTimes();
+
+    replay(request, resource, pm, status, resource1, resource2, userPredicate, query);
+
+    Result result = new TestDeleteHandler(pm).handleRequest(request);
+
+    assertNotNull(result);
+    TreeNode<Resource> tree = result.getResultTree();
+    assertEquals(1, tree.getChildren().size());
+    TreeNode<Resource> resourcesNode = tree.getChild("resources");
+    assertEquals(2, resourcesNode.getChildren().size());
+    boolean foundResource1 = false;
+    boolean foundResource2 = false;
+    for(TreeNode<Resource> child : resourcesNode.getChildren()) {
+      Resource r = child.getObject();
+      if (r == resource1 && ! foundResource1) {
+        foundResource1 = true;
+      } else if (r == resource2 && ! foundResource2) {
+        foundResource2 = true;
+      } else {
+        fail();
+      }
+    }
+
+    assertEquals(ResultStatus.STATUS.OK, result.getStatus().getStatus());
+    verify(request, resource, pm, status, resource1, resource2, userPredicate, query);
+  }
+
+  @Test
+  public void testHandleRequest__Synchronous() throws Exception {
+    Request request = createMock(Request.class);
+    ResourceInstance resource = createMock(ResourceInstance.class);
+    PersistenceManager pm = createStrictMock(PersistenceManager.class);
+    RequestStatus status = createMock(RequestStatus.class);
+    Resource resource1 = createMock(Resource.class);
+    Resource resource2 = createMock(Resource.class);
+    Predicate userPredicate = createNiceMock(Predicate.class);
+    Query query = createNiceMock(Query.class);
 
     Set<Resource> setResources = new HashSet<Resource>();
     setResources.add(resource1);
     setResources.add(resource2);
 
+    Set<NamedPropertySet> setResourceProperties = new HashSet<NamedPropertySet>();
+    Map<String, Object> mapProps = new HashMap<String, Object>();
+    mapProps.put("foo", "bar");
+    NamedPropertySet namedPropSet = new NamedPropertySet("name", mapProps);
+    setResourceProperties.add(namedPropSet);
+
+    Set<Map<String, Object>> setProps = new HashSet<Map<String, Object>>();
+    setProps.add(mapProps);
+
     // expectations
     expect(request.getResource()).andReturn(resource).atLeastOnce();
     expect(request.getHttpBodyProperties()).andReturn(setResourceProperties).atLeastOnce();
@@ -67,7 +129,7 @@ public class DeleteHandlerTest {
     expect(resource.getQuery()).andReturn(query).atLeastOnce();
     query.setUserPredicate(userPredicate);
 
-    expect(pm.delete(resource, setResourceProperties)).andReturn(status);
+    expect(pm.delete(eq(resource), eq(setProps))).andReturn(status);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Complete);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
@@ -99,6 +161,7 @@ public class DeleteHandlerTest {
     verify(request, resource, pm, status, resource1, resource2, userPredicate, query);
   }
 
+
   @Test
   public void testHandleRequest__Asynchronous() throws Exception {
     Request request = createMock(Request.class);
@@ -109,19 +172,17 @@ public class DeleteHandlerTest {
     Resource resource2 = createMock(Resource.class);
     Resource requestResource = createMock(Resource.class);
 
-    Set<Map<String, Object>> setResourceProperties = new HashSet<Map<String, Object>>();
-
     Set<Resource> setResources = new HashSet<Resource>();
     setResources.add(resource1);
     setResources.add(resource2);
 
     // expectations
     expect(request.getResource()).andReturn(resource);
-    expect(request.getHttpBodyProperties()).andReturn(setResourceProperties);
+    expect(request.getHttpBodyProperties()).andReturn(new HashSet<NamedPropertySet>()).atLeastOnce();
     // test delete with no user predicate
     expect(request.getQueryPredicate()).andReturn(null).atLeastOnce();
 
-    expect(pm.delete(resource, setResourceProperties)).andReturn(status);
+    expect(pm.delete(eq(resource), eq(new HashSet<Map<String, Object>>()))).andReturn(status);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Accepted);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
@@ -171,21 +232,4 @@ public class DeleteHandlerTest {
       return m_testPm;
     }
   }
-
-  @Test
-  public void testHandleRequest__InvalidQuery() throws Exception {
-    Request request = createNiceMock(Request.class);
-    ResourceInstance resource = createNiceMock(ResourceInstance.class);
-    Exception e = new InvalidQueryException("test exception");
-
-    expect(request.getResource()).andReturn(resource);
-    expect(request.getQueryPredicate()).andThrow(e);
-    replay(request, resource);
-
-    Result result = new DeleteHandler().handleRequest(request);
-    assertEquals(ResultStatus.STATUS.BAD_REQUEST, result.getStatus().getStatus());
-    assertTrue(result.getStatus().getMessage().contains(e.getMessage()));
-
-    verify(request, resource);
-  }
 }

+ 378 - 43
ambari-server/src/test/java/org/apache/ambari/server/api/handlers/QueryCreateHandlerTest.java

@@ -19,11 +19,11 @@
 
 package org.apache.ambari.server.api.handlers;
 
-import org.apache.ambari.server.api.predicate.InvalidQueryException;
 import org.apache.ambari.server.api.query.Query;
 import org.apache.ambari.server.api.resources.ResourceDefinition;
 import org.apache.ambari.server.api.resources.ResourceInstance;
 import org.apache.ambari.server.api.resources.ResourceInstanceFactory;
+import org.apache.ambari.server.api.services.NamedPropertySet;
 import org.apache.ambari.server.api.services.ResultStatus;
 import org.apache.ambari.server.api.services.persistence.PersistenceManager;
 import org.apache.ambari.server.api.services.Request;
@@ -71,25 +71,27 @@ public class QueryCreateHandlerTest {
     RequestHandler readHandler = createStrictMock(RequestHandler.class);
     ResultStatus resultStatus = createNiceMock(ResultStatus.class);
 
-    String httpBody = "{" +
-        "\"components\" : [" +
-        "{\"ServiceComponentInfo\" : {" +
-        "        \"component_name\" : \"SECONDARY_NAMENODE\"" +
-        "      }" +
-        "}," +
-        "{\"ServiceComponentInfo\" : {" +
-        "        \"component_name\" : \"HDFS_CLIENT\"" +
-        "      }" +
-        "}" +
-        "] }";
+    //  test request body
+    //    {
+    //      "components" : [
+    //        { "ServiceComponentInfo" : {
+    //            "component_name" : "SECONDARY_NAMENODE"
+    //          }
+    //        },
+    //        { "ServiceComponentInfo" : {
+    //            "component_name" : "HDFS_CLIENT"
+    //          }
+    //        }
+    //      ]
+    //   }
 
     Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
 
-    Set<Map<String, Object>> setRequestProps = new HashSet<Map<String, Object>>();
-    setRequestProps.add(Collections.<String, Object>singletonMap(
-        PropertyHelper.getPropertyId("ServiceComponentInfo", "component_name"), "SECONDARY_NAMENODE"));
-    setRequestProps.add(Collections.<String, Object>singletonMap(
-        PropertyHelper.getPropertyId("ServiceComponentInfo", "component_name"), "HDFS_CLIENT"));
+    Set<NamedPropertySet> setRequestProps = new HashSet<NamedPropertySet>();
+    setRequestProps.add(new NamedPropertySet("components", Collections.<String, Object>singletonMap(
+        PropertyHelper.getPropertyId("ServiceComponentInfo", "component_name"), "SECONDARY_NAMENODE")));
+    setRequestProps.add(new NamedPropertySet("components", Collections.<String, Object>singletonMap(
+        PropertyHelper.getPropertyId("ServiceComponentInfo", "component_name"), "HDFS_CLIENT")));
 
     Set<Map<String, Object>> setCreateProps = new HashSet<Map<String, Object>>();
     Map<String, Object> map1 = new HashMap<String, Object>();
@@ -127,7 +129,6 @@ public class QueryCreateHandlerTest {
     expect(result.getResultTree()).andReturn(resultTree);
 
     expect(request.getResource()).andReturn(resourceInstance).anyTimes();
-    expect(request.getHttpBody()).andReturn(httpBody).anyTimes();
     expect(request.getHttpBodyProperties()).andReturn(setRequestProps).anyTimes();
 
     expect(resourceInstance.getResourceDefinition()).andReturn(resourceDefinition).anyTimes();
@@ -190,6 +191,365 @@ public class QueryCreateHandlerTest {
         readHandler, resultStatus);
   }
 
+  @Test
+  public void tesHandleRequest_NoSubResourceNameSpecified() {
+    Request request = createNiceMock(Request.class);
+    ResourceInstance resourceInstance = createNiceMock(ResourceInstance.class);
+    ResourceDefinition resourceDefinition = createNiceMock(ResourceDefinition.class);
+    Query query = createNiceMock(Query.class);
+    Predicate predicate = createNiceMock(Predicate.class);
+    Result result = createNiceMock(Result.class);
+    ResourceInstance subResource = createNiceMock(ResourceInstance.class);
+    ResourceDefinition subResourceDefinition = createNiceMock(ResourceDefinition.class);
+    ClusterController controller = createNiceMock(ClusterController.class);
+    Schema serviceSchema = createNiceMock(Schema.class);
+    Schema componentSchema = createNiceMock(Schema.class);
+    String resourceKeyProperty = "resourceKeyProperty";
+    Resource resource1 = createNiceMock(Resource.class);
+    Resource resource2 = createNiceMock(Resource.class);
+    PersistenceManager pm = createNiceMock(PersistenceManager.class);
+    ResourceInstance createResource = createNiceMock(ResourceInstance.class);
+    RequestStatus status = createNiceMock(RequestStatus.class);
+    Resource statusResource1 = createNiceMock(Resource.class);
+    Resource statusResource2 = createNiceMock(Resource.class);
+    RequestHandler readHandler = createStrictMock(RequestHandler.class);
+    ResultStatus queryResultStatus = createNiceMock(ResultStatus.class);
+
+    //  test request body.  Missing sub-resource name.
+    //    {
+    //      { "ServiceComponentInfo" : {
+    //          "component_name" : "SECONDARY_NAMENODE"
+    //        }
+    //      },
+    //      { "ServiceComponentInfo" : {
+    //          "component_name" : "HDFS_CLIENT"
+    //        }
+    //      }
+    //   }
+
+    Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
+
+    Set<NamedPropertySet> setRequestProps = new HashSet<NamedPropertySet>();
+    setRequestProps.add(new NamedPropertySet("", Collections.<String, Object>singletonMap(
+        PropertyHelper.getPropertyId("ServiceComponentInfo", "component_name"), "SECONDARY_NAMENODE")));
+    setRequestProps.add(new NamedPropertySet("", Collections.<String, Object>singletonMap(
+        PropertyHelper.getPropertyId("ServiceComponentInfo", "component_name"), "HDFS_CLIENT")));
+
+    TreeNode<Resource> resultTree = new TreeNodeImpl<Resource>(null, null, "result");
+    resultTree.addChild(resource1, "resource1");
+    resultTree.addChild(resource2, "resource2");
+
+    //expectations
+    expect(readHandler.handleRequest(request)).andReturn(result);
+    expect(result.getStatus()).andReturn(queryResultStatus).anyTimes();
+    expect(queryResultStatus.isErrorState()).andReturn(false);
+    expect(result.getResultTree()).andReturn(resultTree);
+
+    expect(request.getResource()).andReturn(resourceInstance).anyTimes();
+    expect(request.getHttpBodyProperties()).andReturn(setRequestProps).anyTimes();
+
+    expect(resourceInstance.getResourceDefinition()).andReturn(resourceDefinition).anyTimes();
+    expect(resourceInstance.getIds()).andReturn(mapIds).anyTimes();
+
+    expect(resourceDefinition.getType()).andReturn(Resource.Type.Service).anyTimes();
+    expect(controller.getSchema(Resource.Type.Service)).andReturn(serviceSchema).anyTimes();
+    expect(result.getResultTree()).andReturn(resultTree).anyTimes();
+    expect(resource1.getPropertyValue(resourceKeyProperty)).andReturn("id1").anyTimes();
+    expect(resource2.getPropertyValue(resourceKeyProperty)).andReturn("id2").anyTimes();
+
+    replay(request, resourceInstance, resourceDefinition, query, predicate, result, subResource,
+        subResourceDefinition, controller, serviceSchema, componentSchema, resource1, resource2,
+        pm, createResource, status, statusResource1, statusResource2,
+        readHandler, queryResultStatus);
+
+    //test
+    Result testResult = new TestQueryCreateHandler(null, controller, pm, readHandler).
+        handleRequest(request);
+
+    ResultStatus resultStatus = testResult.getStatus();
+    assertEquals(ResultStatus.STATUS.BAD_REQUEST, resultStatus.getStatus());
+    assertEquals("Invalid Request: A sub-resource name must be supplied.", resultStatus.getMessage());
+
+    verify(request, resourceInstance, resourceDefinition, query, predicate, result, subResource,
+        subResourceDefinition, controller, serviceSchema, componentSchema, resource1, resource2,
+        pm, createResource, status, statusResource1, statusResource2,
+        readHandler, queryResultStatus);
+
+  }
+
+  @Test
+  public void tesHandleRequest_InvalidSubResSpecified() {
+    Request request = createNiceMock(Request.class);
+    ResourceInstance resourceInstance = createNiceMock(ResourceInstance.class);
+    ResourceDefinition resourceDefinition = createNiceMock(ResourceDefinition.class);
+    Query query = createNiceMock(Query.class);
+    Predicate predicate = createNiceMock(Predicate.class);
+    Result result = createNiceMock(Result.class);
+    ResourceInstance subResource = createNiceMock(ResourceInstance.class);
+    ResourceDefinition subResourceDefinition = createNiceMock(ResourceDefinition.class);
+    ClusterController controller = createNiceMock(ClusterController.class);
+    Schema serviceSchema = createNiceMock(Schema.class);
+    Schema componentSchema = createNiceMock(Schema.class);
+    String resourceKeyProperty = "resourceKeyProperty";
+    Resource resource1 = createNiceMock(Resource.class);
+    Resource resource2 = createNiceMock(Resource.class);
+    PersistenceManager pm = createNiceMock(PersistenceManager.class);
+    ResourceInstance createResource = createNiceMock(ResourceInstance.class);
+    RequestStatus status = createNiceMock(RequestStatus.class);
+    Resource statusResource1 = createNiceMock(Resource.class);
+    Resource statusResource2 = createNiceMock(Resource.class);
+    RequestHandler readHandler = createStrictMock(RequestHandler.class);
+    ResultStatus queryResultStatus = createNiceMock(ResultStatus.class);
+
+    //  test request body
+    //    {
+    //      "INVALID" : [
+    //        { "ServiceComponentInfo" : {
+    //            "component_name" : "SECONDARY_NAMENODE"
+    //          }
+    //        },
+    //        { "ServiceComponentInfo" : {
+    //            "component_name" : "HDFS_CLIENT"
+    //          }
+    //        }
+    //      ]
+    //   }
+
+    Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
+
+    Set<NamedPropertySet> setRequestProps = new HashSet<NamedPropertySet>();
+    setRequestProps.add(new NamedPropertySet("INVALID", Collections.<String, Object>singletonMap(
+        PropertyHelper.getPropertyId("ServiceComponentInfo", "component_name"), "SECONDARY_NAMENODE")));
+    setRequestProps.add(new NamedPropertySet("INVALID", Collections.<String, Object>singletonMap(
+        PropertyHelper.getPropertyId("ServiceComponentInfo", "component_name"), "HDFS_CLIENT")));
+
+    Map<String, ResourceInstance> mapSubResources = new HashMap<String, ResourceInstance>();
+    mapSubResources.put("components", subResource);
+
+    TreeNode<Resource> resultTree = new TreeNodeImpl<Resource>(null, null, "result");
+    resultTree.addChild(resource1, "resource1");
+    resultTree.addChild(resource2, "resource2");
+
+    //expectations
+    expect(readHandler.handleRequest(request)).andReturn(result);
+    expect(result.getStatus()).andReturn(queryResultStatus).anyTimes();
+    expect(queryResultStatus.isErrorState()).andReturn(false);
+    expect(result.getResultTree()).andReturn(resultTree);
+
+    expect(request.getResource()).andReturn(resourceInstance).anyTimes();
+    expect(request.getHttpBodyProperties()).andReturn(setRequestProps).anyTimes();
+
+    expect(resourceInstance.getResourceDefinition()).andReturn(resourceDefinition).anyTimes();
+    expect(resourceInstance.getIds()).andReturn(mapIds).anyTimes();
+    expect(resourceInstance.getSubResources()).andReturn(mapSubResources).anyTimes();
+
+    expect(resourceDefinition.getType()).andReturn(Resource.Type.Service).anyTimes();
+    expect(controller.getSchema(Resource.Type.Service)).andReturn(serviceSchema).anyTimes();
+    expect(result.getResultTree()).andReturn(resultTree).anyTimes();
+    expect(resource1.getPropertyValue(resourceKeyProperty)).andReturn("id1").anyTimes();
+    expect(resource2.getPropertyValue(resourceKeyProperty)).andReturn("id2").anyTimes();
+
+    replay(request, resourceInstance, resourceDefinition, query, predicate, result, subResource,
+        subResourceDefinition, controller, serviceSchema, componentSchema, resource1, resource2,
+        pm, createResource, status, statusResource1, statusResource2,
+        readHandler, queryResultStatus);
+
+    //test
+    Result testResult = new TestQueryCreateHandler(null, controller, pm, readHandler).
+        handleRequest(request);
+
+    ResultStatus resultStatus = testResult.getStatus();
+    assertEquals(ResultStatus.STATUS.BAD_REQUEST, resultStatus.getStatus());
+    assertEquals("Invalid Request: The specified sub-resource name is not valid: 'INVALID'.", resultStatus.getMessage());
+
+    verify(request, resourceInstance, resourceDefinition, query, predicate, result, subResource,
+        subResourceDefinition, controller, serviceSchema, componentSchema, resource1, resource2,
+        pm, createResource, status, statusResource1, statusResource2,
+        readHandler, queryResultStatus);
+
+  }
+
+  @Test
+  public void tesHandleRequest_NoSubResourcesSpecified() {
+    Request request = createNiceMock(Request.class);
+    ResourceInstance resourceInstance = createNiceMock(ResourceInstance.class);
+    ResourceDefinition resourceDefinition = createNiceMock(ResourceDefinition.class);
+    Query query = createNiceMock(Query.class);
+    Predicate predicate = createNiceMock(Predicate.class);
+    Result result = createNiceMock(Result.class);
+    ResourceInstance subResource = createNiceMock(ResourceInstance.class);
+    ResourceDefinition subResourceDefinition = createNiceMock(ResourceDefinition.class);
+    ClusterController controller = createNiceMock(ClusterController.class);
+    Schema serviceSchema = createNiceMock(Schema.class);
+    Schema componentSchema = createNiceMock(Schema.class);
+    String resourceKeyProperty = "resourceKeyProperty";
+    Resource resource1 = createNiceMock(Resource.class);
+    Resource resource2 = createNiceMock(Resource.class);
+    PersistenceManager pm = createNiceMock(PersistenceManager.class);
+    ResourceInstance createResource = createNiceMock(ResourceInstance.class);
+    RequestStatus status = createNiceMock(RequestStatus.class);
+    Resource statusResource1 = createNiceMock(Resource.class);
+    Resource statusResource2 = createNiceMock(Resource.class);
+    RequestHandler readHandler = createStrictMock(RequestHandler.class);
+    ResultStatus queryResultStatus = createNiceMock(ResultStatus.class);
+
+    Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
+
+    Set<NamedPropertySet> setRequestProps = new HashSet<NamedPropertySet>();
+    // no body specified so no props
+
+    TreeNode<Resource> resultTree = new TreeNodeImpl<Resource>(null, null, "result");
+    resultTree.addChild(resource1, "resource1");
+    resultTree.addChild(resource2, "resource2");
+
+    //expectations
+    expect(readHandler.handleRequest(request)).andReturn(result);
+    expect(result.getStatus()).andReturn(queryResultStatus).anyTimes();
+    expect(queryResultStatus.isErrorState()).andReturn(false);
+    expect(result.getResultTree()).andReturn(resultTree);
+
+    expect(request.getResource()).andReturn(resourceInstance).anyTimes();
+    expect(request.getHttpBodyProperties()).andReturn(setRequestProps).anyTimes();
+
+    expect(resourceInstance.getResourceDefinition()).andReturn(resourceDefinition).anyTimes();
+    expect(resourceInstance.getIds()).andReturn(mapIds).anyTimes();
+
+    expect(resourceDefinition.getType()).andReturn(Resource.Type.Service).anyTimes();
+    expect(controller.getSchema(Resource.Type.Service)).andReturn(serviceSchema).anyTimes();
+    expect(result.getResultTree()).andReturn(resultTree).anyTimes();
+    expect(resource1.getPropertyValue(resourceKeyProperty)).andReturn("id1").anyTimes();
+    expect(resource2.getPropertyValue(resourceKeyProperty)).andReturn("id2").anyTimes();
+
+    replay(request, resourceInstance, resourceDefinition, query, predicate, result, subResource,
+        subResourceDefinition, controller, serviceSchema, componentSchema, resource1, resource2,
+        pm, createResource, status, statusResource1, statusResource2,
+        readHandler, queryResultStatus);
+
+    //test
+    Result testResult = new TestQueryCreateHandler(null, controller, pm, readHandler).
+        handleRequest(request);
+
+    ResultStatus resultStatus = testResult.getStatus();
+    assertEquals(ResultStatus.STATUS.BAD_REQUEST, resultStatus.getStatus());
+    assertEquals("Invalid Request: A minimum of one sub-resource must be specified for creation.", resultStatus.getMessage());
+
+    verify(request, resourceInstance, resourceDefinition, query, predicate, result, subResource,
+        subResourceDefinition, controller, serviceSchema, componentSchema, resource1, resource2,
+        pm, createResource, status, statusResource1, statusResource2,
+        readHandler, queryResultStatus);
+
+  }
+
+  //todo: this is currently not supported.  We may wish to add this support in the future.
+  @Test
+  public void testHandleRequest_MultipleSubResources() throws Exception {
+    Request request = createNiceMock(Request.class);
+    ResourceInstance resourceInstance = createNiceMock(ResourceInstance.class);
+    ResourceDefinition resourceDefinition = createNiceMock(ResourceDefinition.class);
+    ResourceInstanceFactory resourceInstanceFactory = createNiceMock(ResourceInstanceFactory.class);
+    Query query = createNiceMock(Query.class);
+    Predicate predicate = createNiceMock(Predicate.class);
+    Result result = createNiceMock(Result.class);
+    ResourceInstance subResource1 = createNiceMock(ResourceInstance.class);
+    ResourceInstance subResource2 = createNiceMock(ResourceInstance.class);
+    ResourceDefinition subResourceDefinition1 = createNiceMock(ResourceDefinition.class);
+    ResourceDefinition subResourceDefinition2 = createNiceMock(ResourceDefinition.class);
+    ClusterController controller = createNiceMock(ClusterController.class);
+    Schema serviceSchema = createNiceMock(Schema.class);
+    Schema subResSchema1 = createNiceMock(Schema.class);
+    Schema subResSchema2 = createNiceMock(Schema.class);
+    String resourceKeyProperty = "resourceKeyProperty";
+    String createKeyProperty = "createKeyProperty";
+    Resource resource1 = createNiceMock(Resource.class);
+    Resource resource2 = createNiceMock(Resource.class);
+    PersistenceManager pm = createNiceMock(PersistenceManager.class);
+    ResourceInstance createResource = createNiceMock(ResourceInstance.class);
+    RequestStatus status = createNiceMock(RequestStatus.class);
+    Resource statusResource1 = createNiceMock(Resource.class);
+    Resource statusResource2 = createNiceMock(Resource.class);
+    RequestHandler readHandler = createStrictMock(RequestHandler.class);
+    ResultStatus queryResultStatus = createNiceMock(ResultStatus.class);
+
+    //  test request body.  Multiple valid sub-resource types
+    //  {
+    //    "foo" : [
+    //      { "prop" : "val" }
+    //    ],
+    //    "bar" : [
+    //      { "prop" : "val" }
+    //    ]
+    //  }
+
+    Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
+
+    Set<NamedPropertySet> setRequestProps = new HashSet<NamedPropertySet>();
+    setRequestProps.add(new NamedPropertySet("foo", Collections.<String, Object>singletonMap(
+        PropertyHelper.getPropertyId("ServiceComponentInfo", "component_name"), "SECONDARY_NAMENODE")));
+    setRequestProps.add(new NamedPropertySet("bar", Collections.<String, Object>singletonMap(
+        PropertyHelper.getPropertyId("ServiceComponentInfo", "component_name"), "HDFS_CLIENT")));
+
+    Map<String, ResourceInstance> mapSubResources = new HashMap<String, ResourceInstance>();
+    mapSubResources.put("foo", subResource1);
+    mapSubResources.put("bar", subResource2);
+
+    TreeNode<Resource> resultTree = new TreeNodeImpl<Resource>(null, null, "result");
+    resultTree.addChild(resource1, "resource1");
+    resultTree.addChild(resource2, "resource2");
+
+    //expectations
+    expect(readHandler.handleRequest(request)).andReturn(result);
+    expect(result.getStatus()).andReturn(queryResultStatus).anyTimes();
+    expect(queryResultStatus.isErrorState()).andReturn(false);
+    expect(result.getResultTree()).andReturn(resultTree);
+
+    expect(request.getResource()).andReturn(resourceInstance).anyTimes();
+    expect(request.getHttpBodyProperties()).andReturn(setRequestProps).anyTimes();
+
+    expect(resourceInstance.getResourceDefinition()).andReturn(resourceDefinition).anyTimes();
+    expect(resourceInstance.getIds()).andReturn(mapIds).anyTimes();
+    expect(resourceInstance.getSubResources()).andReturn(mapSubResources).anyTimes();
+
+    expect(resourceDefinition.getType()).andReturn(Resource.Type.Service).anyTimes();
+
+    expect(subResource1.getResourceDefinition()).andReturn(subResourceDefinition1).anyTimes();
+    expect(subResourceDefinition1.getType()).andReturn(Resource.Type.Component).anyTimes();
+    expect(subResource2.getResourceDefinition()).andReturn(subResourceDefinition2).anyTimes();
+    expect(subResourceDefinition2.getType()).andReturn(Resource.Type.HostComponent).anyTimes();
+
+    expect(controller.getSchema(Resource.Type.Service)).andReturn(serviceSchema).anyTimes();
+    expect(controller.getSchema(Resource.Type.Component)).andReturn(subResSchema1).anyTimes();
+    expect(controller.getSchema(Resource.Type.HostComponent)).andReturn(subResSchema2).anyTimes();
+
+    expect(serviceSchema.getKeyPropertyId(Resource.Type.Service)).andReturn(resourceKeyProperty).anyTimes();
+    expect(subResSchema1.getKeyPropertyId(Resource.Type.Service)).andReturn(createKeyProperty).anyTimes();
+    expect(subResSchema2.getKeyPropertyId(Resource.Type.Service)).andReturn(createKeyProperty).anyTimes();
+
+    expect(result.getResultTree()).andReturn(resultTree).anyTimes();
+    expect(resource1.getPropertyValue(resourceKeyProperty)).andReturn("id1").anyTimes();
+    expect(resource2.getPropertyValue(resourceKeyProperty)).andReturn("id2").anyTimes();
+
+
+    replay(request, resourceInstance, resourceDefinition, query, predicate, result, subResource1, subResource2,
+        subResourceDefinition1, subResourceDefinition2, controller, serviceSchema, subResSchema1, subResSchema2,
+        resource1, resource2, pm, resourceInstanceFactory, createResource, status, statusResource1, statusResource2,
+        readHandler, queryResultStatus);
+
+    //test
+    Result testResult = new TestQueryCreateHandler(resourceInstanceFactory, controller, pm, readHandler).
+        handleRequest(request);
+
+    ResultStatus resultStatus = testResult.getStatus();
+    assertEquals(ResultStatus.STATUS.BAD_REQUEST, resultStatus.getStatus());
+    assertEquals("Invalid Request: Multiple sub-resource types may not be created in the same request.",
+        resultStatus.getMessage());
+
+
+    verify(request, resourceInstance, resourceDefinition, query, predicate, result, subResource1, subResource2,
+        subResourceDefinition1, subResourceDefinition2, controller, serviceSchema, subResSchema1, subResSchema2,
+        resource1, resource2, pm, resourceInstanceFactory, createResource, status, statusResource1, statusResource2,
+        readHandler, queryResultStatus);
+  }
+
   static class TestQueryCreateHandler extends QueryCreateHandler {
     private ResourceInstanceFactory m_resourceFactory;
     private ClusterController m_controller;
@@ -224,29 +584,4 @@ public class QueryCreateHandlerTest {
       return m_testReadHandler;
     }
   }
-
-  @Test
-  public void testHandleRequest__InvalidQueryException() throws Exception {
-    Request request = createStrictMock(Request.class);
-    ResourceInstance resource = createStrictMock(ResourceInstance.class);
-    Query query = createMock(Query.class);
-    InvalidQueryException exception = new InvalidQueryException("test");
-
-    expect(request.getResource()).andReturn(resource);
-    expect(resource.getQuery()).andReturn(query);
-
-    expect(request.getFields()).andReturn(Collections.<String, TemporalInfo>emptyMap());
-
-    expect(request.getQueryPredicate()).andThrow(exception);
-    replay(request, resource, query);
-
-    //test
-    QueryCreateHandler handler = new QueryCreateHandler();
-    Result result = handler.handleRequest(request);
-
-    assertEquals(ResultStatus.STATUS.BAD_REQUEST, result.getStatus().getStatus());
-    assertTrue(result.getStatus().getMessage().contains(exception.getMessage()));
-    verify(request, resource, query);
-  }
-
 }

+ 1 - 27
ambari-server/src/test/java/org/apache/ambari/server/api/handlers/ReadHandlerTest.java

@@ -18,7 +18,6 @@
 
 package org.apache.ambari.server.api.handlers;
 
-import org.apache.ambari.server.api.predicate.InvalidQueryException;
 import org.apache.ambari.server.api.query.Query;
 import org.apache.ambari.server.api.resources.ResourceInstance;
 import org.apache.ambari.server.api.services.Request;
@@ -36,7 +35,6 @@ import java.util.Map;
 import static org.easymock.EasyMock.*;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
 
 /**
  * Unit tests for ReadHandler.
@@ -248,31 +246,7 @@ public class ReadHandlerTest {
     verify(request, resource, query);
   }
 
-  @Test
-  public void testHandleRequest__InvalidQueryException() throws Exception {
-    Request request = createStrictMock(Request.class);
-    ResourceInstance resource = createStrictMock(ResourceInstance.class);
-    Query query = createMock(Query.class);
-    InvalidQueryException exception = new InvalidQueryException("test");
-
-    expect(request.getResource()).andReturn(resource);
-    expect(resource.getQuery()).andReturn(query);
-
-    expect(request.getFields()).andReturn(Collections.<String, TemporalInfo>emptyMap());
-
-    expect(request.getQueryPredicate()).andThrow(exception);
-    replay(request, resource, query);
-
-    //test
-    ReadHandler handler = new ReadHandler();
-    Result result = handler.handleRequest(request);
-
-    assertEquals(ResultStatus.STATUS.BAD_REQUEST, result.getStatus().getStatus());
-    assertTrue(result.getStatus().getMessage().contains(exception.getMessage()));
-    verify(request, resource, query);
-  }
-
-  //todo: reverted to just logging the exception and re-throwing it
+//todo: reverted to just logging the exception and re-throwing it
 //  @Test
 //  public void testHandleRequest__RuntimeException() throws Exception {
 //    Request request = createStrictMock(Request.class);

+ 68 - 24
ambari-server/src/test/java/org/apache/ambari/server/api/handlers/UpdateHandlerTest.java

@@ -18,9 +18,9 @@
 
 package org.apache.ambari.server.api.handlers;
 
-import org.apache.ambari.server.api.predicate.InvalidQueryException;
 import org.apache.ambari.server.api.query.Query;
 import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.api.services.NamedPropertySet;
 import org.apache.ambari.server.api.services.ResultStatus;
 import org.apache.ambari.server.api.services.persistence.PersistenceManager;
 import org.apache.ambari.server.api.services.Request;
@@ -42,7 +42,7 @@ import static org.junit.Assert.*;
 public class UpdateHandlerTest {
 
   @Test
-  public void testHandleRequest__Synchronous() throws Exception {
+  public void testHandleRequest__Synchronous_NoPropsInBody() throws Exception {
     Request request = createMock(Request.class);
     ResourceInstance resource = createMock(ResourceInstance.class);
     PersistenceManager pm = createStrictMock(PersistenceManager.class);
@@ -52,12 +52,74 @@ public class UpdateHandlerTest {
     Predicate userPredicate = createNiceMock(Predicate.class);
     Query query = createNiceMock(Query.class);
 
-    Set<Map<String, Object>> setResourceProperties = new HashSet<Map<String, Object>>();
+    Set<Resource> setResources = new HashSet<Resource>();
+    setResources.add(resource1);
+    setResources.add(resource2);
+
+    // expectations
+    expect(request.getResource()).andReturn(resource).anyTimes();
+    expect(request.getHttpBodyProperties()).andReturn(new HashSet<NamedPropertySet>()).anyTimes();
+    expect(request.getQueryPredicate()).andReturn(userPredicate).atLeastOnce();
+
+    expect(resource.getQuery()).andReturn(query).atLeastOnce();
+    query.setUserPredicate(userPredicate);
+
+    expect(pm.update(eq(resource), eq(new HashSet<Map<String, Object>>()))).andReturn(status);
+    expect(status.getStatus()).andReturn(RequestStatus.Status.Complete);
+    expect(status.getAssociatedResources()).andReturn(setResources);
+    expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
+    expect(resource2.getType()).andReturn(Resource.Type.Cluster).anyTimes();
+
+    replay(request, resource, pm, status, resource1, resource2, userPredicate, query);
+
+    Result result = new TestUpdateHandler(pm).handleRequest(request);
+
+    assertNotNull(result);
+    TreeNode<Resource> tree = result.getResultTree();
+    assertEquals(1, tree.getChildren().size());
+    TreeNode<Resource> resourcesNode = tree.getChild("resources");
+    assertEquals(2, resourcesNode.getChildren().size());
+    boolean foundResource1 = false;
+    boolean foundResource2 = false;
+    for(TreeNode<Resource> child : resourcesNode.getChildren()) {
+      Resource r = child.getObject();
+      if (r == resource1 && ! foundResource1) {
+        foundResource1 = true;
+      } else if (r == resource2 && ! foundResource2) {
+        foundResource2 = true;
+      } else {
+        fail();
+      }
+    }
+
+    assertEquals(ResultStatus.STATUS.OK, result.getStatus().getStatus());
+    verify(request, resource, pm, status, resource1, resource2, userPredicate, query);
+  }
+
+  @Test
+  public void testHandleRequest__Synchronous() throws Exception {
+    Request request = createMock(Request.class);
+    ResourceInstance resource = createMock(ResourceInstance.class);
+    PersistenceManager pm = createStrictMock(PersistenceManager.class);
+    RequestStatus status = createMock(RequestStatus.class);
+    Resource resource1 = createMock(Resource.class);
+    Resource resource2 = createMock(Resource.class);
+    Predicate userPredicate = createNiceMock(Predicate.class);
+    Query query = createNiceMock(Query.class);
 
     Set<Resource> setResources = new HashSet<Resource>();
     setResources.add(resource1);
     setResources.add(resource2);
 
+    Set<NamedPropertySet> setResourceProperties = new HashSet<NamedPropertySet>();
+    Map<String, Object> mapProps = new HashMap<String, Object>();
+    mapProps.put("foo", "bar");
+    NamedPropertySet namedPropSet = new NamedPropertySet("name", mapProps);
+    setResourceProperties.add(namedPropSet);
+
+    Set<Map<String, Object>> setProps = new HashSet<Map<String, Object>>();
+    setProps.add(mapProps);
+
     // expectations
     expect(request.getResource()).andReturn(resource).anyTimes();
     expect(request.getHttpBodyProperties()).andReturn(setResourceProperties).anyTimes();
@@ -66,7 +128,7 @@ public class UpdateHandlerTest {
     expect(resource.getQuery()).andReturn(query).atLeastOnce();
     query.setUserPredicate(userPredicate);
 
-    expect(pm.update(resource, setResourceProperties)).andReturn(status);
+    expect(pm.update(eq(resource), eq(setProps))).andReturn(status);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Complete);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
@@ -110,7 +172,6 @@ public class UpdateHandlerTest {
     Predicate userPredicate = createNiceMock(Predicate.class);
     Query query = createNiceMock(Query.class);
 
-    Set<Map<String, Object>> setResourceProperties = new HashSet<Map<String, Object>>();
 
     Set<Resource> setResources = new HashSet<Resource>();
     setResources.add(resource1);
@@ -118,13 +179,13 @@ public class UpdateHandlerTest {
 
     // expectations
     expect(request.getResource()).andReturn(resource);
-    expect(request.getHttpBodyProperties()).andReturn(setResourceProperties);
+    expect(request.getHttpBodyProperties()).andReturn(new HashSet<NamedPropertySet>()).anyTimes();
     expect(request.getQueryPredicate()).andReturn(userPredicate).atLeastOnce();
 
     expect(resource.getQuery()).andReturn(query).atLeastOnce();
     query.setUserPredicate(userPredicate);
 
-    expect(pm.update(resource, setResourceProperties)).andReturn(status);
+    expect(pm.update(eq(resource), eq(new HashSet<Map<String, Object>>()))).andReturn(status);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Accepted);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
@@ -174,21 +235,4 @@ public class UpdateHandlerTest {
       return m_testPm;
     }
   }
-
-  @Test
-  public void testHandleRequest__InvalidQuery() throws Exception {
-    Request request = createNiceMock(Request.class);
-    ResourceInstance resource = createNiceMock(ResourceInstance.class);
-    Exception e = new InvalidQueryException("test exception");
-
-    expect(request.getResource()).andReturn(resource);
-    expect(request.getQueryPredicate()).andThrow(e);
-    replay(request, resource);
-
-    Result result = new UpdateHandler().handleRequest(request);
-    assertEquals(ResultStatus.STATUS.BAD_REQUEST, result.getStatus().getStatus());
-    assertTrue(result.getStatus().getMessage().contains(e.getMessage()));
-
-    verify(request, resource);
-  }
 }

+ 38 - 43
ambari-server/src/test/java/org/apache/ambari/server/api/services/ActionServiceTest.java

@@ -19,82 +19,77 @@ package org.apache.ambari.server.api.services;
 
 import static org.junit.Assert.assertEquals;
 
-import javax.ws.rs.core.Response;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriInfo;
 
-import org.apache.ambari.server.AmbariException;
-import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.resources.ResourceInstance;
-import org.junit.Test;
+import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
+import org.apache.ambari.server.api.services.serializers.ResultSerializer;
 
-public class ActionServiceTest extends BaseServiceTest {
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
 
-  @Test
-  public void testGetActions() {
-    String clusterName = "c1";
-    String serviceName = "HDFS";
+public class ActionServiceTest extends BaseServiceTest {
 
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
+  public List<ServiceTestInvocation> getTestInvocations() throws Exception {
+    List<ServiceTestInvocation> listInvocations = new ArrayList<ServiceTestInvocation>();
 
-    //test
-    ActionService actionService = new TestActionService(getResource(), clusterName, getRequestFactory(),
-        getRequestHandler(), serviceName);
-    Response response = actionService.getActions(getHttpHeaders(), getUriInfo());
-    verifyResults(response, 200);
-  }
+    //getActions
+    ActionService componentService = new TestActionService("clusterName", "serviceName", null);
+    Method m = componentService.getClass().getMethod("getActions", HttpHeaders.class, UriInfo.class);
+    Object[] args = new Object[] {getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, componentService, m, args, null));
 
-  @Test
-  public void testCreateActions() throws AmbariException {
-    String clusterName = "c1";
-    String serviceName = "HDFS";
-    String body = "body";
+    //createAction
+    componentService = new TestActionService("clusterName", "serviceName", "actionName");
+    m = componentService.getClass().getMethod("createAction", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "actionName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, componentService, m, args, "body"));
 
-    registerExpectations(Request.Type.POST, body, 201, false);
-    replayMocks();
+    //createActions
+    componentService = new TestActionService("clusterName", "serviceName", null);
+    m = componentService.getClass().getMethod("createActions", String.class, HttpHeaders.class, UriInfo.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, componentService, m, args, "body"));
 
-    //test
-    ActionService actionService = new TestActionService(getResource(), clusterName, getRequestFactory(),
-        getRequestHandler(), serviceName);
-    Response response = actionService.createActions(body, getHttpHeaders(), getUriInfo());
-    verifyResults(response, 201);
+    return listInvocations;
   }
 
   private class TestActionService extends ActionService {
-    private ResourceInstance m_resourceDef;
     private String m_clusterId;
     private String m_serviceId;
-    private RequestFactory m_requestFactory;
-    private RequestHandler m_requestHandler;
+    private String m_actionId;
 
-    public TestActionService(ResourceInstance resourceDef,
-                             String clusterName, RequestFactory requestFactory,
-                             RequestHandler handler,
-                             String serviceName) {
+    public TestActionService(String clusterName, String serviceName, String actionName) {
 
       super(clusterName, serviceName);
-      m_resourceDef = resourceDef;
       m_clusterId = clusterName;
       m_serviceId = serviceName;
-      m_requestFactory = requestFactory;
-      m_requestHandler = handler;
+      m_actionId  = actionName;
     }
 
     @Override
     ResourceInstance createActionResource(String clusterName, String serviceName, String actionName) {
       assertEquals(m_clusterId, clusterName);
       assertEquals(m_serviceId, serviceName);
-      return m_resourceDef;
+      assertEquals(m_actionId, actionName);
+      return getTestResource();
     }
 
     @Override
     RequestFactory getRequestFactory() {
-      return m_requestFactory;
+      return getTestRequestFactory();
     }
 
+    @Override
+    protected RequestBodyParser getBodyParser() {
+      return getTestBodyParser();
+    }
 
     @Override
-    RequestHandler getRequestHandler(Request.Type requestType) {
-      return m_requestHandler;
+    protected ResultSerializer getResultSerializer() {
+      return getTestResultSerializer();
     }
   }
 }

+ 258 - 36
ambari-server/src/test/java/org/apache/ambari/server/api/services/BaseRequestTest.java

@@ -18,8 +18,11 @@
 
 package org.apache.ambari.server.api.services;
 
+import com.sun.jersey.core.util.MultivaluedMapImpl;
+import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.predicate.InvalidQueryException;
 import org.apache.ambari.server.api.predicate.PredicateCompiler;
+import org.apache.ambari.server.api.resources.ResourceInstance;
 import org.apache.ambari.server.controller.internal.TemporalInfoImpl;
 import org.apache.ambari.server.controller.spi.Predicate;
 import org.apache.ambari.server.controller.spi.TemporalInfo;
@@ -29,11 +32,9 @@ import org.junit.Test;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.UriInfo;
-import java.io.UnsupportedEncodingException;
 import java.net.URI;
-import java.net.URISyntaxException;
 import java.net.URLEncoder;
-import java.util.Map;
+import java.util.*;
 
 import static org.easymock.EasyMock.*;
 import static org.junit.Assert.*;
@@ -43,76 +44,296 @@ import static org.junit.Assert.*;
  */
 public abstract class BaseRequestTest {
 
-  public void testGetQueryPredicate(String uriString) throws Exception {
+  @Test
+  public void testGetHttpBody() {
+    RequestBody body = createNiceMock(RequestBody.class);
+    Request request = getTestRequest(null, body, null, null, null, null, null);
+
+    expect(body.getBody()) .andReturn("{\foo\" : \"bar\"}");
+    replay(body);
+
+    assertEquals("{\foo\" : \"bar\"}", request.getHttpBody());
+
+    verify(body);
+  }
+
+  @Test
+  public void testGetHttpBodyProperties() {
+    Set<NamedPropertySet> setProps = new HashSet<NamedPropertySet>();
+    RequestBody body = createNiceMock(RequestBody.class);
+    Request request = getTestRequest(null, body, null, null, null, null, null);
+
+    expect(body.getPropertySets()).andReturn(setProps);
+    replay(body);
+
+    assertSame(setProps, request.getHttpBodyProperties());
+    verify(body);
+  }
+
+  @Test
+  public void testGetResource() {
+    ResourceInstance resource = createNiceMock(ResourceInstance.class);
+    Request request = getTestRequest(null, null, null, null, null, null, resource);
+
+    assertSame(resource, request.getResource());
+  }
+
+  @Test
+  public void testGetApiVersion() {
+    Request request = getTestRequest(null, null, null, null, null, null, null);
+    assertEquals(1, request.getAPIVersion());
+  }
+
+  @Test
+  public void testGetHttpHeaders() {
+    HttpHeaders headers = createNiceMock(HttpHeaders.class);
+    MultivaluedMap<String, String> mapHeaders = new MultivaluedMapImpl();
+    Request request = getTestRequest(headers, null, null, null, null, null, null);
+
+    expect(headers.getRequestHeaders()).andReturn(mapHeaders);
+    replay(headers);
+
+    assertSame(mapHeaders, request.getHttpHeaders());
+    verify(headers);
+  }
+
+  @Test
+  public void testProcess_noBody() throws Exception {
+    String uriString = "http://localhost.com:8080/api/v1/clusters/c1";
+    URI uri = new URI(URLEncoder.encode(uriString, "UTF-8"));
     PredicateCompiler compiler = createStrictMock(PredicateCompiler.class);
-    Predicate p = createMock(Predicate.class);
     UriInfo uriInfo = createMock(UriInfo.class);
+    RequestHandler handler = createStrictMock(RequestHandler.class);
+    Result result = createMock(Result.class);
+    ResultStatus resultStatus = createMock(ResultStatus.class);
+    ResultPostProcessor processor = createStrictMock(ResultPostProcessor.class);
+    RequestBody body = createNiceMock(RequestBody.class);
+
+    Request request = getTestRequest(null, body, uriInfo, compiler, handler, processor, null);
+
+    //expectations
+    expect(uriInfo.getRequestUri()).andReturn(uri).anyTimes();
+    expect(handler.handleRequest(request)).andReturn(result);
+    expect(result.getStatus()).andReturn(resultStatus).anyTimes();
+    expect(resultStatus.isErrorState()).andReturn(false).anyTimes();
+    processor.process(result);
+
+    replay(compiler, uriInfo, handler, result, resultStatus, processor, body);
+
+    Result processResult = request.process();
+
+    verify(compiler, uriInfo, handler, result, resultStatus, processor, body);
+    assertSame(result, processResult);
+    assertNull(request.getQueryPredicate());
+  }
+
+  @Test
+  public void testProcess_WithBody() throws Exception {
+    String uriString = "http://localhost.com:8080/api/v1/clusters/c1";
     URI uri = new URI(URLEncoder.encode(uriString, "UTF-8"));
+    PredicateCompiler compiler = createStrictMock(PredicateCompiler.class);
+    UriInfo uriInfo = createMock(UriInfo.class);
+    RequestHandler handler = createStrictMock(RequestHandler.class);
+    Result result = createMock(Result.class);
+    ResultStatus resultStatus = createMock(ResultStatus.class);
+    ResultPostProcessor processor = createStrictMock(ResultPostProcessor.class);
+    RequestBody body = createNiceMock(RequestBody.class);
 
-    expect(uriInfo.getRequestUri()).andReturn(uri);
-    expect(compiler.compile(uriString.substring(uriString.indexOf("?") + 1))).andReturn(p);
+    Request request = getTestRequest(null, body, uriInfo, compiler, handler, processor, null);
 
-    replay(uriInfo, compiler, p);
+    //expectations
+    expect(uriInfo.getRequestUri()).andReturn(uri).anyTimes();
+    expect(handler.handleRequest(request)).andReturn(result);
+    expect(result.getStatus()).andReturn(resultStatus).anyTimes();
+    expect(resultStatus.isErrorState()).andReturn(false).anyTimes();
+    processor.process(result);
+    expect(body.getQueryString()).andReturn(null);
 
-    Request request = getTestRequest(null, null, uriInfo, compiler);
+    replay(compiler, uriInfo, handler, result, resultStatus, processor, body);
 
-    assertEquals(p, request.getQueryPredicate());
+    Result processResult = request.process();
 
-    verify(uriInfo, compiler, p);
+    verify(compiler, uriInfo, handler, result, resultStatus, processor, body);
+    assertSame(result, processResult);
+    assertNull(request.getQueryPredicate());
   }
 
+
   @Test
-  public void testGetQueryPredicate_noQueryString() throws Exception {
-    String uriString = "http://localhost.com:8080/api/v1/clusters";
+  public void testProcess_QueryInURI() throws Exception {
+    HttpHeaders headers = createNiceMock(HttpHeaders.class);
+    String uriString = "http://localhost.com:8080/api/v1/clusters/c1?foo=foo-value&bar=bar-value";
+    URI uri = new URI(URLEncoder.encode(uriString, "UTF-8"));
     PredicateCompiler compiler = createStrictMock(PredicateCompiler.class);
+    Predicate predicate = createNiceMock(Predicate.class);
     UriInfo uriInfo = createMock(UriInfo.class);
+    RequestHandler handler = createStrictMock(RequestHandler.class);
+    Result result = createMock(Result.class);
+    ResultStatus resultStatus = createMock(ResultStatus.class);
+    ResultPostProcessor processor = createStrictMock(ResultPostProcessor.class);
+    RequestBody body = createNiceMock(RequestBody.class);
+
+    Request request = getTestRequest(headers, body, uriInfo, compiler, handler, processor, null);
+
+    //expectations
+    expect(uriInfo.getRequestUri()).andReturn(uri).anyTimes();
+    expect(body.getQueryString()).andReturn(null);
+    expect(compiler.compile("foo=foo-value&bar=bar-value")).andReturn(predicate);
+    expect(handler.handleRequest(request)).andReturn(result);
+    expect(result.getStatus()).andReturn(resultStatus).anyTimes();
+    expect(resultStatus.isErrorState()).andReturn(false).anyTimes();
+    processor.process(result);
+
+    replay(headers, compiler, uriInfo, handler, result, resultStatus, processor, predicate, body);
+
+    Result processResult = request.process();
+
+    verify(headers, compiler, uriInfo, handler, result, resultStatus, processor, predicate, body);
+
+    assertSame(processResult, result);
+    assertSame(predicate, request.getQueryPredicate());
+  }
+
+  @Test
+  public void testProcess_QueryInBody() throws Exception {
+    HttpHeaders headers = createNiceMock(HttpHeaders.class);
+    String uriString = "http://localhost.com:8080/api/v1/clusters/c1";
     URI uri = new URI(URLEncoder.encode(uriString, "UTF-8"));
+    PredicateCompiler compiler = createStrictMock(PredicateCompiler.class);
+    Predicate predicate = createNiceMock(Predicate.class);
+    UriInfo uriInfo = createMock(UriInfo.class);
+    RequestHandler handler = createStrictMock(RequestHandler.class);
+    Result result = createMock(Result.class);
+    ResultStatus resultStatus = createMock(ResultStatus.class);
+    ResultPostProcessor processor = createStrictMock(ResultPostProcessor.class);
+    RequestBody body = createNiceMock(RequestBody.class);
+
+    Request request = getTestRequest(headers, body, uriInfo, compiler, handler, processor, null);
 
-    expect(uriInfo.getRequestUri()).andReturn(uri);
+    //expectations
+    expect(uriInfo.getRequestUri()).andReturn(uri).anyTimes();
+    expect(body.getQueryString()).andReturn("foo=bar");
+    expect(compiler.compile("foo=bar")).andReturn(predicate);
+    expect(handler.handleRequest(request)).andReturn(result);
+    expect(result.getStatus()).andReturn(resultStatus).anyTimes();
+    expect(resultStatus.isErrorState()).andReturn(false).anyTimes();
+    processor.process(result);
 
-    replay(uriInfo, compiler);
+    replay(headers, compiler, uriInfo, handler, result, resultStatus, processor, predicate, body);
 
-    Request request = getTestRequest(null, null, uriInfo, compiler);
+    Result processResult = request.process();
 
-    assertEquals(null, request.getQueryPredicate());
+    verify(headers, compiler, uriInfo, handler, result, resultStatus, processor, predicate, body);
 
-    verify(uriInfo, compiler);
+    assertSame(processResult, result);
+    assertSame(predicate, request.getQueryPredicate());
   }
 
   @Test
-  public void testGetQueryPredicate_invalidQuery() throws Exception {
-    String uriString = "http://localhost.com:8080/api/v1/clusters?&foo|";
+  public void testProcess_QueryInBodyAndURI() throws Exception {
+    HttpHeaders headers = createNiceMock(HttpHeaders.class);
+    String uriString = "http://localhost.com:8080/api/v1/clusters/c1?bar=value";
+    URI uri = new URI(URLEncoder.encode(uriString, "UTF-8"));
     PredicateCompiler compiler = createStrictMock(PredicateCompiler.class);
+    Predicate predicate = createNiceMock(Predicate.class);
+    UriInfo uriInfo = createMock(UriInfo.class);
+    RequestHandler handler = createStrictMock(RequestHandler.class);
+    Result result = createMock(Result.class);
+    ResultStatus resultStatus = createMock(ResultStatus.class);
+    ResultPostProcessor processor = createStrictMock(ResultPostProcessor.class);
+    RequestBody body = createNiceMock(RequestBody.class);
+
+    Request request = getTestRequest(headers, body, uriInfo, compiler, handler, processor, null);
+
+    //expectations
+    expect(uriInfo.getRequestUri()).andReturn(uri).anyTimes();
+    expect(body.getQueryString()).andReturn("foo=bar");
+    expect(compiler.compile("foo=bar")).andReturn(predicate);
+    expect(handler.handleRequest(request)).andReturn(result);
+    expect(result.getStatus()).andReturn(resultStatus).anyTimes();
+    expect(resultStatus.isErrorState()).andReturn(false).anyTimes();
+    processor.process(result);
+
+    replay(headers, compiler, uriInfo, handler, result, resultStatus, processor, predicate, body);
+
+    Result processResult = request.process();
+
+    verify(headers, compiler, uriInfo, handler, result, resultStatus, processor, predicate, body);
+
+    assertSame(processResult, result);
+    assertSame(predicate, request.getQueryPredicate());
+  }
+
+  @Test
+  public void testProcess_WithBody_InvalidQuery() throws Exception {
     UriInfo uriInfo = createMock(UriInfo.class);
+    String uriString = "http://localhost.com:8080/api/v1/clusters/c1";
     URI uri = new URI(URLEncoder.encode(uriString, "UTF-8"));
+    PredicateCompiler compiler = createStrictMock(PredicateCompiler.class);
+    RequestBody body = createNiceMock(RequestBody.class);
+    Exception exception = new InvalidQueryException("test");
+
+    Request request = getTestRequest(null, body, uriInfo, compiler, null, null, null);
+
+    //expectations
+    expect(uriInfo.getRequestUri()).andReturn(uri).anyTimes();
+    expect(body.getQueryString()).andReturn("blahblahblah");
+    expect(compiler.compile("blahblahblah")).andThrow(exception);
 
-    expect(uriInfo.getRequestUri()).andReturn(uri);
-    expect(compiler.compile(uriString.substring(uriString.indexOf("?") + 1))).
-        andThrow(new InvalidQueryException("test"));
-    replay(uriInfo, compiler);
+    replay(compiler, uriInfo, body);
 
-    Request request = getTestRequest(null, null, uriInfo, compiler);
+    Result processResult = request.process();
 
-    try {
-      request.getQueryPredicate();
-      fail("Expected InvalidQueryException due to invalid query");
-    } catch (InvalidQueryException e) {
-      //expected
-    }
+    verify(compiler, uriInfo, body);
 
-    verify(uriInfo, compiler);
+    assertEquals(400, processResult.getStatus().getStatusCode());
+    assertTrue(processResult.getStatus().isErrorState());
+    assertEquals("Unable to compile query predicate: test", processResult.getStatus().getMessage());
   }
 
-  public void testGetFields(String fields) {
+  @Test
+  public void testProcess_noBody_ErrorStateResult() throws Exception {
+    String uriString = "http://localhost.com:8080/api/v1/clusters/c1";
+    URI uri = new URI(URLEncoder.encode(uriString, "UTF-8"));
+    PredicateCompiler compiler = createStrictMock(PredicateCompiler.class);
     UriInfo uriInfo = createMock(UriInfo.class);
+    RequestHandler handler = createStrictMock(RequestHandler.class);
+    Result result = createMock(Result.class);
+    ResultStatus resultStatus = createMock(ResultStatus.class);
+    ResultPostProcessor processor = createStrictMock(ResultPostProcessor.class);
+    RequestBody body = createNiceMock(RequestBody.class);
+
+    Request request = getTestRequest(null, body, uriInfo, compiler, handler, processor, null);
+
+    //expectations
+    expect(uriInfo.getRequestUri()).andReturn(uri).anyTimes();
+    expect(handler.handleRequest(request)).andReturn(result);
+    expect(result.getStatus()).andReturn(resultStatus).anyTimes();
+    expect(resultStatus.isErrorState()).andReturn(true).anyTimes();
+
+    replay(compiler, uriInfo, handler, result, resultStatus, processor, body);
+
+    Result processResult = request.process();
+
+    verify(compiler, uriInfo, handler, result, resultStatus, processor, body);
+    assertSame(result, processResult);
+    assertNull(request.getQueryPredicate());
+  }
+
+  @Test
+  public void testGetFields() throws Exception {
+    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";
+    UriInfo uriInfo = createMock(UriInfo.class);
+
+    @SuppressWarnings("unchecked")
     MultivaluedMap<String, String> mapQueryParams = createMock(MultivaluedMap.class);
-    Request request = getTestRequest(null, null, uriInfo, null);
 
     expect(uriInfo.getQueryParameters()).andReturn(mapQueryParams);
     expect(mapQueryParams.getFirst("fields")).andReturn(fields);
 
     replay(uriInfo, mapQueryParams);
 
+    Request request = getTestRequest(null, null, uriInfo, null, null, null, null);
     Map<String, TemporalInfo> mapFields = request.getFields();
 
     assertEquals(7, mapFields.size());
@@ -148,6 +369,7 @@ public abstract class BaseRequestTest {
     verify(uriInfo, mapQueryParams);
   }
 
-   protected abstract Request getTestRequest(HttpHeaders headers, String body,
-                                             UriInfo uriInfo, PredicateCompiler compiler);
+   protected abstract Request getTestRequest(HttpHeaders headers, RequestBody body, UriInfo uriInfo, PredicateCompiler compiler,
+                                             RequestHandler handler, ResultPostProcessor processor, ResourceInstance resource);
+
 }

+ 129 - 46
ambari-server/src/test/java/org/apache/ambari/server/api/services/BaseServiceTest.java

@@ -18,17 +18,27 @@
 
 package org.apache.ambari.server.api.services;
 
-import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.api.services.parsers.BodyParseException;
+import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
 import org.apache.ambari.server.api.services.serializers.ResultSerializer;
+import org.easymock.Capture;
+import org.junit.Test;
 
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import static org.easymock.EasyMock.*;
 import static org.easymock.EasyMock.createNiceMock;
 import static org.easymock.EasyMock.createStrictMock;
+
 import static org.junit.Assert.assertEquals;
 
 /**
@@ -38,43 +48,28 @@ public abstract class BaseServiceTest {
 
   private ResourceInstance resourceInstance = createNiceMock(ResourceInstance.class);
   private RequestFactory requestFactory = createStrictMock(RequestFactory.class);
-  private ResultPostProcessor resultProcessor = createStrictMock(ResultPostProcessor.class);
   private Request request = createNiceMock(Request.class);
-  private RequestHandler requestHandler = createStrictMock(RequestHandler.class);
-  private Result result = createNiceMock(Result.class);
-  private ResultStatus status = createNiceMock(ResultStatus.class);
   private HttpHeaders httpHeaders = createNiceMock(HttpHeaders.class);
   private UriInfo uriInfo = createNiceMock(UriInfo.class);
+  private Result result = createMock(Result.class);
+  private RequestBody requestBody = createNiceMock(RequestBody.class);
+  private RequestBodyParser bodyParser = createStrictMock(RequestBodyParser.class);
+  private ResultStatus status = createNiceMock(ResultStatus.class);
   private ResultSerializer serializer = createStrictMock(ResultSerializer.class);
   private Object serializedResult = new Object();
 
-  public ResourceInstance getResource() {
+  public ResourceInstance getTestResource() {
     return resourceInstance;
   }
 
-  public RequestFactory getRequestFactory() {
+  public RequestFactory getTestRequestFactory() {
     return requestFactory;
   }
 
-  public ResultPostProcessor getResultProcessor() {
-    return resultProcessor;
-  }
-
   public Request getRequest() {
     return request;
   }
 
-  public RequestHandler getRequestHandler() {
-    return requestHandler;
-  }
-
-  public Result getResult() {
-    return result;
-  }
-
-  public ResultStatus getStatus() {
-    return status;
-  }
 
   public HttpHeaders getHttpHeaders() {
     return httpHeaders;
@@ -84,45 +79,133 @@ public abstract class BaseServiceTest {
     return uriInfo;
   }
 
-  public ResultSerializer getSerializer() {
+  public RequestBodyParser getTestBodyParser() {
+    return bodyParser;
+  }
+
+  public ResultSerializer getTestResultSerializer() {
     return serializer;
   }
 
-  public Object getSerializedResult() {
-    return serializedResult;
+  @Test
+  public void testService() throws Exception {
+    List<ServiceTestInvocation> listTestInvocations = getTestInvocations();
+    for (ServiceTestInvocation testInvocation : listTestInvocations) {
+      testMethod(testInvocation);
+      testMethod_bodyParseException(testInvocation);
+      testMethod_resultInErrorState(testInvocation);
+    }
   }
 
-  protected void registerExpectations(Request.Type type, String body, int statusCode, boolean isErrorState) {
-    expect(requestFactory.createRequest(eq(httpHeaders), body == null ? isNull(String.class) : eq(body), eq(uriInfo), eq(type),
-        eq(resourceInstance))).andReturn(request);
-
-    expect(request.getRequestType()).andReturn(type).anyTimes();
-    expect(request.getResultSerializer()).andReturn(serializer).anyTimes();
-    expect(requestHandler.handleRequest(request)).andReturn(result);
-    expect(result.getStatus()).andReturn(status).anyTimes();
-    expect(status.isErrorState()).andReturn(isErrorState).anyTimes();
-    expect(status.getStatusCode()).andReturn(statusCode);
-    if (! isErrorState) {
-      expect(request.getResultPostProcessor()).andReturn(resultProcessor);
-      resultProcessor.process(result);
+  private void testMethod(ServiceTestInvocation testMethod) throws InvocationTargetException, IllegalAccessException {
+    try {
+      expect(bodyParser.parse(testMethod.getBody())).andReturn(requestBody);
+    } catch (BodyParseException e) {
+      // needed for compiler
     }
 
+    expect(requestFactory.createRequest(httpHeaders, requestBody, uriInfo, testMethod.getRequestType(), resourceInstance)).andReturn(request);
+    expect(request.process()).andReturn(result);
+    expect(result.getStatus()).andReturn(status).atLeastOnce();
+    expect(status.getStatusCode()).andReturn(testMethod.getStatusCode()).atLeastOnce();
     expect(serializer.serialize(result)).andReturn(serializedResult);
 
+    replayMocks();
+
+    Response r = testMethod.invoke();
+
+    assertEquals(serializedResult, r.getEntity());
+    assertEquals(testMethod.getStatusCode(), r.getStatus());
+    verifyAndResetMocks();
   }
 
-  protected void replayMocks() {
-    replay(resourceInstance, requestFactory, resultProcessor, request, status, requestHandler,
-        result, httpHeaders, uriInfo, serializer);
+  private void testMethod_bodyParseException(ServiceTestInvocation testMethod) throws Exception {
+    Capture<Result> resultCapture = new Capture<Result>();
+    BodyParseException e = new BodyParseException("TEST MSG");
+    expect(bodyParser.parse(testMethod.getBody())).andThrow(e);
+    expect(serializer.serialize(capture(resultCapture))).andReturn(serializedResult);
+
+    replayMocks();
+
+    Response r = testMethod.invoke();
+
+    assertEquals(serializedResult, r.getEntity());
+    assertEquals(400, r.getStatus());
+    //todo: assert resource state
+    verifyAndResetMocks();
   }
 
+  private void testMethod_resultInErrorState(ServiceTestInvocation testMethod) throws Exception {
+    try {
+      expect(bodyParser.parse(testMethod.getBody())).andReturn(requestBody);
+    } catch (BodyParseException e) {
+      // needed for compiler
+    }
+    expect(requestFactory.createRequest(httpHeaders, requestBody, uriInfo, testMethod.getRequestType(), resourceInstance)).andReturn(request);
+    expect(request.process()).andReturn(result);
+    expect(result.getStatus()).andReturn(status).atLeastOnce();
+    expect(status.getStatusCode()).andReturn(400).atLeastOnce();
+    expect(serializer.serialize(result)).andReturn(serializedResult);
+
+    replayMocks();
+
+    Response r = testMethod.invoke();
+
+    assertEquals(serializedResult, r.getEntity());
+    assertEquals(400, r.getStatus());
+    verifyAndResetMocks();
+  }
 
-  protected void verifyResults(Response response, int statusCode) {
-    assertEquals(getSerializedResult(), response.getEntity());
-    assertEquals(statusCode, response.getStatus());
+  private void replayMocks() {
+    replay(resourceInstance, requestFactory, request, result, requestBody, bodyParser, status, serializer);
+  }
+
+  private void verifyAndResetMocks() {
+    verify(resourceInstance, requestFactory, request, result, requestBody, bodyParser, status, serializer);
+    reset(resourceInstance, requestFactory, request, result, requestBody, bodyParser, status, serializer);
+  }
+
+  public static class ServiceTestInvocation {
+    private Request.Type m_type;
+    private BaseService m_instance;
+    private Method m_method;
+    private Object[] m_args;
+    private String m_body;
+
+    private static final Map<Request.Type, Integer> mapStatusCodes = new HashMap<Request.Type, Integer>();
+
+    static {
+      mapStatusCodes.put(Request.Type.GET, 200);
+      mapStatusCodes.put(Request.Type.POST, 201);
+      mapStatusCodes.put(Request.Type.PUT, 200);
+      mapStatusCodes.put(Request.Type.DELETE, 200);
+      mapStatusCodes.put(Request.Type.QUERY_POST, 201);
+    }
 
-    verify(resourceInstance, requestFactory, resultProcessor, request, status, requestHandler,
-        result, httpHeaders, uriInfo, serializer);
+    public ServiceTestInvocation(Request.Type requestType, BaseService instance, Method method, Object[] args, String body) {
+      m_type = requestType;
+      m_instance = instance;
+      m_method = method;
+      m_args = args;
+      m_body = body;
+    }
+
+    public int getStatusCode() {
+      return mapStatusCodes.get(m_type);
+    }
+
+    public Request.Type getRequestType() {
+      return m_type;
+    }
+
+    public String getBody() {
+      return m_body;
+    }
+
+    public Response invoke() throws InvocationTargetException, IllegalAccessException {
+      return (Response) m_method.invoke(m_instance, m_args);
+    }
   }
 
+  public abstract List<ServiceTestInvocation> getTestInvocations() throws Exception;
 }

+ 52 - 150
ambari-server/src/test/java/org/apache/ambari/server/api/services/ClusterServiceTest.java

@@ -18,11 +18,16 @@
 
 package org.apache.ambari.server.api.services;
 
-import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.resources.ResourceInstance;
-import org.junit.Test;
+import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
+import org.apache.ambari.server.api.services.serializers.ResultSerializer;
 
-import javax.ws.rs.core.Response;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriInfo;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
 
 import static org.junit.Assert.assertEquals;
 
@@ -32,173 +37,70 @@ import static org.junit.Assert.assertEquals;
  */
 public class ClusterServiceTest extends BaseServiceTest {
 
-  @Test
-   public void testGetCluster() {
-    String clusterName = "clusterName";
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    //test
-    ClusterService clusterService = new TestClusterService(getResource(), clusterName, getRequestFactory(), getRequestHandler());
-    Response response = clusterService.getCluster(getHttpHeaders(), getUriInfo(), clusterName);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetCluster__ErrorState() {
-    String clusterName = "clusterName";
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    //test
-    ClusterService clusterService = new TestClusterService(getResource(), clusterName, getRequestFactory(), getRequestHandler());
-    Response response = clusterService.getCluster(getHttpHeaders(), getUriInfo(), clusterName);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testGetClusters() {
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    //test
-    ClusterService clusterService = new TestClusterService(getResource(), null, getRequestFactory(), getRequestHandler());
-    Response response = clusterService.getClusters(getHttpHeaders(), getUriInfo());
-
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetClusters__ErrorState() {
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    //test
-    ClusterService clusterService = new TestClusterService(getResource(), null, getRequestFactory(), getRequestHandler());
-    Response response = clusterService.getClusters(getHttpHeaders(), getUriInfo());
-
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testCreateCluster() {
-    String clusterName = "clusterName";
-    String body = "body";
 
-    registerExpectations(Request.Type.POST, body, 201, false);
-    replayMocks();
-
-    //test
-    ClusterService clusterService = new TestClusterService(getResource(), clusterName, getRequestFactory(), getRequestHandler());
-    Response response = clusterService.createCluster(body, getHttpHeaders(), getUriInfo(), clusterName);
-
-    verifyResults(response, 201);
+  public List<ServiceTestInvocation> getTestInvocations() throws Exception {
+    List<ServiceTestInvocation> listInvocations = new ArrayList<ServiceTestInvocation>();
+
+    //getCluster
+    ClusterService clusterService = new TestClusterService("clusterName");
+    Method m = clusterService.getClass().getMethod("getCluster", HttpHeaders.class, UriInfo.class, String.class);
+    Object[] args = new Object[] {getHttpHeaders(), getUriInfo(), "clusterName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, clusterService, m, args, null));
+
+    //getClusters
+    clusterService = new TestClusterService(null);
+    m = clusterService.getClass().getMethod("getClusters", HttpHeaders.class, UriInfo.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, clusterService, m, args, null));
+
+    //createCluster
+    clusterService = new TestClusterService("clusterName");
+    m = clusterService.getClass().getMethod("createCluster", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "clusterName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, clusterService, m, args, "body"));
+
+    //createCluster
+    clusterService = new TestClusterService("clusterName");
+    m = clusterService.getClass().getMethod("updateCluster", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "clusterName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.PUT, clusterService, m, args, "body"));
+
+    //deleteCluster
+    clusterService = new TestClusterService("clusterName");
+    m = clusterService.getClass().getMethod("deleteCluster", HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo(), "clusterName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.DELETE, clusterService, m, args, null));
+
+    return listInvocations;
   }
 
-  @Test
-  public void testCreateCluster__ErrorState() {
-    String clusterName = "clusterName";
-    String body = "body";
-
-    registerExpectations(Request.Type.POST, body, 500, true);
-    replayMocks();
-
-    //test
-    ClusterService clusterService = new TestClusterService(getResource(), clusterName, getRequestFactory(), getRequestHandler());
-    Response response = clusterService.createCluster(body, getHttpHeaders(), getUriInfo(), clusterName);
-
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testUpdateCluster() {
-    String clusterName = "clusterName";
-    String body = "body";
-
-    registerExpectations(Request.Type.PUT, body, 200, false);
-    replayMocks();
-
-    //test
-    ClusterService clusterService = new TestClusterService(getResource(), clusterName, getRequestFactory(), getRequestHandler());
-    Response response = clusterService.updateCluster(body, getHttpHeaders(), getUriInfo(), clusterName);
-
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testUpdateCluster__ErrorState() {
-    String clusterName = "clusterName";
-    String body = "body";
-
-    registerExpectations(Request.Type.PUT, body, 500, true);
-    replayMocks();
-
-    //test
-    ClusterService clusterService = new TestClusterService(getResource(), clusterName, getRequestFactory(), getRequestHandler());
-    Response response = clusterService.updateCluster(body, getHttpHeaders(), getUriInfo(), clusterName);
-
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testDeleteCluster() {
-    String clusterName = "clusterName";
-
-    registerExpectations(Request.Type.DELETE, null, 200, false);
-    replayMocks();
-
-    //test
-    ClusterService clusterService = new TestClusterService(getResource(), clusterName, getRequestFactory(), getRequestHandler());
-    Response response = clusterService.deleteCluster(getHttpHeaders(), getUriInfo(), clusterName);
-
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testDeleteCluster__ErrorState() {
-    String clusterName = "clusterName";
-
-    registerExpectations(Request.Type.DELETE, null, 500, true);
-    replayMocks();
-
-    //test
-    ClusterService clusterService = new TestClusterService(getResource(), clusterName, getRequestFactory(), getRequestHandler());
-    Response response = clusterService.deleteCluster(getHttpHeaders(), getUriInfo(), clusterName);
-
-    verifyResults(response, 500);
-  }
 
   private class TestClusterService extends ClusterService {
-    private RequestFactory m_requestFactory;
-    private RequestHandler m_requestHandler;
-    private ResourceInstance m_resourceDef;
     private String m_clusterId;
 
-    private TestClusterService(ResourceInstance resourceDef, String clusterId, RequestFactory requestFactory,
-                               RequestHandler handler) {
-      m_resourceDef = resourceDef;
-      m_requestFactory = requestFactory;
-      m_requestHandler = handler;
+    private TestClusterService(String clusterId) {
       m_clusterId = clusterId;
     }
 
     @Override
     ResourceInstance createClusterResource(String clusterName) {
       assertEquals(m_clusterId, clusterName);
-      return m_resourceDef;
+      return getTestResource();
     }
 
     @Override
     RequestFactory getRequestFactory() {
-      return m_requestFactory;
+      return getTestRequestFactory();
+    }
+
+    @Override
+    protected RequestBodyParser getBodyParser() {
+      return getTestBodyParser();
     }
 
     @Override
-    RequestHandler getRequestHandler(Request.Type requestType) {
-      return m_requestHandler;
+    protected ResultSerializer getResultSerializer() {
+      return getTestResultSerializer();
     }
   }
 

+ 64 - 217
ambari-server/src/test/java/org/apache/ambari/server/api/services/ComponentServiceTest.java

@@ -20,11 +20,16 @@
 package org.apache.ambari.server.api.services;
 
 
-import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.resources.ResourceInstance;
-import org.junit.Test;
+import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
+import org.apache.ambari.server.api.services.serializers.ResultSerializer;
 
-import javax.ws.rs.core.Response;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriInfo;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
 
 import static org.junit.Assert.assertEquals;
 
@@ -33,224 +38,61 @@ import static org.junit.Assert.assertEquals;
  */
 public class ComponentServiceTest extends BaseServiceTest {
 
-  @Test
-  public void testGetComponent()  {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-    String componentName = "componentName";
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    //test
-    ComponentService componentService = new TestComponentService(getResource(), clusterName, serviceName, componentName,
-        getRequestFactory(), getRequestHandler());
-
-    Response response = componentService.getComponent(getHttpHeaders(), getUriInfo(), componentName);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetComponent__ErrorState()  {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-    String componentName = "componentName";
-
-    registerExpectations(Request.Type.GET, null, 404, true);
-    replayMocks();
-
-    //test
-    ComponentService componentService = new TestComponentService(getResource(), clusterName, serviceName, componentName,
-        getRequestFactory(), getRequestHandler());
-
-    Response response = componentService.getComponent(getHttpHeaders(), getUriInfo(), componentName);
-    verifyResults(response, 404);
-  }
-
-  @Test
-  public void testGetComponents() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    //test
-    ComponentService componentService = new TestComponentService(getResource(), clusterName, serviceName, null,
-        getRequestFactory(), getRequestHandler());
-
-    Response response = componentService.getComponents(getHttpHeaders(), getUriInfo());
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetComponents__ErrorState() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    //test
-    ComponentService componentService = new TestComponentService(getResource(), clusterName, serviceName, null,
-        getRequestFactory(), getRequestHandler());
-
-    Response response = componentService.getComponents(getHttpHeaders(), getUriInfo());
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testCreateComponent() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-    String componentName = "componentName";
-    String body = "{body}";
-
-    registerExpectations(Request.Type.POST, body, 201, false);
-    replayMocks();
-
-    //test
-    ComponentService componentService = new TestComponentService(getResource(), clusterName, serviceName, componentName,
-        getRequestFactory(), getRequestHandler());
-
-    Response response = componentService.createComponent(body, getHttpHeaders(), getUriInfo(), componentName);
-    verifyResults(response, 201);
-  }
-
-  @Test
-  public void testCreateComponent__ErrorState() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-    String componentName = "componentName";
-    String body = "{body}";
-
-    registerExpectations(Request.Type.POST, body, 500, true);
-    replayMocks();
-
-    //test
-    ComponentService componentService = new TestComponentService(getResource(), clusterName, serviceName, componentName,
-        getRequestFactory(), getRequestHandler());
-
-    Response response = componentService.createComponent(body, getHttpHeaders(), getUriInfo(), componentName);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testUpdateComponent() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-    String componentName = "componentName";
-    String body = "{body}";
-
-    registerExpectations(Request.Type.PUT, body, 200, false);
-    replayMocks();
-
-    //test
-    ComponentService componentService = new TestComponentService(getResource(), clusterName, serviceName, componentName,
-        getRequestFactory(), getRequestHandler());
-
-    Response response = componentService.updateComponent(body, getHttpHeaders(), getUriInfo(), componentName);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testUpdateComponent__ErrorState() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-    String componentName = "componentName";
-    String body = "{body}";
-
-    registerExpectations(Request.Type.PUT, body, 500, true);
-    replayMocks();
-
-    //test
-    ComponentService componentService = new TestComponentService(getResource(), clusterName, serviceName, componentName,
-        getRequestFactory(), getRequestHandler());
-
-    Response response = componentService.updateComponent(body, getHttpHeaders(), getUriInfo(), componentName);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testUpdateComponents() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-    String body = "{body}";
-
-    registerExpectations(Request.Type.PUT, body, 200, false);
-    replayMocks();
-
-    //test
-    ComponentService componentService = new TestComponentService(getResource(), clusterName, serviceName, null,
-        getRequestFactory(), getRequestHandler());
-
-    Response response = componentService.updateComponents(body, getHttpHeaders(), getUriInfo());
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testUpdateComponents__ErrorState() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-    String body = "{body}";
-
-    registerExpectations(Request.Type.PUT, body, 500, true);
-    replayMocks();
-
-    //test
-    ComponentService componentService = new TestComponentService(getResource(), clusterName, serviceName, null,
-        getRequestFactory(), getRequestHandler());
-
-    Response response = componentService.updateComponents(body, getHttpHeaders(), getUriInfo());
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testDeleteComponent() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-
-    registerExpectations(Request.Type.DELETE, null, 200, false);
-    replayMocks();
-
-    //test
-    ComponentService componentService = new TestComponentService(getResource(), clusterName, serviceName, null,
-        getRequestFactory(), getRequestHandler());
-
-    Response response = componentService.deleteComponent(getHttpHeaders(), getUriInfo(), null);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testDeleteComponent__ErrorState() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-
-    registerExpectations(Request.Type.DELETE, null, 500, true);
-    replayMocks();
-
-    //test
-    ComponentService componentService = new TestComponentService(getResource(), clusterName, serviceName, null,
-        getRequestFactory(), getRequestHandler());
-
-    Response response = componentService.deleteComponent(getHttpHeaders(), getUriInfo(), null);
-    verifyResults(response, 500);
+  public List<ServiceTestInvocation> getTestInvocations() throws Exception {
+    List<ServiceTestInvocation> listInvocations = new ArrayList<ServiceTestInvocation>();
+
+    //getComponent
+    ComponentService service = new TestComponentService("clusterName", "serviceName", "componentName");
+    Method m = service.getClass().getMethod("getComponent", HttpHeaders.class, UriInfo.class, String.class);
+    Object[] args = new Object[] {getHttpHeaders(), getUriInfo(), "componentName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
+
+    //getComponents
+    service = new TestComponentService("clusterName", "serviceName", null);
+    m = service.getClass().getMethod("getComponents", HttpHeaders.class, UriInfo.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
+
+    //createComponent
+    service = new TestComponentService("clusterName", "serviceName", "componentName");
+    m = service.getClass().getMethod("createComponent", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "componentName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, service, m, args, "body"));
+
+    //createComponents
+    service = new TestComponentService("clusterName", "serviceName", null);
+    m = service.getClass().getMethod("createComponents", String.class, HttpHeaders.class, UriInfo.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, service, m, args, "body"));
+
+    //updateComponent
+    service = new TestComponentService("clusterName", "serviceName", "componentName");
+    m = service.getClass().getMethod("updateComponent", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "componentName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.PUT, service, m, args, "body"));
+
+    //updateComponents
+    service = new TestComponentService("clusterName", "serviceName", null);
+    m = service.getClass().getMethod("updateComponents", String.class, HttpHeaders.class, UriInfo.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.PUT, service, m, args, "body"));
+
+    //deleteComponent
+    service = new TestComponentService("clusterName", "serviceName", "componentName");
+    m = service.getClass().getMethod("deleteComponent", HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo(), "componentName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.DELETE, service, m, args, null));
+
+    return listInvocations;
   }
 
   private class TestComponentService extends ComponentService {
-    private RequestFactory m_requestFactory;
-    private RequestHandler m_requestHandler;
-    private ResourceInstance m_resource;
     private String m_clusterId;
     private String m_serviceId;
     private String m_componentId;
 
-    private TestComponentService(ResourceInstance resourceDef, String clusterId, String serviceId, String componentId,
-                                 RequestFactory requestFactory, RequestHandler handler) {
+    private TestComponentService(String clusterId, String serviceId, String componentId) {
       super(clusterId, serviceId);
-      m_requestFactory = requestFactory;
-      m_requestHandler = handler;
-      m_resource = resourceDef;
       m_clusterId = clusterId;
       m_serviceId = serviceId;
       m_componentId = componentId;
@@ -261,17 +103,22 @@ public class ComponentServiceTest extends BaseServiceTest {
       assertEquals(m_clusterId, clusterName);
       assertEquals(m_serviceId, serviceName);
       assertEquals(m_componentId, componentName);
-      return m_resource;
+      return getTestResource();
     }
 
     @Override
     RequestFactory getRequestFactory() {
-      return m_requestFactory;
+      return getTestRequestFactory();
+    }
+
+    @Override
+    protected RequestBodyParser getBodyParser() {
+      return getTestBodyParser();
     }
 
     @Override
-    RequestHandler getRequestHandler(Request.Type requestType) {
-      return m_requestHandler;
+    protected ResultSerializer getResultSerializer() {
+      return getTestResultSerializer();
     }
   }
 }

+ 32 - 28
ambari-server/src/test/java/org/apache/ambari/server/api/services/ConfigurationServiceTest.java

@@ -21,60 +21,64 @@ import static org.junit.Assert.assertEquals;
 
 import javax.ws.rs.core.*;
 
-import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.resources.ResourceInstance;
-import org.junit.Test;
+import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
+import org.apache.ambari.server.api.services.serializers.ResultSerializer;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
 
 
 public class ConfigurationServiceTest extends BaseServiceTest {
-  
-  @Test
-  public void testCreateConfiguration() throws Exception{
-    String clusterName = "clusterName";
 
-    String body = "{ \"type\":\"hdfs-site\", \"tag\":\"my-new-tag\"," +
-        "\"properties\":{ \"key1\":\"value1\", \"key2\":\"value2\" } }";
+  public List<ServiceTestInvocation> getTestInvocations() throws Exception {
+    List<ServiceTestInvocation> listInvocations = new ArrayList<ServiceTestInvocation>();
 
-    registerExpectations(Request.Type.POST, body, 200, false);
-    replayMocks();
+    //getConfigurations
+    ConfigurationService service = new TestConfigurationService("clusterName");
+    Method m = service.getClass().getMethod("getConfigurations", HttpHeaders.class, UriInfo.class);
+    Object[] args = new Object[] {getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
 
-    //test
-    ConfigurationService configService = new TestConfigurationService(getResource(), clusterName, getRequestFactory(), getRequestHandler());
-    Response response = configService.createConfigurations(body, getHttpHeaders(), getUriInfo());
+    //createConfigurations
+    service = new TestConfigurationService("clusterName");
+    m = service.getClass().getMethod("createConfigurations", String.class, HttpHeaders.class, UriInfo.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, service, m, args, "body"));
 
-    verifyResults(response, 200);
+    return listInvocations;
   }
-  
+
+
   private class TestConfigurationService extends ConfigurationService {
-    private RequestFactory m_requestFactory;
-    private RequestHandler m_requestHandler;
-    private ResourceInstance m_resourceInstance;
     private String m_clusterId;
 
-    private TestConfigurationService(ResourceInstance resourceInstance, String clusterId, RequestFactory requestFactory,
-                                     RequestHandler handler) {
+    private TestConfigurationService(String clusterId) {
       super(clusterId);
-      m_resourceInstance = resourceInstance;
       m_clusterId = clusterId;
-      m_requestFactory = requestFactory;
-      m_requestHandler = handler;
     }
 
     @Override
     ResourceInstance createConfigurationResource(String clusterName) {
       assertEquals(m_clusterId, clusterName);
-      return m_resourceInstance;
+      return getTestResource();
     }
 
     @Override
     RequestFactory getRequestFactory() {
-      return m_requestFactory;
+      return getTestRequestFactory();
     }
 
     @Override
-    RequestHandler getRequestHandler(Request.Type requestType) {
-      return m_requestHandler;
-    }    
+    protected RequestBodyParser getBodyParser() {
+      return getTestBodyParser();
+    }
+
+    @Override
+    protected ResultSerializer getResultSerializer() {
+      return getTestResultSerializer();
+    }
   }
 
 }

+ 18 - 16
ambari-server/src/test/java/org/apache/ambari/server/api/services/DeleteRequestTest.java

@@ -18,7 +18,9 @@
 
 package org.apache.ambari.server.api.services;
 
+import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.predicate.PredicateCompiler;
+import org.apache.ambari.server.api.resources.ResourceInstance;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
@@ -31,28 +33,28 @@ import javax.ws.rs.core.UriInfo;
  */
 public class DeleteRequestTest extends BaseRequestTest {
   @Test
-  public void testRequestType() {
-    assertSame(Request.Type.DELETE, new DeleteRequest(null, null, null, null).getRequestType());
+  public void testRequestType() throws Exception {
+    Request r = new DeleteRequest(null, new RequestBody(), null, null);
+    assertSame(Request.Type.DELETE, r.getRequestType());
   }
 
-  @Test
-  public void testGetQueryPredicate() throws Exception {
-    String uri = "http://foo.bar.com/api/v1/clusters?foo=bar&orProp1=5|orProp2!=6|orProp3<100&prop!=5&prop2>10&prop3>=20&prop4<500&prop5<=1&fields=field1,category/field2";
-    super.testGetQueryPredicate(uri);
-  }
-
-  @Test
-  public void testGetFields() {
-    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";
-    super.testGetFields(fields);
-  }
-
-  protected Request getTestRequest(HttpHeaders headers, String body, UriInfo uriInfo, final PredicateCompiler compiler) {
-    return new DeleteRequest(headers, body, uriInfo, null) {
+  protected Request getTestRequest(HttpHeaders headers, RequestBody body, UriInfo uriInfo, final PredicateCompiler compiler,
+                                   final RequestHandler handler, final ResultPostProcessor processor, ResourceInstance resource) {
+    return new DeleteRequest(headers, body, uriInfo, resource) {
       @Override
       protected PredicateCompiler getPredicateCompiler() {
         return compiler;
       }
+
+      @Override
+      protected RequestHandler getRequestHandler() {
+        return handler;
+      }
+
+      @Override
+      public ResultPostProcessor getResultPostProcessor() {
+        return processor;
+      }
     };
   }
 }

+ 18 - 15
ambari-server/src/test/java/org/apache/ambari/server/api/services/GetRequestTest.java

@@ -18,7 +18,9 @@
 
 package org.apache.ambari.server.api.services;
 
+import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.predicate.PredicateCompiler;
+import org.apache.ambari.server.api.resources.ResourceInstance;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
@@ -32,28 +34,29 @@ import javax.ws.rs.core.UriInfo;
 public class GetRequestTest extends BaseRequestTest {
 
   @Test
-  public void testRequestType() {
-    assertSame(Request.Type.GET, new GetRequest(null, null, null, null).getRequestType());
+  public void testRequestType() throws Exception {
+    Request r = new GetRequest(null, new RequestBody(), null, null);
+    assertSame(Request.Type.GET, r.getRequestType());
   }
 
-  @Test
-  public void testGetQueryPredicate() throws Exception {
-    String uri = "http://foo.bar.com/api/v1/clusters?foo=bar&orProp1=5|orProp2!=6|orProp3<100&prop!=5&prop2>10&prop3>=20&prop4<500&prop5<=1&fields=field1,category/field2";
-    super.testGetQueryPredicate(uri);
-  }
-
-  @Test
-  public void testGetFields() {
-    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";
-    super.testGetFields(fields);
-  }
 
-  protected Request getTestRequest(HttpHeaders headers, String body, UriInfo uriInfo, final PredicateCompiler compiler) {
-    return new GetRequest(headers, body, uriInfo, null) {
+  protected Request getTestRequest(HttpHeaders headers, RequestBody body, UriInfo uriInfo, final PredicateCompiler compiler,
+                                   final RequestHandler handler, final ResultPostProcessor processor, ResourceInstance resource) {
+    return new GetRequest(headers, body, uriInfo, resource) {
       @Override
       protected PredicateCompiler getPredicateCompiler() {
         return compiler;
       }
+
+      @Override
+      protected RequestHandler getRequestHandler() {
+        return handler;
+      }
+
+      @Override
+      public ResultPostProcessor getResultPostProcessor() {
+        return processor;
+      }
     };
   }
 }

+ 64 - 219
ambari-server/src/test/java/org/apache/ambari/server/api/services/HostComponentServiceTest.java

@@ -19,11 +19,16 @@
 
 package org.apache.ambari.server.api.services;
 
-import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.resources.ResourceInstance;
-import org.junit.Test;
+import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
+import org.apache.ambari.server.api.services.serializers.ResultSerializer;
 
-import javax.ws.rs.core.Response;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriInfo;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
 
 import static org.junit.Assert.assertEquals;
 
@@ -31,248 +36,88 @@ import static org.junit.Assert.assertEquals;
  * Unit tests for HostComponentService.
  */
 public class HostComponentServiceTest extends BaseServiceTest {
-  @Test
-  public void testGetHostComponent() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-    String hostComponentName = "hostComponentName";
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    //test
-    HostComponentService hostComponentService = new TestHostComponentService(getResource(), clusterName,
-        hostName, hostComponentName, getRequestFactory(), getRequestHandler());
-
-    Response response = hostComponentService.getHostComponent(getHttpHeaders(), getUriInfo(), hostComponentName);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetHostComponent__ErrorState() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-    String hostComponentName = "hostComponentName";
-
-    registerExpectations(Request.Type.GET, null, 404, true);
-    replayMocks();
-
-    //test
-    HostComponentService hostComponentService = new TestHostComponentService(getResource(), clusterName,
-        hostName, hostComponentName, getRequestFactory(), getRequestHandler());
-
-    Response response = hostComponentService.getHostComponent(getHttpHeaders(), getUriInfo(), hostComponentName);
-    verifyResults(response, 404);
-  }
-
-  @Test
-  public void testGetHostComponents() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    //test
-    HostComponentService hostComponentService = new TestHostComponentService(getResource(), clusterName,
-        hostName, null, getRequestFactory(), getRequestHandler());
-
-    Response response = hostComponentService.getHostComponents(getHttpHeaders(), getUriInfo());
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetHostComponents__ErrorState() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    //test
-    HostComponentService hostComponentService = new TestHostComponentService(getResource(), clusterName,
-        hostName, null, getRequestFactory(), getRequestHandler());
-
-    Response response = hostComponentService.getHostComponents(getHttpHeaders(), getUriInfo());
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testCreateHostComponent() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-    String hostComponentName = "hostComponentName";
-    String body = "body";
-
-    registerExpectations(Request.Type.POST, body, 201, false);
-    replayMocks();
-
-    //test
-    HostComponentService hostComponentService = new TestHostComponentService(getResource(), clusterName,
-        hostName, hostComponentName, getRequestFactory(), getRequestHandler());
-
-    Response response = hostComponentService.createHostComponent(body, getHttpHeaders(), getUriInfo(), hostComponentName);
-    verifyResults(response, 201);
-  }
-
-  @Test
-  public void testCreateHostComponent__ErrorState() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-    String hostComponentName = "hostComponentName";
-    String body = "body";
-
-    registerExpectations(Request.Type.POST, body, 500, true);
-    replayMocks();
-
-    //test
-    HostComponentService hostComponentService = new TestHostComponentService(getResource(), clusterName,
-        hostName, hostComponentName, getRequestFactory(), getRequestHandler());
-
-    Response response = hostComponentService.createHostComponent(body, getHttpHeaders(), getUriInfo(), hostComponentName);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testUpdateHostComponent() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-    String hostComponentName = "hostComponentName";
-    String body = "body";
 
-    registerExpectations(Request.Type.PUT, body, 200, false);
-    replayMocks();
-
-    //test
-    HostComponentService hostComponentService = new TestHostComponentService(getResource(), clusterName,
-        hostName, hostComponentName, getRequestFactory(), getRequestHandler());
-
-    Response response = hostComponentService.updateHostComponent(body, getHttpHeaders(), getUriInfo(), hostComponentName);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testUpdateHostComponent__ErrorState() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-    String hostComponentName = "hostComponentName";
-    String body = "body";
-
-    registerExpectations(Request.Type.PUT, body, 500, true);
-    replayMocks();
-
-    //test
-    HostComponentService hostComponentService = new TestHostComponentService(getResource(), clusterName,
-        hostName, hostComponentName, getRequestFactory(), getRequestHandler());
-
-    Response response = hostComponentService.updateHostComponent(body, getHttpHeaders(), getUriInfo(), hostComponentName);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testUpdateHostComponents() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-    String body = "body";
-
-    registerExpectations(Request.Type.PUT, body, 200, false);
-    replayMocks();
-
-    //test
-    HostComponentService hostComponentService = new TestHostComponentService(getResource(), clusterName,
-        hostName, null, getRequestFactory(), getRequestHandler());
-
-    Response response = hostComponentService.updateHostComponents(body, getHttpHeaders(), getUriInfo());
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testUpdateHostComponents__ErrorState() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-    String body = "body";
-
-    registerExpectations(Request.Type.PUT, body, 500, true);
-    replayMocks();
-
-    //test
-    HostComponentService hostComponentService = new TestHostComponentService(getResource(), clusterName,
-        hostName, null, getRequestFactory(), getRequestHandler());
-
-    Response response = hostComponentService.updateHostComponents(body, getHttpHeaders(), getUriInfo());
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testDeleteHostComponent() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-    String hostComponentName = "hostComponentName";
-
-    registerExpectations(Request.Type.DELETE, null, 200, false);
-    replayMocks();
-
-    //test
-    HostComponentService hostComponentService = new TestHostComponentService(getResource(), clusterName,
-        hostName, hostComponentName, getRequestFactory(), getRequestHandler());
-
-    Response response = hostComponentService.deleteHostComponent(getHttpHeaders(), getUriInfo(), hostComponentName);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testDeleteHostComponent__ErrorState() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-    String hostComponentName = "hostComponentName";
-
-    registerExpectations(Request.Type.DELETE, null, 500, true);
-    replayMocks();
-
-    //test
-    HostComponentService hostComponentService = new TestHostComponentService(getResource(), clusterName,
-        hostName, hostComponentName, getRequestFactory(), getRequestHandler());
-
-    Response response = hostComponentService.deleteHostComponent(getHttpHeaders(), getUriInfo(), hostComponentName);
-    verifyResults(response, 500);
+  public List<ServiceTestInvocation> getTestInvocations() throws Exception {
+    List<ServiceTestInvocation> listInvocations = new ArrayList<ServiceTestInvocation>();
+
+    //getHostComponent
+    HostComponentService componentService = new TestHostComponentService("clusterName", "serviceName", "componentName");
+    Method m = componentService.getClass().getMethod("getHostComponent", HttpHeaders.class, UriInfo.class, String.class);
+    Object[] args = new Object[] {getHttpHeaders(), getUriInfo(), "componentName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, componentService, m, args, null));
+
+    //getHostComponents
+    componentService = new TestHostComponentService("clusterName", "serviceName", null);
+    m = componentService.getClass().getMethod("getHostComponents", HttpHeaders.class, UriInfo.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, componentService, m, args, null));
+
+    //createHostComponent
+    componentService = new TestHostComponentService("clusterName", "serviceName", "componentName");
+    m = componentService.getClass().getMethod("createHostComponent", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "componentName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, componentService, m, args, "body"));
+
+    //createHostComponents
+    componentService = new TestHostComponentService("clusterName", "serviceName", null);
+    m = componentService.getClass().getMethod("createHostComponents", String.class, HttpHeaders.class, UriInfo.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, componentService, m, args, "body"));
+
+    //updateHostComponent
+    componentService = new TestHostComponentService("clusterName", "serviceName", "componentName");
+    m = componentService.getClass().getMethod("updateHostComponent", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "componentName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.PUT, componentService, m, args, "body"));
+
+    //updateHostComponents
+    componentService = new TestHostComponentService("clusterName", "serviceName", null);
+    m = componentService.getClass().getMethod("updateHostComponents", String.class, HttpHeaders.class, UriInfo.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.PUT, componentService, m, args, "body"));
+
+    //deleteHostComponent
+    componentService = new TestHostComponentService("clusterName", "serviceName", "componentName");
+    m = componentService.getClass().getMethod("deleteHostComponent", HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo(), "componentName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.DELETE, componentService, m, args, null));
+
+    return listInvocations;
   }
 
   private class TestHostComponentService extends HostComponentService {
-    private RequestFactory m_requestFactory;
-    private RequestHandler m_requestHandler;
-    private ResourceInstance m_resourceDef;
     private String m_clusterId;
     private String m_hostId;
     private String m_hostComponentId;
 
-    private TestHostComponentService(ResourceInstance resourceDef, String clusterId, String hostId, String hostComponentId,
-                                     RequestFactory requestFactory, RequestHandler handler) {
+    private TestHostComponentService(String clusterId, String hostId, String hostComponentId) {
       super(clusterId, hostId);
-      m_resourceDef = resourceDef;
       m_clusterId = clusterId;
       m_hostId = hostId;
       m_hostComponentId = hostComponentId;
-      m_requestFactory = requestFactory;
-      m_requestHandler = handler;
     }
 
-
     @Override
     ResourceInstance createHostComponentResource(String clusterName, String hostName, String hostComponentName) {
       assertEquals(m_clusterId, clusterName);
       assertEquals(m_hostId, hostName);
       assertEquals(m_hostComponentId, hostComponentName);
-      return m_resourceDef;
+      return getTestResource();
     }
 
     @Override
     RequestFactory getRequestFactory() {
-      return m_requestFactory;
+      return getTestRequestFactory();
+    }
+
+    @Override
+    protected RequestBodyParser getBodyParser() {
+      return getTestBodyParser();
     }
 
     @Override
-    RequestHandler getRequestHandler(Request.Type requestType) {
-      return m_requestHandler;
+    protected ResultSerializer getResultSerializer() {
+      return getTestResultSerializer();
     }
   }
 

+ 64 - 242
ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java

@@ -19,13 +19,17 @@
 
 package org.apache.ambari.server.api.services;
 
-
-import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.resources.ResourceInstance;
-import org.junit.Test;
-import javax.ws.rs.core.Response;
+import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
+import org.apache.ambari.server.api.services.serializers.ResultSerializer;
+
+import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.UriInfo;
 
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
 import static org.junit.Assert.assertEquals;
 
 /**
@@ -33,266 +37,84 @@ import static org.junit.Assert.assertEquals;
  */
 public class HostServiceTest extends BaseServiceTest {
 
-  @Test
-  public void testGetHost() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    //test
-    HostService hostService = new TestHostService(getResource(), clusterName, hostName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.getHost(getHttpHeaders(), getUriInfo(), hostName);
-
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetHost__ErrorState() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    //test
-    HostService hostService = new TestHostService(getResource(), clusterName, hostName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.getHost(getHttpHeaders(), getUriInfo(), hostName);
-
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testGetHosts() {
-    String clusterName = "clusterName";
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    //test
-    HostService hostService = new TestHostService(getResource(), clusterName, null, getRequestFactory(), getRequestHandler());
-    Response response = hostService.getHosts(getHttpHeaders(), getUriInfo());
-
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetHosts__ErrorState() {
-    String clusterName = "clusterName";
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    //test
-    HostService hostService = new TestHostService(getResource(), clusterName, null, getRequestFactory(), getRequestHandler());
-    Response response = hostService.getHosts(getHttpHeaders(), getUriInfo());
-
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testCreateHost() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-    String body = "body";
-
-    registerExpectations(Request.Type.POST, body, 201, false);
-    replayMocks();
-
-    //test
-    HostService hostService = new TestHostService(getResource(), clusterName, hostName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.createHost(body, getHttpHeaders(), getUriInfo(), hostName);
-
-    verifyResults(response, 201);
+  public List<ServiceTestInvocation> getTestInvocations() throws Exception {
+    List<ServiceTestInvocation> listInvocations = new ArrayList<ServiceTestInvocation>();
+
+    //getHost
+    HostService service = new TestHostService("clusterName", "hostName");
+    Method m = service.getClass().getMethod("getHost", HttpHeaders.class, UriInfo.class, String.class);
+    Object[] args = new Object[] {getHttpHeaders(), getUriInfo(), "hostName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
+
+    //getHosts
+    service = new TestHostService("clusterName", null);
+    m = service.getClass().getMethod("getHosts", HttpHeaders.class, UriInfo.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
+
+    //createHost
+    service = new TestHostService("clusterName", "hostName");
+    m = service.getClass().getMethod("createHost", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "hostName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, service, m, args, "body"));
+
+    //createHosts
+    service = new TestHostService("clusterName", null);
+    m = service.getClass().getMethod("createHosts", String.class, HttpHeaders.class, UriInfo.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, service, m, args, "body"));
+
+    //updateHost
+    service = new TestHostService("clusterName", "hostName");
+    m = service.getClass().getMethod("updateHost", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "hostName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.PUT, service, m, args, "body"));
+
+    //updateHosts
+    service = new TestHostService("clusterName", null);
+    m = service.getClass().getMethod("updateHosts", String.class, HttpHeaders.class, UriInfo.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.PUT, service, m, args, "body"));
+
+    //deleteHost
+    service = new TestHostService("clusterName", "hostName");
+    m = service.getClass().getMethod("deleteHost", HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo(), "hostName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.DELETE, service, m, args, null));
+
+    return listInvocations;
   }
 
-  @Test
-  public void testCreateHost__ErrorState() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-    String body = "body";
-
-    registerExpectations(Request.Type.POST, body, 500, true);
-    replayMocks();
-
-    //test
-    HostService hostService = new TestHostService(getResource(), clusterName, hostName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.createHost(body, getHttpHeaders(), getUriInfo(), hostName);
-
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testCreateHosts()  {
-    String clusterName = "clusterName";
-    String body = "[ " +
-        "{\"Hosts\" : {" +
-        "            \"cluster_name\" : \"mycluster\"," +
-        "            \"host_name\" : \"host1\"" +
-        "          }" +
-        "}," +
-        "{\"Hosts\" : {" +
-        "            \"cluster_name\" : \"mycluster\"," +
-        "            \"host_name\" : \"host2\"" +
-        "          }" +
-        "}," +
-        "{\"Hosts\" : {" +
-        "            \"cluster_name\" : \"mycluster\"," +
-        "            \"host_name\" : \"host3\"" +
-        "          }" +
-        "}]";
-
-    registerExpectations(Request.Type.POST, body, 201, false);
-    replayMocks();
-
-    //test
-    HostService hostService = new TestHostService(getResource(), clusterName, null, getRequestFactory(), getRequestHandler());
-    Response response = hostService.createHosts(body, getHttpHeaders(), getUriInfo());
-
-    verifyResults(response, 201);
-  }
-
-  @Test
-  public void testCreateHosts__ErrorState()  {
-    String clusterName = "clusterName";
-    String body = "body";
-
-    registerExpectations(Request.Type.POST, body, 500, true);
-    replayMocks();
-
-    //test
-    HostService hostService = new TestHostService(getResource(), clusterName, null, getRequestFactory(), getRequestHandler());
-    Response response = hostService.createHosts(body, getHttpHeaders(), getUriInfo());
-
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testUpdateHost() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-    String body = "body";
-
-    registerExpectations(Request.Type.PUT, body, 200, false);
-    replayMocks();
-
-    //test
-    HostService hostService = new TestHostService(getResource(), clusterName, hostName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.updateHost(body, getHttpHeaders(), getUriInfo(), hostName);
-
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testUpdateHost__ErrorState() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-    String body = "body";
-
-    registerExpectations(Request.Type.PUT, body, 500, true);
-    replayMocks();
-
-    //test
-    HostService hostService = new TestHostService(getResource(), clusterName, hostName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.updateHost(body, getHttpHeaders(), getUriInfo(), hostName);
-
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testUpdateHosts() {
-    String clusterName = "clusterName";
-    String body = "body";
-
-    registerExpectations(Request.Type.PUT, body, 200, false);
-    replayMocks();
-
-    //test
-    HostService hostService = new TestHostService(getResource(), clusterName, null, getRequestFactory(), getRequestHandler());
-    Response response = hostService.updateHosts(body, getHttpHeaders(), getUriInfo());
-
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testUpdateHosts__ErrorState() {
-    String clusterName = "clusterName";
-    String body = "body";
-
-    registerExpectations(Request.Type.PUT, body, 500, true);
-    replayMocks();
-
-    //test
-    HostService hostService = new TestHostService(getResource(), clusterName, null, getRequestFactory(), getRequestHandler());
-    Response response = hostService.updateHosts(body, getHttpHeaders(), getUriInfo());
-
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testDeleteHost() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-
-    registerExpectations(Request.Type.DELETE, null, 200, false);
-    replayMocks();
-
-    //test
-    HostService hostService = new TestHostService(getResource(), clusterName, hostName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.deleteHost(getHttpHeaders(), getUriInfo(), hostName);
-
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testDeleteHost__ErrorState() {
-    String clusterName = "clusterName";
-    String hostName = "hostName";
-
-    registerExpectations(Request.Type.DELETE, null, 500, true);
-    replayMocks();
-
-    //test
-    HostService hostService = new TestHostService(getResource(), clusterName, hostName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.deleteHost(getHttpHeaders(), getUriInfo(), hostName);
-
-    verifyResults(response, 500);
-  }
-
-
   private class TestHostService extends HostService {
-    private RequestFactory m_requestFactory;
-    private RequestHandler m_requestHandler;
-    private ResourceInstance m_resourceDef;
     private String m_clusterId;
     private String m_hostId;
 
-    private TestHostService(ResourceInstance resourceDef, String clusterId, String hostId, RequestFactory requestFactory,
-                            RequestHandler handler) {
+    private TestHostService(String clusterId, String hostId) {
       super(clusterId);
-      m_resourceDef = resourceDef;
       m_clusterId = clusterId;
       m_hostId = hostId;
-      m_requestFactory = requestFactory;
-      m_requestHandler = handler;
     }
 
     @Override
     ResourceInstance createHostResource(String clusterName, String hostName, UriInfo ui) {
       assertEquals(m_clusterId, clusterName);
       assertEquals(m_hostId, hostName);
-      return m_resourceDef;
+      return getTestResource();
     }
 
     @Override
     RequestFactory getRequestFactory() {
-      return m_requestFactory;
+      return getTestRequestFactory();
+    }
+
+    @Override
+    protected RequestBodyParser getBodyParser() {
+      return getTestBodyParser();
     }
 
     @Override
-    RequestHandler getRequestHandler(Request.Type requestType) {
-      return m_requestHandler;
+    protected ResultSerializer getResultSerializer() {
+      return getTestResultSerializer();
     }
   }
 }

+ 68 - 0
ambari-server/src/test/java/org/apache/ambari/server/api/services/NamedPropertySetTest.java

@@ -0,0 +1,68 @@
+/**
+ * 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.api.services;
+
+import org.junit.Test;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.*;
+
+/**
+ * NamedPropertySet unit tests.
+ */
+public class NamedPropertySetTest {
+
+  @Test
+  public void testGetters() {
+    Map<String, Object> mapProps = new HashMap<String, Object>();
+    mapProps.put("foo", "bar");
+
+    NamedPropertySet propertySet = new NamedPropertySet("foo", mapProps);
+    assertEquals("foo", propertySet.getName());
+    assertEquals(mapProps, propertySet.getProperties());
+  }
+
+  @Test
+  public void testEquals() {
+    Map<String, Object> mapProps = new HashMap<String, Object>();
+    mapProps.put("foo", "bar");
+
+    NamedPropertySet propertySet = new NamedPropertySet("foo", mapProps);
+    NamedPropertySet propertySet2 = new NamedPropertySet("foo", mapProps);
+
+    assertEquals(propertySet, propertySet2);
+
+    NamedPropertySet propertySet3 = new NamedPropertySet("bar", mapProps);
+    assertFalse(propertySet.equals(propertySet3));
+
+    NamedPropertySet propertySet4 = new NamedPropertySet("foo", new HashMap<String, Object>());
+    assertFalse(propertySet.equals(propertySet4));
+  }
+
+  @Test
+  public void testHashCode() {
+    Map<String, Object> mapProps = new HashMap<String, Object>();
+
+    NamedPropertySet propertySet = new NamedPropertySet("foo", mapProps);
+    NamedPropertySet propertySet2 = new NamedPropertySet("foo", mapProps);
+
+    assertEquals(propertySet.hashCode(), propertySet2.hashCode());
+  }
+}

+ 18 - 16
ambari-server/src/test/java/org/apache/ambari/server/api/services/PostRequestTest.java

@@ -18,7 +18,9 @@
 
 package org.apache.ambari.server.api.services;
 
+import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.predicate.PredicateCompiler;
+import org.apache.ambari.server.api.resources.ResourceInstance;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
@@ -31,28 +33,28 @@ import javax.ws.rs.core.UriInfo;
  */
 public class PostRequestTest extends BaseRequestTest {
   @Test
-  public void testRequestType() {
-    assertSame(Request.Type.POST, new PostRequest(null, null, null, null).getRequestType());
+  public void testRequestType() throws Exception {
+    Request r = new PostRequest(null, new RequestBody(), null, null);
+    assertSame(Request.Type.POST, r.getRequestType());
   }
 
-  @Test
-  public void testGetQueryPredicate() throws Exception {
-    String uri = "http://foo.bar.com/api/v1/clusters?foo=bar&orProp1=5|orProp2!=6|orProp3<100&prop!=5&prop2>10&prop3>=20&prop4<500&prop5<=1&fields=field1,category/field2";
-    super.testGetQueryPredicate(uri);
-  }
-
-  @Test
-  public void testGetFields() {
-    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";
-    super.testGetFields(fields);
-  }
-
-  protected Request getTestRequest(HttpHeaders headers, String body, UriInfo uriInfo, final PredicateCompiler compiler) {
-    return new PostRequest(headers, body, uriInfo, null) {
+  protected Request getTestRequest(HttpHeaders headers, RequestBody body, UriInfo uriInfo, final PredicateCompiler compiler,
+                                   final RequestHandler handler, final ResultPostProcessor processor, ResourceInstance resource) {
+    return new PostRequest(headers, body, uriInfo, resource) {
       @Override
       protected PredicateCompiler getPredicateCompiler() {
         return compiler;
       }
+
+      @Override
+      protected RequestHandler getRequestHandler() {
+        return handler;
+      }
+
+      @Override
+      public ResultPostProcessor getResultPostProcessor() {
+        return processor;
+      }
     };
   }
 }

+ 18 - 16
ambari-server/src/test/java/org/apache/ambari/server/api/services/PutRequestTest.java

@@ -18,7 +18,9 @@
 
 package org.apache.ambari.server.api.services;
 
+import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.predicate.PredicateCompiler;
+import org.apache.ambari.server.api.resources.ResourceInstance;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
@@ -31,28 +33,28 @@ import javax.ws.rs.core.UriInfo;
  */
 public class PutRequestTest extends BaseRequestTest {
   @Test
-  public void testRequestType() {
-    assertSame(Request.Type.PUT, new PutRequest(null, null, null, null).getRequestType());
+  public void testRequestType() throws Exception {
+    Request r = new PutRequest(null, new RequestBody(), null, null);
+    assertSame(Request.Type.PUT, r.getRequestType());
   }
 
-  @Test
-  public void testGetQueryPredicate() throws Exception {
-    String uri = "http://foo.bar.com/api/v1/clusters?foo=bar&orProp1=5|orProp2!=6|orProp3<100&prop!=5&prop2>10&prop3>=20&prop4<500&prop5<=1&fields=field1,category/field2";
-    super.testGetQueryPredicate(uri);
-  }
-
-  @Test
-  public void testGetFields() {
-    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";
-    super.testGetFields(fields);
-  }
-
-  protected Request getTestRequest(HttpHeaders headers, String body, UriInfo uriInfo, final PredicateCompiler compiler) {
-    return new PutRequest(headers, body, uriInfo, null) {
+  protected Request getTestRequest(HttpHeaders headers, RequestBody body, UriInfo uriInfo, final PredicateCompiler compiler,
+                                   final RequestHandler handler, final ResultPostProcessor processor, ResourceInstance resource) {
+    return new PutRequest(headers, body, uriInfo, resource) {
       @Override
       protected PredicateCompiler getPredicateCompiler() {
         return compiler;
       }
+
+      @Override
+      protected RequestHandler getRequestHandler() {
+        return handler;
+      }
+
+      @Override
+      public ResultPostProcessor getResultPostProcessor() {
+        return processor;
+      }
     };
   }
 }

+ 65 - 0
ambari-server/src/test/java/org/apache/ambari/server/api/services/QueryPostRequestTest.java

@@ -0,0 +1,65 @@
+/**
+ * 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.api.services;
+
+import org.apache.ambari.server.api.handlers.RequestHandler;
+import org.apache.ambari.server.api.predicate.PredicateCompiler;
+import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.api.services.serializers.ResultSerializer;
+import org.junit.Test;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriInfo;
+
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * QueryPostRequest unit tests.
+ */
+public class QueryPostRequestTest extends BaseRequestTest {
+  @Test
+  public void testRequestType() throws Exception {
+    Request r = new QueryPostRequest(null, new RequestBody(), null, null);
+    assertSame(Request.Type.QUERY_POST, r.getRequestType());
+  }
+
+  protected Request getTestRequest(HttpHeaders headers, RequestBody body, UriInfo uriInfo, final PredicateCompiler compiler,
+                                   final RequestHandler handler, final ResultPostProcessor processor, ResourceInstance resource) {
+    return new QueryPostRequest(headers, body, uriInfo, resource) {
+      @Override
+      protected PredicateCompiler getPredicateCompiler() {
+        return compiler;
+      }
+
+      @Override
+      protected RequestHandler getRequestHandler() {
+        return handler;
+      }
+
+      @Override
+      public ResultPostProcessor getResultPostProcessor() {
+        return processor;
+      }
+    };
+  }
+}

+ 63 - 0
ambari-server/src/test/java/org/apache/ambari/server/api/services/RequestBodyTest.java

@@ -0,0 +1,63 @@
+/**
+ * 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.api.services;
+
+import org.junit.Test;
+import java.util.HashMap;
+import static org.junit.Assert.*;
+
+/**
+ * RequestBody unit tests.
+ */
+public class RequestBodyTest {
+
+  @Test
+  public void testSetGetQueryString() {
+    RequestBody body = new RequestBody();
+    assertNull(body.getQueryString());
+    body.setQueryString("foo=bar");
+    assertEquals("foo=bar", body.getQueryString());
+  }
+
+  @Test
+  public void testSetGetPartialResponseFields() {
+    RequestBody body = new RequestBody();
+    assertNull(body.getPartialResponseFields());
+    body.setPartialResponseFields("foo,bar");
+    assertEquals("foo,bar", body.getPartialResponseFields());
+  }
+
+  @Test
+  public void testAddGetPropertySets() {
+    RequestBody body = new RequestBody();
+    assertEquals(0, body.getPropertySets().size());
+    NamedPropertySet ps = new NamedPropertySet("foo", new HashMap<String, Object>());
+    body.addPropertySet(ps);
+    assertEquals(1, body.getPropertySets().size());
+    assertSame(ps, body.getPropertySets().iterator().next());
+  }
+
+  @Test
+  public void testSetGetBody() {
+    RequestBody body = new RequestBody();
+    assertNull(body.getBody());
+    body.setBody("{\"foo\" : \"value\" }");
+    assertEquals("{\"foo\" : \"value\" }", body.getBody());
+  }
+}

+ 64 - 195
ambari-server/src/test/java/org/apache/ambari/server/api/services/ServiceServiceTest.java

@@ -18,11 +18,16 @@
 
 package org.apache.ambari.server.api.services;
 
-import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.resources.ResourceInstance;
-import org.junit.Test;
+import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
+import org.apache.ambari.server.api.services.serializers.ResultSerializer;
 
-import javax.ws.rs.core.Response;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriInfo;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
 
 import static org.junit.Assert.assertEquals;
 
@@ -31,220 +36,84 @@ import static org.junit.Assert.assertEquals;
  */
 public class ServiceServiceTest extends BaseServiceTest {
 
-  @Test
-  public void testGetService() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    //test
-    ServiceService hostService = new TestServiceService(getResource(), clusterName, serviceName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.getService(getHttpHeaders(), getUriInfo(), serviceName);
-
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetService__ErrorState() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    //test
-    ServiceService hostService = new TestServiceService(getResource(), clusterName, serviceName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.getService(getHttpHeaders(), getUriInfo(), serviceName);
-
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testGetServices()  {
-    String clusterName = "clusterName";
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    //test
-    ServiceService hostService = new TestServiceService(getResource(), clusterName, null, getRequestFactory(), getRequestHandler());
-    Response response = hostService.getServices(getHttpHeaders(), getUriInfo());
-
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetServices__ErrorState(){
-    String clusterName = "clusterName";
-
-    registerExpectations(Request.Type.GET, null, 400, false);
-    replayMocks();
-
-    //test
-    ServiceService hostService = new TestServiceService(getResource(), clusterName, null, getRequestFactory(), getRequestHandler());
-    Response response = hostService.getServices(getHttpHeaders(), getUriInfo());
-
-    verifyResults(response, 400);
-  }
-
-  @Test
-  public void testCreateService() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-    String body = "{body}";
-
-    registerExpectations(Request.Type.POST, body, 201, false);
-    replayMocks();
-
-    //test
-    ServiceService hostService = new TestServiceService(getResource(), clusterName, serviceName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.createService(body, getHttpHeaders(), getUriInfo(), serviceName);
-
-    verifyResults(response, 201);
-  }
-
-  @Test
-  public void testCreateService__ErrorState() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-    String body = "{body}";
-
-    registerExpectations(Request.Type.POST, body, 500, true);
-    replayMocks();
-
-    //test
-    ServiceService hostService = new TestServiceService(getResource(), clusterName, serviceName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.createService(body, getHttpHeaders(), getUriInfo(), serviceName);
-
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testUpdateServices() {
-    String clusterName = "clusterName";
-    String body = "{body}";
-
-    registerExpectations(Request.Type.PUT, body, 200, false);
-    replayMocks();
-
-    //test
-    ServiceService hostService = new TestServiceService(getResource(), clusterName, null, getRequestFactory(), getRequestHandler());
-    Response response = hostService.updateServices(body, getHttpHeaders(), getUriInfo());
-
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testUpdateServices__ErrorState() {
-    String clusterName = "clusterName";
-    String body = "{body}";
-
-    registerExpectations(Request.Type.PUT, body, 500, true);
-    replayMocks();
-
-    //test
-    ServiceService hostService = new TestServiceService(getResource(), clusterName, null, getRequestFactory(), getRequestHandler());
-    Response response = hostService.updateServices(body, getHttpHeaders(), getUriInfo());
-
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testUpdateService() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-    String body = "{body}";
-
-    registerExpectations(Request.Type.PUT, body, 200, false);
-    replayMocks();
-
-    //test
-    ServiceService hostService = new TestServiceService(getResource(), clusterName, serviceName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.updateService(body, getHttpHeaders(), getUriInfo(), serviceName);
-
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testUpdateService__ErrorState() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-    String body = "{body}";
-
-    registerExpectations(Request.Type.PUT, body, 500, true);
-    replayMocks();
-
-    //test
-    ServiceService hostService = new TestServiceService(getResource(), clusterName, serviceName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.updateService(body, getHttpHeaders(), getUriInfo(), serviceName);
-
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testDeleteService() {
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-
-    registerExpectations(Request.Type.DELETE, null, 200, false);
-    replayMocks();
-
-    //test
-    ServiceService hostService = new TestServiceService(getResource(), clusterName, serviceName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.deleteService(getHttpHeaders(), getUriInfo(), serviceName);
-
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testDeleteService__ErrorState(){
-    String clusterName = "clusterName";
-    String serviceName = "serviceName";
-
-    registerExpectations(Request.Type.DELETE, null, 500, true);
-    replayMocks();
-
-    //test
-    ServiceService hostService = new TestServiceService(getResource(), clusterName, serviceName, getRequestFactory(), getRequestHandler());
-    Response response = hostService.deleteService(getHttpHeaders(), getUriInfo(), serviceName);
-
-    verifyResults(response, 500);
+  public List<ServiceTestInvocation> getTestInvocations() throws Exception {
+    List<ServiceTestInvocation> listInvocations = new ArrayList<ServiceTestInvocation>();
+
+    //getService
+    ServiceService service = new TestServiceService("clusterName", "serviceName");
+    Method m = service.getClass().getMethod("getService", HttpHeaders.class, UriInfo.class, String.class);
+    Object[] args = new Object[] {getHttpHeaders(), getUriInfo(), "serviceName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
+
+    //getService
+    service = new TestServiceService("clusterName", null);
+    m = service.getClass().getMethod("getServices", HttpHeaders.class, UriInfo.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
+
+    //createService
+    service = new TestServiceService("clusterName", "serviceName");
+    m = service.getClass().getMethod("createService", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "serviceName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, service, m, args, "body"));
+
+    //createServices
+    service = new TestServiceService("clusterName", null);
+    m = service.getClass().getMethod("createServices", String.class, HttpHeaders.class, UriInfo.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, service, m, args, "body"));
+
+    //updateServices
+    service = new TestServiceService("clusterName", "serviceName");
+    m = service.getClass().getMethod("updateService", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "serviceName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.PUT, service, m, args, "body"));
+
+    //updateServices
+    service = new TestServiceService("clusterName", null);
+    m = service.getClass().getMethod("updateServices", String.class, HttpHeaders.class, UriInfo.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.PUT, service, m, args, "body"));
+
+    //deleteServices
+    service = new TestServiceService("clusterName", "serviceName");
+    m = service.getClass().getMethod("deleteService", HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo(), "serviceName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.DELETE, service, m, args, null));
+
+    return listInvocations;
   }
 
   private class TestServiceService extends ServiceService {
-    private RequestFactory m_requestFactory;
-    private RequestHandler m_requestHandler;
-    private ResourceInstance m_resourceDef;
     private String m_clusterId;
     private String m_serviceId;
 
-    private TestServiceService(ResourceInstance resourceDef, String clusterId, String serviceId, RequestFactory requestFactory,
-                               RequestHandler handler) {
+    private TestServiceService(String clusterId, String serviceId) {
       super(clusterId);
-      m_resourceDef = resourceDef;
       m_clusterId = clusterId;
       m_serviceId = serviceId;
-      m_requestFactory = requestFactory;
-      m_requestHandler = handler;
     }
 
     @Override
     ResourceInstance createServiceResource(String clusterName, String serviceName) {
       assertEquals(m_clusterId, clusterName);
       assertEquals(m_serviceId, serviceName);
-      return m_resourceDef;
+      return getTestResource();
     }
 
     @Override
     RequestFactory getRequestFactory() {
-      return m_requestFactory;
+      return getTestRequestFactory();
+    }
+
+    @Override
+    protected RequestBodyParser getBodyParser() {
+      return getTestBodyParser();
     }
 
     @Override
-    RequestHandler getRequestHandler(Request.Type requestType) {
-      return m_requestHandler;
+    protected ResultSerializer getResultSerializer() {
+      return getTestResultSerializer();
     }
   }
 }

+ 63 - 428
ambari-server/src/test/java/org/apache/ambari/server/api/services/StacksServiceTest.java

@@ -18,487 +18,122 @@
 
 package org.apache.ambari.server.api.services;
 
-import org.apache.ambari.server.api.handlers.RequestHandler;
 import org.apache.ambari.server.api.resources.ResourceInstance;
-import org.junit.Test;
+import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
+import org.apache.ambari.server.api.services.serializers.ResultSerializer;
 
-import javax.ws.rs.core.Response;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriInfo;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
 
-/**
- * Unit tests for StacksService.
- */
-public class StacksServiceTest extends BaseServiceTest {
-
-  private static final String STACK_NAME = "stackName";
-  private static final String STACK_VERSION = "stackVersion";
-  private static final String OS_TYPE = "osType";
-  private static final String REPO_ID = "repoId";
-  private static final String SERVICE_NAME = "serviceName";
-  private static final String PROPERTY_NAME = "propertyName";
-  private static final String COMPONENT_NAME = "componentName";
-
-  @Test
-  public void testGetStacks() {
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getStacks(getHttpHeaders(), getUriInfo());
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetStacks__ErrorState() {
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getStacks(getHttpHeaders(), getUriInfo());
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testGetStack() {
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getStack(getHttpHeaders(), getUriInfo(),
-        STACK_NAME);
-    verifyResults(response, 200);
-  }
 
-  @Test
-  public void testGetStack__ErrorState() {
+import static org.junit.Assert.assertEquals;
 
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getStack(getHttpHeaders(), getUriInfo(),
-        STACK_NAME);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testGetStackVersion() {
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-
-    Response response = stacksService.getStackVersion(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetStackVersion__ErrorState() {
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getStackVersion(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testGetStackVersions() {
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-
-    Response response = stacksService.getStackVersions(getHttpHeaders(),
-        getUriInfo(), STACK_NAME);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetStackVersions__ErrorState() {
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getStackVersions(getHttpHeaders(),
-        getUriInfo(), STACK_NAME);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testGetRepositories() {
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-
-    Response response = stacksService.getRepositories(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_NAME, OS_TYPE);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetRepositories__ErrorState() {
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getRepositories(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION, OS_TYPE);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testGetRepository() {
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-
-    Response response = stacksService.getRepository(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_NAME, OS_TYPE, REPO_ID);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetRepository__ErrorState() {
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getRepository(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION, OS_TYPE, REPO_ID);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testGetStackServices() {
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-
-    Response response = stacksService.getStackServices(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetStackServices__ErrorState() {
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getStackServices(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testGetStackService() {
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-
-    Response response = stacksService.getStackService(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION, SERVICE_NAME);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetStackService__ErrorState() {
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getStackService(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION, SERVICE_NAME);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testGetStackConfigurations() {
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-
-    Response response = stacksService.getStackConfigurations(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION, SERVICE_NAME);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetStackConfigurations__ErrorState() {
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getStackConfigurations(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION, SERVICE_NAME);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testGetStackConfiguration() {
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-
-    Response response = stacksService.getStackConfiguration(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION, SERVICE_NAME, PROPERTY_NAME);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetStackConfiguration__ErrorState() {
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getStackConfiguration(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION, SERVICE_NAME, PROPERTY_NAME);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testGetServiceComponent() {
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-
-    Response response = stacksService.getServiceComponent(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION, SERVICE_NAME, COMPONENT_NAME);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetServiceComponent__ErrorState() {
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getServiceComponent(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION, SERVICE_NAME, COMPONENT_NAME);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testGetServiceComponents() {
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-
-    Response response = stacksService.getStackConfiguration(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION, SERVICE_NAME, PROPERTY_NAME);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetServiceComponents__ErrorState() {
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getStackConfiguration(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION, SERVICE_NAME, PROPERTY_NAME);
-    verifyResults(response, 500);
-  }
-
-  @Test
-  public void testGetOperatingSystems() {
-
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-
-    Response response = stacksService.getOperatingSystems(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION);
-    verifyResults(response, 200);
-  }
-
-  @Test
-  public void testGetOperatingSystems__ErrorState() {
-
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
-
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getOperatingSystems(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION);
-    verifyResults(response, 500);
-  }
+/**
+* Unit tests for StacksService.
+*/
+public class StacksServiceTest extends BaseServiceTest {
 
-  @Test
-  public void testGetOperatingSystem() {
+  @Override
+  public List<ServiceTestInvocation> getTestInvocations() throws Exception {
+    List<ServiceTestInvocation> listInvocations = new ArrayList<ServiceTestInvocation>();
 
-    registerExpectations(Request.Type.GET, null, 200, false);
-    replayMocks();
+    //getStack
+    StacksService service = new TestStacksService("stackName", null);
+    Method m = service.getClass().getMethod("getStack", HttpHeaders.class, UriInfo.class, String.class);
+    Object[] args = new Object[] {getHttpHeaders(), getUriInfo(), "stackName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
 
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
+    //getStacks
+    service = new TestStacksService(null, null);
+    m = service.getClass().getMethod("getStacks", HttpHeaders.class, UriInfo.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
 
-    Response response = stacksService.getOperatingSystem(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION, OS_TYPE);
-    verifyResults(response, 200);
-  }
+    //getStackVersion
+    service = new TestStacksService("stackName", "stackVersion");
+    m = service.getClass().getMethod("getStackVersion", HttpHeaders.class, UriInfo.class, String.class, String.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo(), "stackName", "stackVersion"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
 
-  @Test
-  public void testGetOperatingSystem__ErrorState() {
+    //getStackVersions
+    service = new TestStacksService("stackName", null);
+    m = service.getClass().getMethod("getStackVersions", HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo(), "stackName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null));
 
-    registerExpectations(Request.Type.GET, null, 500, true);
-    replayMocks();
+    //todo: other methods
 
-    // test
-    StacksService stacksService = new TestStacksService(getResource(),
-        getRequestFactory(), getRequestHandler());
-    Response response = stacksService.getOperatingSystem(getHttpHeaders(),
-        getUriInfo(), STACK_NAME, STACK_VERSION, OS_TYPE);
-    verifyResults(response, 500);
+    return listInvocations;
   }
 
   private class TestStacksService extends StacksService {
-    private RequestFactory m_requestFactory;
-    private RequestHandler m_requestHandler;
-    private ResourceInstance m_resourceDef;
 
-    private TestStacksService(ResourceInstance resourceDef,
-        RequestFactory requestFactory, RequestHandler handler) {
-      m_resourceDef = resourceDef;
-      m_requestFactory = requestFactory;
-      m_requestHandler = handler;
+    private String m_stackId;
+    private String m_stackVersion;
+
+    private TestStacksService(String stackName, String stackVersion) {
+      m_stackId = stackName;
+      m_stackVersion = stackVersion;
     }
 
     @Override
     ResourceInstance createStackResource(String stackName) {
-      return m_resourceDef;
+      assertEquals(m_stackId, stackName);
+      return getTestResource();
     }
 
     @Override
-    ResourceInstance createStackVersionResource(String stackName,
-        String stackVersion) {
-      return m_resourceDef;
+    ResourceInstance createStackVersionResource(String stackName, String stackVersion) {
+      assertEquals(m_stackId, stackName);
+      assertEquals(m_stackVersion, stackVersion);
+      return getTestResource();
     }
 
     @Override
     ResourceInstance createRepositoryResource(String stackName,
         String stackVersion, String osType, String repoId) {
-      return m_resourceDef;
+      return getTestResource();
     }
 
     @Override
     ResourceInstance createStackServiceResource(String stackName,
         String stackVersion, String serviceName) {
-      return m_resourceDef;
+      return getTestResource();
     }
 
     ResourceInstance createStackConfigurationResource(String stackName,
         String stackVersion, String serviceName, String propertyName) {
-      return m_resourceDef;
+      return getTestResource();
     }
 
     ResourceInstance createStackServiceComponentResource(String stackName,
         String stackVersion, String serviceName, String componentName) {
-      return m_resourceDef;
+      return getTestResource();
     }
 
     ResourceInstance createOperatingSystemResource(String stackName,
         String stackVersion, String osType) {
-      return m_resourceDef;
+      return getTestResource();
     }
 
+
     @Override
     RequestFactory getRequestFactory() {
-      return m_requestFactory;
+      return getTestRequestFactory();
+    }
+
+    @Override
+    protected RequestBodyParser getBodyParser() {
+      return getTestBodyParser();
     }
 
     @Override
-    RequestHandler getRequestHandler(Request.Type requestType) {
-      return m_requestHandler;
+    protected ResultSerializer getResultSerializer() {
+      return getTestResultSerializer();
     }
   }
 
-  // todo: test getHostHandler, getServiceHandler, getHostComponentHandler
 }

+ 45 - 0
ambari-server/src/test/java/org/apache/ambari/server/api/services/parsers/BodyParseExceptionTest.java

@@ -0,0 +1,45 @@
+/**
+ * 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.api.services.parsers;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ * BodyParseException unit tests.
+ */
+public class BodyParseExceptionTest {
+
+  @Test
+  public void testCreateFromString() {
+    String msg = "some msg";
+    BodyParseException e = new BodyParseException(msg);
+
+    assertEquals(msg, e.getMessage());
+  }
+
+  @Test
+  public void testCreateFromException() {
+    Exception e = new Exception("test error msg");
+    BodyParseException bpe = new BodyParseException(e);
+
+    assertEquals("Invalid Request: Malformed Request Body.  An exception occurred parsing the request body: " +
+        e.getMessage(), bpe.getMessage());
+  }
+}

+ 0 - 140
ambari-server/src/test/java/org/apache/ambari/server/api/services/parsers/JsonPropertyParserTest.java

@@ -1,140 +0,0 @@
-/**
- * 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.api.services.parsers;
-
-import org.apache.ambari.server.controller.utilities.PropertyHelper;
-import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Unit tests for JsonPropertyParser.
- */
-public class JsonPropertyParserTest {
-
-  String serviceJson = "{\"Services\" : {" +
-      "    \"display_name\" : \"HDFS\"," +
-      "    \"description\" : \"Apache Hadoop Distributed File System\"," +
-      "    \"attributes\" : \"{ \\\"runnable\\\": true, \\\"mustInstall\\\": true, \\\"editable\\\": false, \\\"noDisplay\\\": false }\"," +
-      "    \"service_name\" : \"HDFS\"" +
-      "  }," +
-      "  \"ServiceInfo\" : {" +
-      "    \"cluster_name\" : \"tbmetrictest\"," +
-      "    \"state\" : \"STARTED\"" +
-      "  }," +
-      "\"OuterCategory\" : { \"propName\" : 100, \"nested1\" : { \"nested2\" : { \"innerPropName\" : \"innerPropValue\" } } } }";
-
-
-  String clustersJson = "[ {" +
-      "\"Clusters\" : {\n" +
-      "    \"cluster_name\" : \"unitTestCluster1\"" +
-      "} }," +
-      "{" +
-      "\"Clusters\" : {\n" +
-      "    \"cluster_name\" : \"unitTestCluster2\"," +
-      "    \"property1\" : \"prop1Value\"" +
-      "} }," +
-      "{" +
-      "\"Clusters\" : {\n" +
-      "    \"cluster_name\" : \"unitTestCluster3\"," +
-      "    \"Category\" : { \"property2\" : \"prop2Value\"}" +
-      "} } ]";
-
-
-  @Test
-  public void testParse() throws Exception {
-    RequestBodyParser parser = new JsonPropertyParser();
-    Set<Map<String, Object>> setProps = parser.parse(serviceJson);
-
-    assertEquals(1, setProps.size());
-
-    Map<String, Object> mapExpected = new HashMap<String, Object>();
-    mapExpected.put(PropertyHelper.getPropertyId("Services", "service_name"), "HDFS");
-    mapExpected.put(PropertyHelper.getPropertyId("Services", "display_name"), "HDFS");
-    mapExpected.put(PropertyHelper.getPropertyId("ServiceInfo", "cluster_name"), "tbmetrictest");
-    mapExpected.put(PropertyHelper.getPropertyId("Services", "attributes"), "{ \"runnable\": true, \"mustInstall\": true, \"editable\": false, \"noDisplay\": false }");
-    mapExpected.put(PropertyHelper.getPropertyId("Services", "description"), "Apache Hadoop Distributed File System");
-    mapExpected.put(PropertyHelper.getPropertyId("ServiceInfo", "state"), "STARTED");
-    mapExpected.put(PropertyHelper.getPropertyId("OuterCategory", "propName"), "100");
-    mapExpected.put(PropertyHelper.getPropertyId("OuterCategory.nested1.nested2", "innerPropName"), "innerPropValue");
-
-    assertEquals(mapExpected, setProps.iterator().next());
-  }
-
-  @Test
-  public void testParse_NullBody() {
-    RequestBodyParser parser = new JsonPropertyParser();
-    Set<Map<String, Object>> setProps = parser.parse(null);
-    assertNotNull(setProps);
-    assertEquals(0, setProps.size());
-  }
-
-  @Test
-  public void testParse_EmptyBody() {
-    RequestBodyParser parser = new JsonPropertyParser();
-    Set<Map<String, Object>> setProps = parser.parse("");
-    assertNotNull(setProps);
-    assertEquals(0, setProps.size());
-  }
-
-  @Test
-  public void testParse_Array() {
-    RequestBodyParser parser = new JsonPropertyParser();
-    Set<Map<String, Object>> setProps = parser.parse(clustersJson);
-    assertEquals(3, setProps.size());
-
-    boolean cluster1Matches = false;
-    boolean cluster2Matches = false;
-    boolean cluster3Matches = false;
-
-    Map<String, String> mapCluster1 = new HashMap<String, String>();
-    mapCluster1.put(PropertyHelper.getPropertyId("Clusters", "cluster_name"), "unitTestCluster1");
-
-    Map<String, String> mapCluster2 = new HashMap<String, String>();
-    mapCluster2.put(PropertyHelper.getPropertyId("Clusters", "cluster_name"), "unitTestCluster2");
-    mapCluster2.put(PropertyHelper.getPropertyId("Clusters", "property1"), "prop1Value");
-
-
-    Map<String, String> mapCluster3 = new HashMap<String, String>();
-    mapCluster3.put(PropertyHelper.getPropertyId("Clusters", "cluster_name"), "unitTestCluster3");
-    mapCluster3.put(PropertyHelper.getPropertyId("Clusters.Category", "property2"), "prop2Value");
-
-
-    for (Map<String, Object> mapProps : setProps) {
-      if (mapProps.equals(mapCluster1)) {
-        cluster1Matches = true;
-      } else if (mapProps.equals(mapCluster2)) {
-        cluster2Matches = true;
-      } else if (mapProps.equals(mapCluster3)) {
-        cluster3Matches = true;
-      }
-    }
-
-    assertTrue(cluster1Matches);
-    assertTrue(cluster2Matches);
-    assertTrue(cluster3Matches);
-  }
-}
-
-

+ 455 - 0
ambari-server/src/test/java/org/apache/ambari/server/api/services/parsers/JsonRequestBodyParserTest.java

@@ -0,0 +1,455 @@
+/**
+ * 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.api.services.parsers;
+
+import org.apache.ambari.server.api.services.NamedPropertySet;
+import org.apache.ambari.server.api.services.RequestBody;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.*;
+
+/**
+ * Unit tests for JsonPropertyParser.
+ */
+public class JsonRequestBodyParserTest {
+
+  String serviceJson = "{\"Services\" : {" +
+      "    \"display_name\" : \"HDFS\"," +
+      "    \"description\" : \"Apache Hadoop Distributed File System\","+
+      "    \"service_name\" : \"HDFS\"" +
+      "  }," +
+      "  \"ServiceInfo\" : {" +
+      "    \"cluster_name\" : \"tbmetrictest\"," +
+      "    \"state\" : \"STARTED\"" +
+      "  }," +
+      "\"OuterCategory\" : { \"propName\" : 100, \"nested1\" : { \"nested2\" : { \"innerPropName\" : \"innerPropValue\" } } }," +
+      "\"topLevelProp\" : \"value\" }";
+
+
+  String arrayJson = "[ {" +
+      "\"Clusters\" : {\n" +
+      "    \"cluster_name\" : \"unitTestCluster1\"" +
+      "} }," +
+      "{" +
+      "\"Clusters\" : {\n" +
+      "    \"cluster_name\" : \"unitTestCluster2\"," +
+      "    \"property1\" : \"prop1Value\"" +
+      "} }," +
+      "{" +
+      "\"Clusters\" : {\n" +
+      "    \"cluster_name\" : \"unitTestCluster3\"," +
+      "    \"Category\" : { \"property2\" : \"prop2Value\"}" +
+      "} } ]";
+
+  String arrayJson2 = "{" +
+      "\"Clusters\" : {\n" +
+      "    \"cluster_name\" : \"unitTestCluster1\"" +
+      "} }," +
+      "{" +
+      "\"Clusters\" : {\n" +
+      "    \"cluster_name\" : \"unitTestCluster2\"," +
+      "    \"property1\" : \"prop1Value\"" +
+      "} }," +
+      "{" +
+      "\"Clusters\" : {\n" +
+      "    \"cluster_name\" : \"unitTestCluster3\"," +
+      "    \"Category\" : { \"property2\" : \"prop2Value\"}" +
+      "} }";
+
+  String queryPostJson = "{ \"services\" : [ {" +
+      "\"ServiceInfo\" : {\n" +
+      "    \"service_name\" : \"unitTestService1\"" +
+      "} }," +
+      "{" +
+      "\"ServiceInfo\" : {\n" +
+      "    \"service_name\" : \"unitTestService2\"," +
+      "    \"property1\" : \"prop1Value\"" +
+      "} }," +
+      "{" +
+      "\"ServiceInfo\" : {\n" +
+      "    \"service_name\" : \"unitTestService3\"," +
+      "    \"Category\" : { \"property2\" : \"prop2Value\"}" +
+      "} } ] }";
+
+  String queryPostMultipleSubResourcesJson = "{ \"foo\" : [ {" +
+      "\"ServiceInfo\" : {\n" +
+      "    \"service_name\" : \"unitTestService1\"" +
+      "} }" +
+      "]," +
+      " \"bar\" : [" +
+      "{" +
+      "\"ServiceInfo\" : {\n" +
+      "    \"service_name\" : \"unitTestService2\"," +
+      "    \"Category\" : { \"property2\" : \"prop2Value\"}" +
+      "} } ] }";
+
+
+  String malformedJson = "{ \"Category\" : { \"foo\" : \"bar\"}";
+
+
+  @Test
+  public void testParse() throws BodyParseException {
+    RequestBodyParser parser = new JsonRequestBodyParser();
+    RequestBody body = parser.parse(serviceJson);
+
+    Set<NamedPropertySet> setProps = body.getPropertySets();
+    assertEquals(1, setProps.size());
+
+    Map<String, Object> mapExpected = new HashMap<String, Object>();
+    mapExpected.put(PropertyHelper.getPropertyId("Services", "service_name"), "HDFS");
+    mapExpected.put(PropertyHelper.getPropertyId("Services", "display_name"), "HDFS");
+    mapExpected.put(PropertyHelper.getPropertyId("ServiceInfo", "cluster_name"), "tbmetrictest");
+    mapExpected.put(PropertyHelper.getPropertyId("Services", "description"), "Apache Hadoop Distributed File System");
+    mapExpected.put(PropertyHelper.getPropertyId("ServiceInfo", "state"), "STARTED");
+    mapExpected.put(PropertyHelper.getPropertyId("OuterCategory", "propName"), "100");
+    mapExpected.put(PropertyHelper.getPropertyId("OuterCategory/nested1/nested2", "innerPropName"), "innerPropValue");
+    mapExpected.put(PropertyHelper.getPropertyId(null, "topLevelProp"), "value");
+
+    assertEquals(mapExpected, setProps.iterator().next().getProperties());
+
+    //assert body is correct by checking that properties match
+    String b = body.getBody();
+    body = parser.parse(b);
+    Set<NamedPropertySet> setProps2 = body.getPropertySets();
+    assertEquals(mapExpected, setProps2.iterator().next().getProperties());
+  }
+
+  @Test
+  public void testParse_NullBody() throws BodyParseException {
+    RequestBodyParser parser = new JsonRequestBodyParser();
+    RequestBody body = parser.parse(null);
+
+    assertNotNull(body.getPropertySets());
+    assertEquals(0, body.getPropertySets().size());
+    assertNull(body.getQueryString());
+    assertNull(body.getPartialResponseFields());
+    assertNull(body.getBody());
+  }
+
+  @Test
+  public void testParse_EmptyBody() throws BodyParseException {
+    RequestBodyParser parser = new JsonRequestBodyParser();
+    RequestBody body = parser.parse("");
+
+    assertNotNull(body.getPropertySets());
+    assertEquals(0, body.getPropertySets().size());
+    assertNull(body.getQueryString());
+    assertNull(body.getPartialResponseFields());
+    assertNull(body.getBody());
+  }
+
+  @Test
+  public void testParse_Array() throws BodyParseException {
+    RequestBodyParser parser = new JsonRequestBodyParser();
+    RequestBody body = parser.parse(arrayJson);
+
+    Set<NamedPropertySet> setProps = body.getPropertySets();
+
+    assertEquals(3, setProps.size());
+
+    boolean cluster1Matches = false;
+    boolean cluster2Matches = false;
+    boolean cluster3Matches = false;
+
+    Map<String, String> mapCluster1 = new HashMap<String, String>();
+    mapCluster1.put(PropertyHelper.getPropertyId("Clusters", "cluster_name"), "unitTestCluster1");
+
+    Map<String, String> mapCluster2 = new HashMap<String, String>();
+    mapCluster2.put(PropertyHelper.getPropertyId("Clusters", "cluster_name"), "unitTestCluster2");
+    mapCluster2.put(PropertyHelper.getPropertyId("Clusters", "property1"), "prop1Value");
+
+
+    Map<String, String> mapCluster3 = new HashMap<String, String>();
+    mapCluster3.put(PropertyHelper.getPropertyId("Clusters", "cluster_name"), "unitTestCluster3");
+    mapCluster3.put(PropertyHelper.getPropertyId("Clusters/Category", "property2"), "prop2Value");
+
+
+    for (NamedPropertySet propertySet : setProps) {
+      assertEquals("", propertySet.getName());
+      Map<String, Object> mapProps = propertySet.getProperties();
+      if (mapProps.equals(mapCluster1)) {
+        cluster1Matches = true;
+      } else if (mapProps.equals(mapCluster2)) {
+        cluster2Matches = true;
+      } else if (mapProps.equals(mapCluster3)) {
+        cluster3Matches = true;
+      }
+    }
+
+    assertTrue(cluster1Matches);
+    assertTrue(cluster2Matches);
+    assertTrue(cluster3Matches);
+
+    //assert body is correct by checking that properties match
+    String b = body.getBody();
+
+    body = parser.parse(b);
+
+    Set<NamedPropertySet> setProps2 = body.getPropertySets();
+    assertEquals(3, setProps2.size());
+    assertEquals(setProps, setProps2);
+  }
+
+  @Test
+  public void testParse___Array_NoArrayBrackets() throws BodyParseException {
+    RequestBodyParser parser = new JsonRequestBodyParser();
+    RequestBody body = parser.parse(arrayJson2);
+
+    Set<NamedPropertySet> setProps = body.getPropertySets();
+
+    assertEquals(3, setProps.size());
+
+    boolean cluster1Matches = false;
+    boolean cluster2Matches = false;
+    boolean cluster3Matches = false;
+
+    Map<String, String> mapCluster1 = new HashMap<String, String>();
+    mapCluster1.put(PropertyHelper.getPropertyId("Clusters", "cluster_name"), "unitTestCluster1");
+
+    Map<String, String> mapCluster2 = new HashMap<String, String>();
+    mapCluster2.put(PropertyHelper.getPropertyId("Clusters", "cluster_name"), "unitTestCluster2");
+    mapCluster2.put(PropertyHelper.getPropertyId("Clusters", "property1"), "prop1Value");
+
+
+    Map<String, String> mapCluster3 = new HashMap<String, String>();
+    mapCluster3.put(PropertyHelper.getPropertyId("Clusters", "cluster_name"), "unitTestCluster3");
+    mapCluster3.put(PropertyHelper.getPropertyId("Clusters/Category", "property2"), "prop2Value");
+
+
+    for (NamedPropertySet propertySet : setProps) {
+      Map<String, Object> mapProps = propertySet.getProperties();
+      if (mapProps.equals(mapCluster1)) {
+        cluster1Matches = true;
+      } else if (mapProps.equals(mapCluster2)) {
+        cluster2Matches = true;
+      } else if (mapProps.equals(mapCluster3)) {
+        cluster3Matches = true;
+      }
+    }
+
+    assertTrue(cluster1Matches);
+    assertTrue(cluster2Matches);
+    assertTrue(cluster3Matches);
+
+    //assert body is correct by checking that properties match
+    String b = body.getBody();
+    body = parser.parse(b);
+
+    Set<NamedPropertySet> setProps2 = body.getPropertySets();
+    assertEquals(3, setProps2.size());
+    assertEquals(setProps, setProps2);
+  }
+
+  @Test
+  public void testParse_QueryInBody() throws BodyParseException {
+    RequestBodyParser parser = new JsonRequestBodyParser();
+    String queryBody = "{ \"RequestInfo\" : { \"query\" : \"foo=bar\" }, \"Body\":" + serviceJson + "}";
+    RequestBody body = parser.parse(queryBody);
+
+
+    Set<NamedPropertySet> setProps = body.getPropertySets();
+    assertEquals(1, setProps.size());
+
+    Map<String, Object> mapExpected = new HashMap<String, Object>();
+    mapExpected.put(PropertyHelper.getPropertyId("Services", "service_name"), "HDFS");
+    mapExpected.put(PropertyHelper.getPropertyId("Services", "display_name"), "HDFS");
+    mapExpected.put(PropertyHelper.getPropertyId("ServiceInfo", "cluster_name"), "tbmetrictest");
+    mapExpected.put(PropertyHelper.getPropertyId("Services", "description"), "Apache Hadoop Distributed File System");
+    mapExpected.put(PropertyHelper.getPropertyId("ServiceInfo", "state"), "STARTED");
+    mapExpected.put(PropertyHelper.getPropertyId("OuterCategory", "propName"), "100");
+    mapExpected.put(PropertyHelper.getPropertyId("OuterCategory/nested1/nested2", "innerPropName"), "innerPropValue");
+    mapExpected.put(PropertyHelper.getPropertyId(null, "topLevelProp"), "value");
+
+    assertEquals(mapExpected, setProps.iterator().next().getProperties());
+    assertEquals("foo=bar", body.getQueryString());
+
+    //assert body is correct by checking that properties match
+    String b = body.getBody();
+    body = parser.parse(b);
+
+    Set<NamedPropertySet> setProps2 = body.getPropertySets();
+    assertEquals(mapExpected, setProps2.iterator().next().getProperties());
+  }
+
+  @Test
+  public void testParse_QueryPost() throws BodyParseException {
+    RequestBodyParser parser = new JsonRequestBodyParser();
+    RequestBody body = parser.parse(queryPostJson);
+
+
+    Set<NamedPropertySet> setProperties = body.getPropertySets();
+
+    assertEquals(3, setProperties.size());
+    boolean contains1 = false;
+    boolean contains2 = false;
+    boolean contains3 = false;
+
+    for (NamedPropertySet ps : setProperties) {
+      assertEquals("services", ps.getName());
+      Map<String, Object> mapProps = ps.getProperties();
+      String serviceName = (String) mapProps.get("ServiceInfo/service_name");
+      if (serviceName.equals("unitTestService1")) {
+        assertEquals(1, mapProps.size());
+        contains1 = true;
+      } else if (serviceName.equals("unitTestService2")) {
+        assertEquals("prop1Value", mapProps.get("ServiceInfo/property1"));
+        assertEquals(2, mapProps.size());
+        contains2 = true;
+      } else if (serviceName.equals("unitTestService3")) {
+        assertEquals("prop2Value", mapProps.get("ServiceInfo/Category/property2"));
+        assertEquals(2, mapProps.size());
+        contains3 = true;
+      } else {
+        fail("Unexpected service name");
+      }
+    }
+    assertTrue(contains1);
+    assertTrue(contains2);
+    assertTrue(contains3);
+
+    //assert body is correct by checking that properties match
+    String b = body.getBody();
+    body = parser.parse(b);
+
+    Set<NamedPropertySet> setProps2 = body.getPropertySets();
+    assertEquals(3, setProps2.size());
+    assertEquals(setProperties, setProps2);
+  }
+
+  @Test
+  public void testParse___QueryPost_multipleSubResTypes() throws BodyParseException {
+    RequestBodyParser parser = new JsonRequestBodyParser();
+    RequestBody body = parser.parse(queryPostMultipleSubResourcesJson);
+
+
+    Set<NamedPropertySet> setProperties = body.getPropertySets();
+
+    assertEquals(2, setProperties.size());
+    boolean contains1 = false;
+    boolean contains2 = false;
+
+    for (NamedPropertySet ps : setProperties) {
+      Map<String, Object> mapProps = ps.getProperties();
+      String serviceName = (String) mapProps.get("ServiceInfo/service_name");
+      if (serviceName.equals("unitTestService1")) {
+        assertEquals("foo", ps.getName());
+        assertEquals(1, mapProps.size());
+        contains1 = true;
+      } else if (serviceName.equals("unitTestService2")) {
+        assertEquals("bar", ps.getName());
+        assertEquals("prop2Value", mapProps.get("ServiceInfo/Category/property2"));
+        assertEquals(2, mapProps.size());
+        contains2 = true;
+      } else {
+        fail("Unexpected service name");
+      }
+    }
+    assertTrue(contains1);
+    assertTrue(contains2);
+
+    //assert body is correct by checking that properties match
+    String b = body.getBody();
+    body = parser.parse(b);
+
+    Set<NamedPropertySet> setProps2 = body.getPropertySets();
+    assertEquals(2, setProps2.size());
+    assertEquals(setProperties, setProps2);
+  }
+
+  @Test
+  public void testParse___QueryPost_QueryInBody() throws BodyParseException {
+    RequestBodyParser parser = new JsonRequestBodyParser();
+    String queryBody = "{ \"RequestInfo\" : { \"query\" : \"foo=bar\" }, \"Body\":" + queryPostJson + "}";
+    RequestBody body = parser.parse(queryBody);
+
+
+    Set<NamedPropertySet> setProperties = body.getPropertySets();
+
+    assertEquals("foo=bar", body.getQueryString());
+    assertEquals(3, setProperties.size());
+    boolean contains1 = false;
+    boolean contains2 = false;
+    boolean contains3 = false;
+
+    for (NamedPropertySet ps : setProperties) {
+      assertEquals("services", ps.getName());
+      Map<String, Object> mapProps = ps.getProperties();
+      String serviceName = (String) mapProps.get("ServiceInfo/service_name");
+      if (serviceName.equals("unitTestService1")) {
+        assertEquals(1, mapProps.size());
+        contains1 = true;
+      } else if (serviceName.equals("unitTestService2")) {
+        assertEquals("prop1Value", mapProps.get("ServiceInfo/property1"));
+        assertEquals(2, mapProps.size());
+        contains2 = true;
+      } else if (serviceName.equals("unitTestService3")) {
+        assertEquals("prop2Value", mapProps.get("ServiceInfo/Category/property2"));
+        assertEquals(2, mapProps.size());
+        contains3 = true;
+      } else {
+        fail("Unexpected service name");
+      }
+    }
+    assertTrue(contains1);
+    assertTrue(contains2);
+    assertTrue(contains3);
+
+    //assert body is correct by checking that properties match
+    String b = body.getBody();
+    assertEquals("{\"services\":[{\"ServiceInfo\":{" +
+        "\"service_name\":\"unitTestService1\"}},{\"ServiceInfo\":{" +
+        "\"service_name\":\"unitTestService2\",\"property1\":\"prop1Value\"}},{\"ServiceInfo\":{" +
+        "\"service_name\":\"unitTestService3\",\"Category\":{\"property2\":\"prop2Value\"}}}]}", b);
+
+    body = parser.parse(b);
+
+    Set<NamedPropertySet> setProps2 = body.getPropertySets();
+    assertEquals(3, setProps2.size());
+    assertEquals(setProperties, setProps2);
+  }
+
+  @Test
+  public void testParse_QueryOnlyInBody() throws BodyParseException {
+    RequestBodyParser parser = new JsonRequestBodyParser();
+    String queryBody = "{ \"RequestInfo\" : { \"query\" : \"foo=bar\" }}";
+    RequestBody body = parser.parse(queryBody);
+
+    assertEquals("foo=bar", body.getQueryString());
+    assertNull(body.getBody());
+  }
+
+  @Test
+  public void testParse_malformedBody() {
+    RequestBodyParser parser = new JsonRequestBodyParser();
+
+    try {
+      parser.parse(malformedJson);
+      fail("Expected exception due to malformed body");
+    } catch (BodyParseException e) {
+      // expected case
+    }
+  }
+}
+
+