You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by js...@apache.org on 2014/02/27 16:58:18 UTC
[3/3] git commit: AMBARI-4786. Add ability to export a blueprint from
a running cluster. This patch also includes new functionality for alternate
renderings
AMBARI-4786. Add ability to export a blueprint from a running cluster.
This patch also includes new functionality for alternate renderings
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/7eb4bc6b
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/7eb4bc6b
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/7eb4bc6b
Branch: refs/heads/trunk
Commit: 7eb4bc6b9905cc6df6aee2cfce662e7b53fe6d85
Parents: 62aa47b
Author: John Speidel <js...@hortonworks.com>
Authored: Fri Feb 21 12:57:02 2014 -0500
Committer: John Speidel <js...@hortonworks.com>
Committed: Thu Feb 27 10:58:00 2014 -0500
----------------------------------------------------------------------
.../api/handlers/BaseManagementHandler.java | 5 +-
.../ambari/server/api/handlers/ReadHandler.java | 12 +-
.../server/api/handlers/RequestHandler.java | 1 -
.../ambari/server/api/predicate/QueryLexer.java | 2 +
.../apache/ambari/server/api/query/Query.java | 17 +-
.../ambari/server/api/query/QueryImpl.java | 295 ++++++----
.../ambari/server/api/query/QueryInfo.java | 75 +++
.../server/api/query/render/BaseRenderer.java | 172 ++++++
.../query/render/ClusterBlueprintRenderer.java | 302 ++++++++++
.../api/query/render/DefaultRenderer.java | 68 +++
.../api/query/render/MinimalRenderer.java | 229 ++++++++
.../server/api/query/render/Renderer.java | 83 +++
.../api/resources/BaseResourceDefinition.java | 15 +
.../resources/ClusterResourceDefinition.java | 14 +-
.../api/resources/ResourceDefinition.java | 11 +
.../ambari/server/api/services/BaseRequest.java | 46 +-
.../ambari/server/api/services/BaseService.java | 7 +-
.../ambari/server/api/services/Request.java | 8 +-
.../api/services/ResultPostProcessorImpl.java | 12 +-
.../services/serializers/JsonSerializer.java | 12 +-
.../services/serializers/ResultSerializer.java | 3 +-
.../apache/ambari/server/api/util/TreeNode.java | 7 +
.../ambari/server/api/util/TreeNodeImpl.java | 7 +
.../controller/spi/ClusterController.java | 2 +-
.../server/controller/spi/SchemaFactory.java | 32 ++
.../server/api/handlers/CreateHandlerTest.java | 36 +-
.../server/api/handlers/DeleteHandlerTest.java | 21 +-
.../server/api/handlers/ReadHandlerTest.java | 67 +--
.../server/api/handlers/UpdateHandlerTest.java | 12 +-
.../server/api/predicate/QueryLexerTest.java | 36 ++
.../ambari/server/api/query/QueryImplTest.java | 3 +-
.../ambari/server/api/query/QueryInfoTest.java | 50 ++
.../render/ClusterBlueprintRendererTest.java | 225 ++++++++
.../api/query/render/DefaultRendererTest.java | 342 ++++++++++++
.../api/query/render/MinimalRendererTest.java | 559 +++++++++++++++++++
.../resources/BaseResourceDefinitionTest.java | 21 +-
.../ClusterResourceDefinitionTest.java | 88 +++
.../server/api/services/BaseRequestTest.java | 212 ++++++-
.../server/api/services/BaseServiceTest.java | 28 +-
.../serializers/JsonSerializerTest.java | 55 +-
40 files changed, 2882 insertions(+), 310 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/BaseManagementHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/BaseManagementHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/BaseManagementHandler.java
index a31a46e..c34f0d7 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/BaseManagementHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/BaseManagementHandler.java
@@ -18,6 +18,7 @@
package org.apache.ambari.server.api.handlers;
+import org.apache.ambari.server.api.query.Query;
import org.apache.ambari.server.api.resources.*;
import org.apache.ambari.server.api.services.*;
import org.apache.ambari.server.api.services.persistence.PersistenceManager;
@@ -61,10 +62,12 @@ public abstract class BaseManagementHandler implements RequestHandler {
@Override
public Result handleRequest(Request request) {
+ Query query = request.getResource().getQuery();
Predicate queryPredicate = request.getQueryPredicate();
+ query.setRenderer(request.getRenderer());
if (queryPredicate != null) {
- request.getResource().getQuery().setUserPredicate(queryPredicate);
+ query.setUserPredicate(queryPredicate);
}
return persist(request.getResource(), request.getBody());
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java
index 3b4cda1..d5717a6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java
@@ -23,8 +23,12 @@ import org.apache.ambari.server.api.services.ResultImpl;
import org.apache.ambari.server.api.services.ResultStatus;
import org.apache.ambari.server.api.services.Result;
import org.apache.ambari.server.api.query.Query;
-import org.apache.ambari.server.controller.spi.*;
-import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
+import org.apache.ambari.server.controller.spi.NoSuchResourceException;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.TemporalInfo;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,7 +50,7 @@ public class ReadHandler implements RequestHandler {
Query query = request.getResource().getQuery();
query.setPageRequest(request.getPageRequest());
- query.setMinimal(request.isMinimal());
+ query.setRenderer(request.getRenderer());
try {
addFieldsToQuery(request, query);
@@ -95,7 +99,7 @@ public class ReadHandler implements RequestHandler {
* Add partial response fields to the provided query.
*
* @param request the current request
- * @param query the associated query *
+ * @param query the associated query
*/
private void addFieldsToQuery(Request request, Query query) {
//Partial response
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/RequestHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/RequestHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/RequestHandler.java
index 381dedb..9e2d923 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/RequestHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/RequestHandler.java
@@ -18,7 +18,6 @@
package org.apache.ambari.server.api.handlers;
-import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.api.services.Request;
import org.apache.ambari.server.api.services.Result;
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java b/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java
index 5aa04c4..4ab75aa 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java
@@ -38,6 +38,7 @@ public class QueryLexer {
* Query string constants.
*/
public static final String QUERY_FIELDS = "fields";
+ public static final String QUERY_FORMAT = "format";
public static final String QUERY_PAGE_SIZE = "page_size";
public static final String QUERY_TO = "to";
public static final String QUERY_FROM = "from";
@@ -178,6 +179,7 @@ public class QueryLexer {
static {
// ignore values
SET_IGNORE.add(QUERY_FIELDS);
+ SET_IGNORE.add(QUERY_FORMAT);
SET_IGNORE.add(QUERY_PAGE_SIZE);
SET_IGNORE.add(QUERY_TO);
SET_IGNORE.add(QUERY_FROM);
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/query/Query.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/query/Query.java b/ambari-server/src/main/java/org/apache/ambari/server/api/query/Query.java
index 58c947a..b1dfa3d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/query/Query.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/query/Query.java
@@ -18,8 +18,15 @@
package org.apache.ambari.server.api.query;
+import org.apache.ambari.server.api.query.render.Renderer;
import org.apache.ambari.server.api.services.Result;
-import org.apache.ambari.server.controller.spi.*;
+import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
+import org.apache.ambari.server.controller.spi.NoSuchResourceException;
+import org.apache.ambari.server.controller.spi.PageRequest;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.TemporalInfo;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
import java.util.Set;
@@ -93,9 +100,11 @@ public interface Query {
public void setPageRequest(PageRequest pageRequest);
/**
- * Set a flag to indicate whether or not the response should be minimal.
+ * Set the corresponding renderer.
+ * The renderer is responsible for the rendering of the query result, including which
+ * properties are contained and the format of the result.
*
- * @param minimal minimal flag; true indicates that the response should be minimal
+ * @param renderer renderer for the query
*/
- public void setMinimal(boolean minimal);
+ public void setRenderer(Renderer renderer);
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryImpl.java
index a7ac498..fc7f7a2 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryImpl.java
@@ -18,19 +18,21 @@
package org.apache.ambari.server.api.query;
+import org.apache.ambari.server.api.query.render.DefaultRenderer;
+import org.apache.ambari.server.api.query.render.Renderer;
import org.apache.ambari.server.api.resources.ResourceDefinition;
import org.apache.ambari.server.api.resources.ResourceInstance;
import org.apache.ambari.server.api.resources.ResourceInstanceFactoryImpl;
import org.apache.ambari.server.api.resources.SubResourceDefinition;
import org.apache.ambari.server.api.services.ResultImpl;
-import org.apache.ambari.server.controller.internal.ResourceImpl;
+import org.apache.ambari.server.api.util.TreeNode;
+import org.apache.ambari.server.api.util.TreeNodeImpl;
import org.apache.ambari.server.controller.utilities.PredicateHelper;
import org.apache.ambari.server.controller.utilities.PropertyHelper;
import org.apache.ambari.server.controller.predicate.AndPredicate;
import org.apache.ambari.server.controller.predicate.EqualsPredicate;
import org.apache.ambari.server.api.services.Result;
import org.apache.ambari.server.controller.spi.*;
-import org.apache.ambari.server.api.util.TreeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -55,7 +57,7 @@ public class QueryImpl implements Query, ResourceInstance {
/**
* Properties of the query which make up the select portion of the query.
*/
- private final Set<String> queryPropertySet = new HashSet<String>();
+ private final Set<String> requestedProperties = new HashSet<String>();
/**
* Map that associates categories with temporal data.
@@ -74,14 +76,15 @@ public class QueryImpl implements Query, ResourceInstance {
/**
* Sub-resources of the resource which is being operated on.
+ * Should only be added via {@link #addSubResource(String, QueryImpl)}
*/
- private final Map<String, QueryImpl> querySubResourceSet = new HashMap<String, QueryImpl>();
+ private final Map<String, QueryImpl> requestedSubResources = new HashMap<String, QueryImpl>();
/**
* Sub-resource instances of this resource.
* Map of resource name to resource instance.
*/
- private Map<String, QueryImpl> subResourceSet;
+ private Map<String, QueryImpl> availableSubResources;
/**
* Indicates that the query should include all available properties.
@@ -99,14 +102,27 @@ public class QueryImpl implements Query, ResourceInstance {
private PageRequest pageRequest;
/**
- * Indicates whether or not the response should be minimal.
+ * The sub resource properties referenced in the user predicate.
*/
- private boolean minimal;
+ private final Set<String> subResourcePredicateProperties = new HashSet<String>();
/**
- * The sub resource properties referenced in the user predicate.
+ * Associated renderer. The default renderer is used unless
+ * an alternate renderer is specified for the request. The renderer
+ * is responsible for determining which properties are selected
+ * as well as the overall structure of the result.
*/
- private final Set<String> subResourcePredicateProperties = new HashSet<String>();
+ private Renderer renderer;
+
+ /**
+ * Sub-resource predicate.
+ */
+ private Predicate subResourcePredicate;
+
+ /**
+ * Processed predicate.
+ */
+ private Predicate processedPredicate;
/**
* The logger.
@@ -141,9 +157,7 @@ public class QueryImpl implements Query, ResourceInstance {
// wildcard
addAllProperties(temporalInfo);
} else{
- if (addPropertyToSubResource(propertyId, temporalInfo)){
- addKeyProperties(getResourceDefinition().getType(), !minimal);
- } else {
+ if (! addPropertyToSubResource(propertyId, temporalInfo)) {
if (propertyId.endsWith("/*")) {
propertyId = propertyId.substring(0, propertyId.length() - 2);
}
@@ -157,7 +171,7 @@ public class QueryImpl implements Query, ResourceInstance {
@Override
public void addLocalProperty(String property) {
- queryPropertySet.add(property);
+ requestedProperties.add(property);
}
@Override
@@ -178,7 +192,7 @@ public class QueryImpl implements Query, ResourceInstance {
@Override
public Set<String> getProperties() {
- return Collections.unmodifiableSet(queryPropertySet);
+ return Collections.unmodifiableSet(requestedProperties);
}
@Override
@@ -192,8 +206,9 @@ public class QueryImpl implements Query, ResourceInstance {
}
@Override
- public void setMinimal(boolean minimal) {
- this.minimal = minimal;
+ public void setRenderer(Renderer renderer) {
+ this.renderer = renderer;
+ renderer.init(clusterController);
}
@@ -245,7 +260,7 @@ public class QueryImpl implements Query, ResourceInstance {
return clusterController.equals(query.clusterController) && !(pageRequest != null ?
!pageRequest.equals(query.pageRequest) :
- query.pageRequest != null) && queryPropertySet.equals(query.queryPropertySet) &&
+ query.pageRequest != null) && requestedProperties.equals(query.requestedProperties) &&
resourceDefinition.equals(query.resourceDefinition) &&
keyValueMap.equals(query.keyValueMap) && !(userPredicate != null ?
!userPredicate.equals(query.userPredicate) :
@@ -256,7 +271,7 @@ public class QueryImpl implements Query, ResourceInstance {
public int hashCode() {
int result = resourceDefinition.hashCode();
result = 31 * result + clusterController.hashCode();
- result = 31 * result + queryPropertySet.hashCode();
+ result = 31 * result + requestedProperties.hashCode();
result = 31 * result + keyValueMap.hashCode();
result = 31 * result + (userPredicate != null ? userPredicate.hashCode() : 0);
result = 31 * result + (pageRequest != null ? pageRequest.hashCode() : 0);
@@ -270,8 +285,8 @@ public class QueryImpl implements Query, ResourceInstance {
* Get the map of sub-resources. Lazily create the map if required.
*/
protected Map<String, QueryImpl> ensureSubResources() {
- if (subResourceSet == null) {
- subResourceSet = new HashMap<String, QueryImpl>();
+ if (availableSubResources == null) {
+ availableSubResources = new HashMap<String, QueryImpl>();
Set<SubResourceDefinition> setSubResourceDefs =
getResourceDefinition().getSubResourceDefinitions();
@@ -283,28 +298,12 @@ public class QueryImpl implements Query, ResourceInstance {
QueryImpl resource = new QueryImpl(valueMap,
ResourceInstanceFactoryImpl.getResourceDefinition(type, valueMap),
controller);
- resource.setMinimal(minimal);
-
- Schema schema = controller.getSchema(type);
- // ensure pk is returned
- resource.addLocalProperty(schema.getKeyPropertyId(type));
-
- if (!minimal) {
- // add additionally required fk properties
- for (Resource.Type fkType : subResDef.getAdditionalForeignKeys()) {
- resource.addLocalProperty(schema.getKeyPropertyId(fkType));
- }
- }
-
- String subResourceName = subResDef.isCollection() ?
- resource.getResourceDefinition().getPluralName() :
- resource.getResourceDefinition().getSingularName();
-
- subResourceSet.put(subResourceName, resource);
+ String subResourceName = getSubResourceName(resource.getResourceDefinition(), subResDef);
+ availableSubResources.put(subResourceName, resource);
}
}
- return subResourceSet;
+ return availableSubResources;
}
/**
@@ -317,59 +316,52 @@ public class QueryImpl implements Query, ResourceInstance {
NoSuchParentResourceException {
Set<Resource> providerResourceSet = new HashSet<Resource>();
-
Resource.Type resourceType = getResourceDefinition().getType();
- Request request = createRequest(!minimal);
- Request qRequest = createRequest(true);
Predicate queryPredicate = createPredicate(getKeyValueMap(), processUserPredicate(userPredicate));
- Set<Resource> resourceSet = new LinkedHashSet<Resource>();
- for (Resource queryResource : doQuery(resourceType, qRequest, queryPredicate)) {
+ // must occur after processing user predicate and prior to creating request
+ finalizeProperties();
+
+ Request request = createRequest();
+ Set<Resource> resourceSet = new LinkedHashSet<Resource>();
+
+ for (Resource queryResource : doQuery(resourceType, request, queryPredicate)) {
providerResourceSet.add(queryResource);
resourceSet.add(queryResource);
}
- queryResults.put(null,
- new QueryResult(request, queryPredicate, userPredicate, getKeyValueMap(), resourceSet));
- clusterController.populateResources(resourceType, providerResourceSet, qRequest, queryPredicate);
- queryForSubResources(userPredicate, hasSubResourcePredicate());
+ queryResults.put(null, new QueryResult(
+ request, queryPredicate, userPredicate, getKeyValueMap(), resourceSet));
+
+ clusterController.populateResources(resourceType, providerResourceSet, request, queryPredicate);
+ queryForSubResources();
}
/**
* Query the cluster controller for the sub-resources associated with
* this query object.
*/
- private void queryForSubResources(Predicate predicate, boolean hasSubResourcePredicate)
+ private void queryForSubResources()
throws UnsupportedPropertyException,
SystemException,
NoSuchResourceException,
NoSuchParentResourceException {
- for (Map.Entry<String, QueryImpl> entry : querySubResourceSet.entrySet()) {
-
- QueryImpl subResource = entry.getValue();
- Resource.Type resourceType = subResource.getResourceDefinition().getType();
- Request request = subResource.createRequest(!minimal);
- Request qRequest = subResource.createRequest(true);
-
- Predicate subResourcePredicate = hasSubResourcePredicate ?
- getSubResourcePredicate(predicate, entry.getKey()) : null;
-
- Predicate processedPredicate = hasSubResourcePredicate ? subResource.processUserPredicate(subResourcePredicate) : null;
-
+ for (Map.Entry<String, QueryImpl> entry : requestedSubResources.entrySet()) {
+ QueryImpl subResource = entry.getValue();
+ Resource.Type resourceType = subResource.getResourceDefinition().getType();
+ Request request = subResource.createRequest();
Set<Resource> providerResourceSet = new HashSet<Resource>();
for (QueryResult queryResult : queryResults.values()) {
for (Resource resource : queryResult.getProviderResourceSet()) {
-
Map<Resource.Type, String> map = getKeyValueMap(resource, queryResult.getKeyValueMap());
- Predicate queryPredicate = subResource.createPredicate(map, processedPredicate);
-
- Set<Resource> resourceSet = new LinkedHashSet<Resource>();
+ Predicate queryPredicate = subResource.createPredicate(map, subResource.processedPredicate);
+ Set<Resource> resourceSet = new LinkedHashSet<Resource>();
try {
- for (Resource queryResource : subResource.doQuery(resourceType, qRequest, queryPredicate)) {
+ for (Resource queryResource : subResource.doQuery(resourceType, request, queryPredicate)) {
providerResourceSet.add(queryResource);
resourceSet.add(queryResource);
}
@@ -380,8 +372,8 @@ public class QueryImpl implements Query, ResourceInstance {
new QueryResult(request, queryPredicate, subResourcePredicate, map, resourceSet));
}
}
- clusterController.populateResources(resourceType, providerResourceSet, qRequest, null);
- subResource.queryForSubResources(subResourcePredicate, hasSubResourcePredicate);
+ clusterController.populateResources(resourceType, providerResourceSet, request, null);
+ subResource.queryForSubResources();
}
}
@@ -394,14 +386,10 @@ public class QueryImpl implements Query, ResourceInstance {
NoSuchResourceException,
NoSuchParentResourceException {
- if (queryPropertySet.isEmpty() && querySubResourceSet.isEmpty()) {
- //Add sub resource properties for default case where no fields are specified.
- querySubResourceSet.putAll(ensureSubResources());
- }
-
if (LOG.isDebugEnabled()) {
LOG.debug("Executing resource query: " + request + " where " + predicate);
}
+
return clusterController.getResources(type, request, predicate);
}
@@ -499,7 +487,7 @@ public class QueryImpl implements Query, ResourceInstance {
Set<Map<String, Object>> propertyMaps = new HashSet<Map<String, Object>>();
// For each sub category get the property maps for the sub resources
- for (Map.Entry<String, QueryImpl> entry : querySubResourceSet.entrySet()) {
+ for (Map.Entry<String, QueryImpl> entry : requestedSubResources.entrySet()) {
String subResourceCategory = category == null ? entry.getKey() : category + "/" + entry.getKey();
QueryImpl subResource = entry.getValue();
@@ -530,6 +518,77 @@ public class QueryImpl implements Query, ResourceInstance {
return resourcePropertyMaps;
}
+ /**
+ * Finalize properties for entire query tree before executing query.
+ */
+ private void finalizeProperties() {
+ ResourceDefinition rootDefinition = this.resourceDefinition;
+
+ QueryInfo rootQueryInfo = new QueryInfo(rootDefinition, this.requestedProperties);
+ TreeNode<QueryInfo> rootNode = new TreeNodeImpl<QueryInfo>(
+ null, rootQueryInfo, rootDefinition.getType().name());
+
+ TreeNode<QueryInfo> requestedPropertyTree = buildQueryPropertyTree(this, rootNode);
+
+ mergeFinalizedProperties(renderer.finalizeProperties(
+ requestedPropertyTree, isCollectionResource()), this);
+ }
+
+ /**
+ * Recursively build a tree of query information.
+ *
+ * @param query query to process
+ * @param node tree node associated with the query
+ *
+ * @return query info tree
+ */
+ private TreeNode<QueryInfo> buildQueryPropertyTree(QueryImpl query, TreeNode<QueryInfo> node) {
+ for (QueryImpl subQuery : query.requestedSubResources.values()) {
+ ResourceDefinition childResource = subQuery.resourceDefinition;
+
+ QueryInfo queryInfo = new QueryInfo(childResource, subQuery.requestedProperties);
+ TreeNode<QueryInfo> childNode = node.addChild(queryInfo, childResource.getType().name());
+ buildQueryPropertyTree(subQuery, childNode);
+ }
+ return node;
+ }
+
+ /**
+ * Merge the tree of query properties returned by the renderer with properties in
+ * the query tree.
+ *
+ * @param node property tree node
+ * @param query query associated with the property tree node
+ */
+ private void mergeFinalizedProperties(TreeNode<Set<String>> node, QueryImpl query) {
+
+ Set<String> finalizedProperties = node.getObject();
+ query.requestedProperties.clear();
+ // currently not exposing temporal information to renderer
+ query.requestedProperties.addAll(finalizedProperties);
+
+ for (TreeNode<Set<String>> child : node.getChildren()) {
+ Resource.Type childType = Resource.Type.valueOf(child.getName());
+ ResourceDefinition parentResource = query.resourceDefinition;
+ Set<SubResourceDefinition> subResources = parentResource.getSubResourceDefinitions();
+ String subResourceName = null;
+ for (SubResourceDefinition subResource : subResources) {
+ if (subResource.getType() == childType) {
+ ResourceDefinition resource = ResourceInstanceFactoryImpl.getResourceDefinition(
+ subResource.getType(), query.keyValueMap);
+ subResourceName = getSubResourceName(resource, subResource);
+ break;
+ }
+ }
+ QueryImpl subQuery = query.requestedSubResources.get(subResourceName);
+ if (subQuery == null) {
+ query.addProperty(subResourceName, null);
+ subQuery = query.requestedSubResources.get(subResourceName);
+ }
+ mergeFinalizedProperties(child, subQuery);
+ }
+ }
+
// Map the given set of property ids to corresponding property ids in the
// given sub-resource category.
private Map<String, String> getPropertyIdsForCategory(Set<String> propertyIds, String category) {
@@ -601,17 +660,14 @@ public class QueryImpl implements Query, ResourceInstance {
iterResource = pageResponse.getIterable();
}
- Set<String> propertyIds = queryRequest.getPropertyIds();
-
int count = 1;
for (Resource resource : iterResource) {
// add a child node for the resource and provide a unique name. The name is never used.
TreeNode<Resource> node = tree.addChild(
- minimal ? new ResourceImpl(resource, propertyIds) : resource,
- resource.getType() + ":" + count++);
+ resource, resource.getType() + ":" + count++);
- for (Map.Entry<String, QueryImpl> entry : querySubResourceSet.entrySet()) {
+ for (Map.Entry<String, QueryImpl> entry : requestedSubResources.entrySet()) {
String subResCategory = entry.getKey();
QueryImpl subResource = entry.getValue();
@@ -622,7 +678,7 @@ public class QueryImpl implements Query, ResourceInstance {
}
}
}
- return result;
+ return renderer.finalizeResult(result);
}
// Indicates whether or not this query has sub-resource elements
@@ -651,24 +707,6 @@ public class QueryImpl implements Query, ResourceInstance {
return visitor.getExtendedPredicate();
}
- private void addKeyProperties(Resource.Type resourceType, boolean includeFKs) {
- Schema schema = clusterController.getSchema(resourceType);
-
- if (includeFKs) {
- for (Resource.Type type : Resource.Type.values()) {
- // add fk's
- String propertyId = schema.getKeyPropertyId(type);
- if (propertyId != null) {
- addProperty(propertyId, null);
- }
- }
- } else {
- // add pk only
- String propertyId = schema.getKeyPropertyId(resourceType);
- addProperty(propertyId, null);
- }
- }
-
private void addAllProperties(TemporalInfo temporalInfo) {
allProperties = true;
if (temporalInfo != null) {
@@ -677,8 +715,8 @@ public class QueryImpl implements Query, ResourceInstance {
for (Map.Entry<String, QueryImpl> entry : ensureSubResources().entrySet()) {
String name = entry.getKey();
- if (! querySubResourceSet.containsKey(name)) {
- querySubResourceSet.put(name, entry.getValue());
+ if (! requestedSubResources.containsKey(name)) {
+ addSubResource(name, entry.getValue());
}
}
}
@@ -691,7 +729,7 @@ public class QueryImpl implements Query, ResourceInstance {
QueryImpl subResource = subResources.get(category);
if (subResource != null) {
- querySubResourceSet.put(category, subResource);
+ addSubResource(category, subResource);
//only add if a sub property is set or if a sub category is specified
if (index != -1) {
@@ -766,27 +804,28 @@ public class QueryImpl implements Query, ResourceInstance {
// record the sub-resource properties on this query
subResourcePredicateProperties.addAll(visitor.getSubResourceProperties());
- return visitor.getProcessedPredicate();
+ if (hasSubResourcePredicate()) {
+ for (Map.Entry<String, QueryImpl> entry : requestedSubResources.entrySet()) {
+ subResourcePredicate = getSubResourcePredicate(predicate, entry.getKey());
+ entry.getValue().processUserPredicate(subResourcePredicate);
+ }
+ }
+
+ processedPredicate = visitor.getProcessedPredicate();
+ return processedPredicate;
}
- private Request createRequest(boolean includeFKs) {
+ private Request createRequest() {
if (allProperties) {
return PropertyHelper.getReadRequest(Collections.<String>emptySet());
}
-
- Set<String> setProperties = new HashSet<String>();
Map<String, TemporalInfo> mapTemporalInfo = new HashMap<String, TemporalInfo>();
TemporalInfo globalTemporalInfo = temporalInfoMap.get(null);
- Resource.Type resourceType = getResourceDefinition().getType();
- if (getKeyValueMap().get(resourceType) == null) {
- addKeyProperties(resourceType, includeFKs);
- }
-
- setProperties.addAll(queryPropertySet);
-
+ Set<String> setProperties = new HashSet<String>();
+ setProperties.addAll(requestedProperties);
for (String propertyId : setProperties) {
TemporalInfo temporalInfo = temporalInfoMap.get(propertyId);
if (temporalInfo != null) {
@@ -822,6 +861,33 @@ public class QueryImpl implements Query, ResourceInstance {
return resourceKeyValueMap;
}
+ /**
+ * Add a sub query with the renderer set.
+ *
+ * @param name name of sub resource
+ * @param query sub resource
+ */
+ private void addSubResource(String name, QueryImpl query) {
+ // renderer specified for request only applies to top level query
+ query.setRenderer(new DefaultRenderer());
+ requestedSubResources.put(name, query);
+ }
+
+ /**
+ * Obtain the name of a sub-resource.
+ *
+ * @param resource parent resource
+ * @param subResource sub-resource
+ *
+ * @return either the plural or singular sub-resource name based on whether the sub-resource is
+ * included as a collection
+ */
+ private String getSubResourceName(ResourceDefinition resource, SubResourceDefinition subResource) {
+ return subResource.isCollection() ?
+ resource.getPluralName() :
+ resource.getSingularName();
+ }
+
// ----- inner class : QueryResult -----------------------------------------
/**
@@ -836,9 +902,8 @@ public class QueryImpl implements Query, ResourceInstance {
// ----- Constructor -----------------------------------------------------
- private QueryResult(Request request, Predicate predicate,
- Predicate userPredicate, Map<Resource.Type, String> keyValueMap,
- Set<Resource> providerResourceSet) {
+ private QueryResult(Request request, Predicate predicate, Predicate userPredicate,
+ Map<Resource.Type, String> keyValueMap, Set<Resource> providerResourceSet) {
this.request = request;
this.predicate = predicate;
this.userPredicate = userPredicate;
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryInfo.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryInfo.java b/ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryInfo.java
new file mode 100644
index 0000000..80b79f0
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryInfo.java
@@ -0,0 +1,75 @@
+/**
+ * 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.query;
+
+import org.apache.ambari.server.api.resources.ResourceDefinition;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Query related information.
+ */
+public class QueryInfo {
+ /**
+ * Resource definition
+ */
+ private ResourceDefinition m_resource;
+
+ /**
+ * Requested properties for the query.
+ * These properties comprise the select portion of a query.
+ */
+ private Set<String> m_properties;
+
+ // ----- Constructors ------------------------------------------------------
+
+ /**
+ * Constructor
+ *
+ * @param resource resource definition
+ * @param properties query properties
+ */
+ public QueryInfo(ResourceDefinition resource, Set<String> properties) {
+ m_resource = resource;
+ m_properties = new HashSet<String>(properties);
+ }
+
+ // ----- QueryInfo ---------------------------------------------------------
+
+ /**
+ * Obtain the resource definition associated with the query.
+ *
+ * @return associated resource definition
+ */
+ public ResourceDefinition getResource() {
+ return m_resource;
+ }
+
+ /**
+ * Obtain the properties associated with the query.
+ * These are the requested properties which comprise
+ * the select portion of the query.
+ *
+ * @return requested properties
+ */
+ public Set<String> getProperties() {
+ return m_properties;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/BaseRenderer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/BaseRenderer.java b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/BaseRenderer.java
new file mode 100644
index 0000000..7866aa4
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/BaseRenderer.java
@@ -0,0 +1,172 @@
+/**
+ * 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.query.render;
+
+import org.apache.ambari.server.api.query.QueryInfo;
+import org.apache.ambari.server.api.resources.ResourceDefinition;
+import org.apache.ambari.server.api.resources.SubResourceDefinition;
+import org.apache.ambari.server.api.util.TreeNode;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.Schema;
+import org.apache.ambari.server.controller.spi.SchemaFactory;
+
+import java.util.HashSet;
+import java.util.Set;
+
+
+/**
+ * Base renderer.
+ * Contains functionality which may be common across implementations.
+ */
+public abstract class BaseRenderer implements Renderer {
+
+ /**
+ * Factory for creating schema instances.
+ */
+ private SchemaFactory m_schemaFactory;
+
+ @Override
+ public void init(SchemaFactory schemaFactory) {
+ m_schemaFactory = schemaFactory;
+ }
+
+ /**
+ * Obtain a schema instance based on resource type.
+ *
+ * @param type resource type
+ *
+ * @return schema instance for the provided resource type
+ */
+ protected Schema getSchema(Resource.Type type) {
+ return m_schemaFactory.getSchema(type);
+ }
+
+ /**
+ * Copies a tree of QueryInfo to a tree of Set<String>.
+ * This is useful in {@link Renderer#finalizeProperties(TreeNode, boolean)} for converting
+ * the passed in tree of query info to the return type which is a set of property names.
+ *
+ * @param queryTree source tree
+ * @param propertyTree target tree
+ */
+ protected void copyPropertiesToResult(TreeNode<QueryInfo> queryTree, TreeNode<Set<String>> propertyTree) {
+ for (TreeNode<QueryInfo> node : queryTree.getChildren()) {
+ TreeNode<Set<String>> child = propertyTree.addChild(
+ node.getObject().getProperties(), node.getName());
+ copyPropertiesToResult(node, child);
+ }
+ }
+
+ /**
+ * Add primary key for the specified resource type to the provided set.
+ *
+ * @param resourceType resource type
+ * @param properties set of properties which pk will be added to
+ */
+ protected void addPrimaryKey(Resource.Type resourceType, Set<String> properties) {
+ properties.add(getSchema(resourceType).getKeyPropertyId(resourceType));
+ }
+
+ /**
+ * Add primary and all foreign keys for the specified resource type to the provided set.
+ *
+ * @param resourceType resource type
+ * @param properties set of properties which keys will be added to
+ */
+ protected void addKeys(Resource.Type resourceType, Set<String> properties) {
+ Schema schema = getSchema(resourceType);
+
+ for (Resource.Type type : Resource.Type.values()) {
+ String propertyId = schema.getKeyPropertyId(type);
+ if (propertyId != null) {
+ properties.add(propertyId);
+ }
+ }
+ }
+
+ /**
+ * Determine if the query node contains no properties and no children.
+ *
+ * @param queryNode the query node to check
+ *
+ * @return true if the node contains no properties or children; false otherwise
+ */
+ protected boolean isRequestWithNoProperties(TreeNode<QueryInfo> queryNode) {
+ return queryNode.getChildren().isEmpty() &&
+ queryNode.getObject().getProperties().size() == 0;
+ }
+
+ /**
+ * Add available sub resources to property node.
+ *
+ * @param queryTree query tree
+ * @param propertyTree property tree
+ */
+ protected void addSubResources(TreeNode<QueryInfo> queryTree,
+ TreeNode<Set<String>> propertyTree) {
+
+ QueryInfo queryInfo = queryTree.getObject();
+ ResourceDefinition resource = queryInfo.getResource();
+ Set<SubResourceDefinition> subResources = resource.getSubResourceDefinitions();
+ for (SubResourceDefinition subResource : subResources) {
+ Set<String> resourceProperties = new HashSet<String>();
+ populateSubResourceDefaults(subResource, resourceProperties);
+ propertyTree.addChild(resourceProperties, subResource.getType().name());
+ }
+ }
+
+ /**
+ * Populate sub-resource properties.
+ *
+ * @param subResource definition of sub-resource
+ * @param properties property set to update
+ */
+ protected void populateSubResourceDefaults(
+ SubResourceDefinition subResource, Set<String> properties) {
+
+ Schema schema = getSchema(subResource.getType());
+ Set<Resource.Type> foreignKeys = subResource.getAdditionalForeignKeys();
+ for (Resource.Type fk : foreignKeys) {
+ properties.add(schema.getKeyPropertyId(fk));
+ }
+ addPrimaryKey(subResource.getType(), properties);
+ addKeys(subResource.getType(), properties);
+ }
+
+ /**
+ * Add required primary and foreign keys properties based on request type.
+ *
+ * @param propertyTree tree of properties
+ * @param addIfEmpty whether keys should be added to node with no properties
+ */
+ protected void ensureRequiredProperties(
+ TreeNode<Set<String>> propertyTree, boolean addIfEmpty) {
+
+ Resource.Type type = Resource.Type.valueOf(propertyTree.getName());
+ Set<String> properties = propertyTree.getObject();
+
+ if (!properties.isEmpty() || addIfEmpty) {
+ addKeys(type, properties);
+ }
+
+ for (TreeNode<Set<String>> child : propertyTree.getChildren()) {
+ ensureRequiredProperties(child, addIfEmpty);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/ClusterBlueprintRenderer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/ClusterBlueprintRenderer.java b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/ClusterBlueprintRenderer.java
new file mode 100644
index 0000000..db3dff8
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/ClusterBlueprintRenderer.java
@@ -0,0 +1,302 @@
+/**
+ * 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.query.render;
+
+import org.apache.ambari.server.api.query.QueryInfo;
+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.ResultPostProcessor;
+import org.apache.ambari.server.api.services.ResultPostProcessorImpl;
+import org.apache.ambari.server.api.util.TreeNode;
+import org.apache.ambari.server.api.util.TreeNodeImpl;
+import org.apache.ambari.server.controller.internal.ResourceImpl;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Renderer which renders a cluster resource as a blueprint.
+ */
+public class ClusterBlueprintRenderer extends BaseRenderer implements Renderer {
+
+ // ----- Renderer ----------------------------------------------------------
+
+ @Override
+ public TreeNode<Set<String>> finalizeProperties(
+ TreeNode<QueryInfo> queryProperties, boolean isCollection) {
+
+ Set<String> properties = new HashSet<String>(queryProperties.getObject().getProperties());
+ TreeNode<Set<String>> resultTree = new TreeNodeImpl<Set<String>>(
+ null, properties, queryProperties.getName());
+
+ copyPropertiesToResult(queryProperties, resultTree);
+ String hostType = Resource.Type.Host.name();
+ String hostComponentType = Resource.Type.HostComponent.name();
+ TreeNode<Set<String>> hostComponentNode = resultTree.getChild(
+ hostType + "/" + hostComponentType);
+
+ if (hostComponentNode == null) {
+ TreeNode<Set<String>> hostNode = resultTree.getChild(hostType);
+ if (hostNode == null) {
+ hostNode = resultTree.addChild(new HashSet<String>(), hostType);
+ }
+ hostComponentNode = hostNode.addChild(new HashSet<String>(), hostComponentType);
+ }
+ hostComponentNode.getObject().add("HostRoles/component_name");
+
+ return resultTree;
+ }
+
+ @Override
+ public Result finalizeResult(Result queryResult) {
+ TreeNode<Resource> resultTree = queryResult.getResultTree();
+ Result result = new ResultImpl(true);
+ TreeNode<Resource> blueprintResultTree = result.getResultTree();
+ if (isCollection(resultTree)) {
+ blueprintResultTree.setProperty("isCollection", "true");
+ }
+
+ for (TreeNode<Resource> node : resultTree.getChildren()) {
+ Resource blueprintResource = createBlueprintResource(node);
+ blueprintResultTree.addChild(new TreeNodeImpl<Resource>(
+ blueprintResultTree, blueprintResource, node.getName()));
+ }
+ return result;
+ }
+
+ @Override
+ public ResultPostProcessor getResultPostProcessor(Request request) {
+ return new BlueprintPostProcessor(request);
+ }
+
+ // ----- private instance methods ------------------------------------------
+
+ /**
+ * Create a blueprint resource.
+ *
+ * @param clusterNode cluster tree node
+ *
+ * @return a new blueprint resource
+ */
+ private Resource createBlueprintResource(TreeNode<Resource> clusterNode) {
+ Resource clusterResource = clusterNode.getObject();
+ Resource blueprintResource = new ResourceImpl(Resource.Type.Cluster);
+ String clusterName = (String) clusterResource.getPropertyValue(
+ PropertyHelper.getPropertyId("Clusters", "cluster_name"));
+ //todo: deal with name collision?
+ String blueprintName = "blueprint-" + clusterName;
+ String[] stackTokens = ((String) clusterResource.getPropertyValue(
+ PropertyHelper.getPropertyId("Clusters", "version"))).split("-");
+
+ blueprintResource.setProperty("Blueprints/blueprint_name", blueprintName);
+ blueprintResource.setProperty("Blueprints/stack_name", stackTokens[0]);
+ blueprintResource.setProperty("Blueprints/stack_version", stackTokens[1]);
+ blueprintResource.setProperty(
+ "host_groups", processHostGroups(clusterNode.getChild("hosts")));
+
+ return blueprintResource;
+ }
+
+ /**
+ * Process host group information for all hosts.
+ *
+ * @param hostNode a host node
+ *
+ * @return list of host group property maps, one element for each host group
+ */
+ private List<Map<String, Object>> processHostGroups(TreeNode<Resource> hostNode) {
+ Map<HostGroup, HostGroup> mapHostGroups = new HashMap<HostGroup, HostGroup>();
+ for (TreeNode<Resource> host : hostNode.getChildren()) {
+ HostGroup group = HostGroup.parse(host);
+ if (mapHostGroups.containsKey(group)) {
+ mapHostGroups.get(group).incrementCardinality();
+ } else {
+ mapHostGroups.put(group, group);
+ }
+ }
+
+ int count = 1;
+ List<Map<String, Object>> listHostGroups = new ArrayList<Map<String, Object>>();
+ for (HostGroup group : mapHostGroups.values()) {
+ String groupName = "host_group_" + count++;
+ Map<String, Object> mapGroupProperties = new HashMap<String, Object>();
+ listHostGroups.add(mapGroupProperties);
+
+ mapGroupProperties.put("name", groupName);
+ mapGroupProperties.put("cardinality", String.valueOf(group.getCardinality()));
+ mapGroupProperties.put("components", processHostGroupComponents(group));
+ }
+ return listHostGroups;
+ }
+
+ /**
+ * Process host group component information for a specific host.
+ *
+ * @param group host group instance
+ *
+ * @return list of component names for the host
+ */
+ private List<Map<String, String>> processHostGroupComponents(HostGroup group) {
+ List<Map<String, String>> listHostGroupComponents = new ArrayList<Map<String, String>>();
+ for (String component : group.getComponents()) {
+ Map<String, String> mapComponentProperties = new HashMap<String, String>();
+ listHostGroupComponents.add(mapComponentProperties);
+ mapComponentProperties.put("name", component);
+ }
+ return listHostGroupComponents;
+ }
+
+ /**
+ * Determine whether a node represents a collection.
+ *
+ * @param node node which is evaluated for being a collection
+ *
+ * @return true if the node represents a collection; false otherwise
+ */
+ private boolean isCollection(TreeNode<Resource> node) {
+ String isCollection = node.getProperty("isCollection");
+ return isCollection != null && isCollection.equals("true");
+ }
+
+ // ----- Host Group inner class --------------------------------------------
+
+ /**
+ * Host Group representation.
+ */
+ private static class HostGroup {
+ /**
+ * Associated components.
+ */
+ private Set<String> m_components = new HashSet<String>();
+
+ /**
+ * Number of instances.
+ */
+ private int m_cardinality = 1;
+
+ /**
+ * Factory method for obtaining a host group instance.
+ * Parses a host tree node for host related information.
+ *
+ * @param host host tree node
+ *
+ * @return a new HostGroup instance
+ */
+ public static HostGroup parse(TreeNode<Resource> host) {
+ HostGroup group = new HostGroup();
+
+ TreeNode<Resource> components = host.getChild("host_components");
+ for (TreeNode<Resource> component : components.getChildren()) {
+ group.getComponents().add((String) component.getObject().getPropertyValue(
+ "HostRoles/component_name"));
+ }
+
+ group.addAmbariComponentIfLocalhost((String) host.getObject().getPropertyValue(
+ PropertyHelper.getPropertyId("Hosts", "host_name")));
+
+ return group;
+ }
+
+ /** `
+ * Obtain associated components.
+ *
+ * @return set of associated components
+ */
+ public Set<String> getComponents() {
+ return m_components;
+ }
+
+ /**
+ * Obtain the number of instances associated with this host group.
+ *
+ * @return number of hosts associated with this host group
+ */
+ public int getCardinality() {
+ return m_cardinality;
+ }
+
+ /**
+ * Increment the cardinality count by one.
+ */
+ public void incrementCardinality() {
+ m_cardinality += 1;
+ }
+
+ /**
+ * Add the AMBARI_SERVER component if the host is the local host.
+ *
+ * @param hostname host to check
+ */
+ private void addAmbariComponentIfLocalhost(String hostname) {
+ try {
+ InetAddress hostAddress = InetAddress.getByName(hostname);
+ try {
+ if (hostAddress.equals(InetAddress.getLocalHost())) {
+ getComponents().add("AMBARI_SERVER");
+ }
+ } catch (UnknownHostException e) {
+ //todo: SystemException?
+ throw new RuntimeException("Unable to obtain local host name", e);
+ }
+ } catch (UnknownHostException e) {
+ // ignore
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ HostGroup hostGroup = (HostGroup) o;
+
+ return m_components.equals(hostGroup.m_components);
+ }
+
+ @Override
+ public int hashCode() {
+ return m_components.hashCode();
+ }
+ }
+
+ // ----- Blueprint Post Processor inner class ------------------------------
+
+ /**
+ * Post processor that strips href properties
+ */
+ private static class BlueprintPostProcessor extends ResultPostProcessorImpl {
+ private BlueprintPostProcessor(Request request) {
+ super(request);
+ }
+
+ @Override
+ protected void finalizeNode(TreeNode<Resource> node) {
+ node.removeProperty("href");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/DefaultRenderer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/DefaultRenderer.java b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/DefaultRenderer.java
new file mode 100644
index 0000000..1b996a2
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/DefaultRenderer.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.query.render;
+
+import org.apache.ambari.server.api.query.QueryInfo;
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultPostProcessor;
+import org.apache.ambari.server.api.services.ResultPostProcessorImpl;
+import org.apache.ambari.server.api.util.TreeNode;
+import org.apache.ambari.server.api.util.TreeNodeImpl;
+import java.util.Set;
+
+/**
+ * Default resource renderer.
+ * Provides the default "native" rendering for all resources.
+ */
+public class DefaultRenderer extends BaseRenderer implements Renderer {
+
+ // ----- Renderer ----------------------------------------------------------
+
+ @Override
+ public TreeNode<Set<String>> finalizeProperties(
+ TreeNode<QueryInfo> queryTree, boolean isCollection) {
+
+ QueryInfo queryInfo = queryTree.getObject();
+ TreeNode<Set<String>> resultTree = new TreeNodeImpl<Set<String>>(
+ null, queryInfo.getProperties(), queryTree.getName());
+
+ copyPropertiesToResult(queryTree, resultTree);
+
+ boolean addKeysToEmptyResource = true;
+ if (! isCollection && isRequestWithNoProperties(queryTree)) {
+ addSubResources(queryTree, resultTree);
+ addKeysToEmptyResource = false;
+ }
+ ensureRequiredProperties(resultTree, addKeysToEmptyResource);
+
+ return resultTree;
+ }
+
+ @Override
+ public ResultPostProcessor getResultPostProcessor(Request request) {
+ // simply return the native rendering
+ return new ResultPostProcessorImpl(request);
+ }
+
+ @Override
+ public Result finalizeResult(Result queryResult) {
+ return queryResult;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/MinimalRenderer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/MinimalRenderer.java b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/MinimalRenderer.java
new file mode 100644
index 0000000..2fe4fce
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/MinimalRenderer.java
@@ -0,0 +1,229 @@
+/**
+ * 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.query.render;
+
+import org.apache.ambari.server.api.query.QueryInfo;
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultPostProcessor;
+import org.apache.ambari.server.api.services.ResultPostProcessorImpl;
+import org.apache.ambari.server.api.util.TreeNode;
+import org.apache.ambari.server.api.util.TreeNodeImpl;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Minimal Renderer.
+ *
+ * All href properties are stripped from the result.
+ *
+ * For the root resource, this renderer behaves identically to the
+ * default renderer wrt resource properties and sub-resources. If
+ * no root properties or sub-resources are specified all top level
+ * properties and all sub-resources are included in the result. If
+ * any root properties or any sub-resources are requested, then only
+ * those will be included in the result.
+ *
+ * For sub-resource, only primary keys and any requested properties
+ * are included in the result.
+ *
+ * This renderer can be specified for any resource using
+ * 'format=minimal' or the older syntax 'minimal_response=true'.
+ */
+public class MinimalRenderer extends BaseRenderer implements Renderer {
+
+ /**
+ * Type of root resource.
+ */
+ private Resource.Type m_rootType;
+
+ /**
+ * Whether the request is for a collection.
+ */
+ private boolean m_isCollection;
+
+ /**
+ * Map of requested properties.
+ */
+ private Map<Resource.Type, Set<String>> m_originalProperties =
+ new HashMap<Resource.Type, Set<String>>();
+
+ // ----- Renderer ----------------------------------------------------------
+
+ @Override
+ public TreeNode<Set<String>> finalizeProperties(
+ TreeNode<QueryInfo> queryTree, boolean isCollection) {
+
+ QueryInfo queryInfo = queryTree.getObject();
+ TreeNode<Set<String>> resultTree = new TreeNodeImpl<Set<String>>(
+ null, queryInfo.getProperties(), queryTree.getName());
+
+ copyPropertiesToResult(queryTree, resultTree);
+
+ m_rootType = queryTree.getObject().getResource().getType();
+ m_isCollection = isCollection;
+
+ boolean addKeysToEmptyResource = true;
+ if (! isCollection && isRequestWithNoProperties(queryTree)) {
+ addSubResources(queryTree, resultTree);
+ addKeysToEmptyResource = false;
+ }
+ ensureRequiredProperties(resultTree, addKeysToEmptyResource);
+ processRequestedProperties(queryTree);
+
+ return resultTree;
+ }
+
+ @Override
+ public Result finalizeResult(Result queryResult) {
+ // can't just return result, need to strip added properties.
+ processResultNode(queryResult.getResultTree());
+ return queryResult;
+ }
+
+ @Override
+ public ResultPostProcessor getResultPostProcessor(Request request) {
+ return new MinimalPostProcessor(request);
+ }
+
+ // ----- BaseRenderer ------------------------------------------------------
+
+ @Override
+ protected void addKeys(Resource.Type resourceType, Set<String> properties) {
+ // override to only add pk instead of pk and all fk's
+ addPrimaryKey(resourceType, properties);
+ }
+
+ // ----- private instance methods ------------------------------------------
+
+ /**
+ * Recursively save all requested properties to check the result
+ * properties against.
+ *
+ * @param queryTree query tree to process
+ */
+ private void processRequestedProperties(TreeNode<QueryInfo> queryTree) {
+ QueryInfo queryInfo = queryTree.getObject();
+ if (queryInfo != null) {
+ Resource.Type type = queryInfo.getResource().getType();
+ Set<String> properties = m_originalProperties.get(type);
+ if (properties == null) {
+ properties = new HashSet<String>();
+ m_originalProperties.put(type, properties);
+ }
+ properties.addAll(queryInfo.getProperties());
+ for (TreeNode<QueryInfo> child : queryTree.getChildren()) {
+ processRequestedProperties(child);
+ }
+ }
+ }
+
+ /**
+ * Recursively strip all unwanted properties from the result nodes.
+ * During normal processing, foreign keys are always added to the request which need
+ * to be stripped unless they were requested.
+ *
+ * @param node node to process for extra properties
+ */
+ private void processResultNode(TreeNode<Resource> node) {
+ Resource resource = node.getObject();
+ if (resource != null && ( resource.getType() != m_rootType || m_isCollection)) {
+ Resource.Type type = resource.getType();
+ Set<String> requestedProperties = m_originalProperties.get(type);
+ Map<String, Map<String, Object>> properties = resource.getPropertiesMap();
+
+ Iterator<Map.Entry<String, Map<String, Object>>> iter;
+ for(iter = properties.entrySet().iterator(); iter.hasNext(); ) {
+ Map.Entry<String, Map<String, Object>> entry = iter.next();
+ String categoryName = entry.getKey();
+ Iterator<String> valueIter;
+
+ for(valueIter = entry.getValue().keySet().iterator(); valueIter.hasNext(); ) {
+ String propName = valueIter.next();
+ // if property was not requested and it is not a pk, remove
+ String absPropertyName = PropertyHelper.getPropertyId(categoryName, propName);
+ if ((requestedProperties == null ||
+ (! requestedProperties.contains(absPropertyName) &&
+ ! requestedProperties.contains(categoryName))) &&
+ ! getPrimaryKeys(type).contains(absPropertyName)) {
+ valueIter.remove();
+ }
+ }
+ if (entry.getValue().isEmpty()) {
+ iter.remove();
+ }
+ }
+ }
+ for (TreeNode<Resource> child : node.getChildren()) {
+ processResultNode(child);
+ }
+ }
+
+ /**
+ * Obtain the primary keys for the specified type.
+ * This method is necessary because some resource types, specifically
+ * the configuration type, don't have a proper pk even though one is
+ * registered. Instead, multiple properties are used as a 'composite'
+ * key even though this is not supported by the framework.
+ *
+ * @param type resource type
+ *
+ * @return set of pk's for a type
+ */
+ private Set<String> getPrimaryKeys(Resource.Type type) {
+ Set<String> primaryKeys = new HashSet<String>();
+
+ if (type == Resource.Type.Configuration) {
+ primaryKeys.add("type");
+ primaryKeys.add("tag");
+ } else {
+ Map<Resource.Type, String> keys = PropertyHelper.getKeyPropertyIds(type);
+ if (keys != null) {
+ String pk = PropertyHelper.getKeyPropertyIds(type).get(type);
+ if (pk != null) {
+ primaryKeys = Collections.singleton(pk);
+ }
+ }
+ }
+ return primaryKeys;
+ }
+
+ // ----- inner classes -----------------------------------------------------
+
+ /**
+ * Post processor which doesn't generate href properties in the result tree.
+ */
+ private static class MinimalPostProcessor extends ResultPostProcessorImpl {
+ private MinimalPostProcessor(Request request) {
+ super(request);
+ }
+
+ @Override
+ protected void finalizeNode(TreeNode<Resource> node) {
+ node.removeProperty("href");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/Renderer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/Renderer.java b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/Renderer.java
new file mode 100644
index 0000000..f353d53
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/Renderer.java
@@ -0,0 +1,83 @@
+/**
+ * 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.query.render;
+
+
+import org.apache.ambari.server.api.query.QueryInfo;
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultPostProcessor;
+import org.apache.ambari.server.api.util.TreeNode;
+import org.apache.ambari.server.controller.spi.SchemaFactory;
+
+import java.util.Set;
+
+/**
+ * Responsible for the rendering of a result.
+ * This includes both the content (which properties), and the format
+ * of the query result. Format doesn't refer to json or xml, but
+ * instead to the structure of the categories, sub-resources and
+ * properties. Renderer's are registered for a resource type by
+ * adding them to the corresponding resource definition.
+ */
+public interface Renderer {
+
+ /**
+ * Set a schema factory on the renderer.
+ *
+ * @param schemaFactory factory of schema instances
+ */
+ public void init(SchemaFactory schemaFactory);
+
+ /**
+ * Finalize which properties are requested by the query.
+ * This is called once per user query regardless of
+ * how many sub-queries the original query is decomposed
+ * into.
+ *
+ * @param queryProperties tree of query information. Contains query information
+ * for the root query and all sub-queries (children)
+ * @param isCollection whether the query is a collection
+ *
+ * @return tree of sets of string properties for each query including any sub-queries
+ */
+ public TreeNode<Set<String>> finalizeProperties(
+ TreeNode<QueryInfo> queryProperties, boolean isCollection);
+
+ /**
+ * Finalize the query results.
+ *
+ * @param queryResult result of query in native (default) format
+ *
+ * @return result in the format dictated by the renderer
+ */
+ public Result finalizeResult(Result queryResult);
+
+ /**
+ * Obtain the associated post processor.
+ * Post Processors existed prior to renderer's to allow the native result
+ * to be augmented before returning it to the user. This functionality should
+ * be merged into the renderer.
+ *
+ * @param request original request
+ *
+ * @return associated post processor
+ */
+ public ResultPostProcessor getResultPostProcessor(Request request);
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/BaseResourceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/BaseResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/BaseResourceDefinition.java
index 85ca8e5..1db8518 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/BaseResourceDefinition.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/BaseResourceDefinition.java
@@ -19,6 +19,9 @@
package org.apache.ambari.server.api.resources;
+import org.apache.ambari.server.api.query.render.DefaultRenderer;
+import org.apache.ambari.server.api.query.render.MinimalRenderer;
+import org.apache.ambari.server.api.query.render.Renderer;
import org.apache.ambari.server.api.services.Request;
import org.apache.ambari.server.api.util.TreeNode;
import org.apache.ambari.server.controller.spi.ClusterController;
@@ -69,6 +72,18 @@ public abstract class BaseResourceDefinition implements ResourceDefinition {
return listProcessors;
}
+ @Override
+ public Renderer getRenderer(String name) {
+ if (name == null || name.equals("default")) {
+ return new DefaultRenderer();
+ } else if (name.equals("minimal")) {
+ return new MinimalRenderer();
+ } else {
+ throw new IllegalArgumentException("Invalid renderer name: " + name +
+ " for resource of type: " + m_type);
+ }
+ }
+
ClusterController getClusterController() {
return ClusterControllerHelper.getClusterController();
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
index 4b0e8e1..43578c6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
@@ -20,14 +20,15 @@ package org.apache.ambari.server.api.resources;
import java.util.HashSet;
import java.util.Set;
+
+import org.apache.ambari.server.api.query.render.ClusterBlueprintRenderer;
+import org.apache.ambari.server.api.query.render.Renderer;
import org.apache.ambari.server.controller.spi.Resource;
/**
* Cluster resource definition.
*/
public class ClusterResourceDefinition extends BaseResourceDefinition {
-
-
/**
* Constructor.
*/
@@ -47,6 +48,15 @@ public class ClusterResourceDefinition extends BaseResourceDefinition {
}
@Override
+ public Renderer getRenderer(String name) {
+ if (name != null && name.equals("blueprint")) {
+ return new ClusterBlueprintRenderer();
+ } else {
+ return super.getRenderer(name);
+ }
+ }
+
+ @Override
public Set<SubResourceDefinition> getSubResourceDefinitions() {
Set<SubResourceDefinition> setChildren = new HashSet<SubResourceDefinition>();
setChildren.add(new SubResourceDefinition(Resource.Type.Service));
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceDefinition.java
index ba69869..6a169b1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceDefinition.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceDefinition.java
@@ -18,6 +18,7 @@
package org.apache.ambari.server.api.resources;
+import org.apache.ambari.server.api.query.render.Renderer;
import org.apache.ambari.server.api.services.Request;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.api.util.TreeNode;
@@ -69,6 +70,16 @@ public interface ResourceDefinition {
public List<PostProcessor> getPostProcessors();
/**
+ * Obtain the associated renderer based on name.
+ *
+ * @param name name of the renderer to obtain
+ *
+ * @return associated renderer instance
+ * @throws IllegalArgumentException if name is invalid for this resource
+ */
+ public Renderer getRenderer(String name) throws IllegalArgumentException;
+
+ /**
* Resource specific result processor.
* Used to provide resource specific processing of a result.
*/
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseRequest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseRequest.java
index ed7bc45..6d9c13b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseRequest.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseRequest.java
@@ -22,6 +22,7 @@ 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.predicate.QueryLexer;
+import org.apache.ambari.server.api.query.render.Renderer;
import org.apache.ambari.server.api.resources.*;
import org.apache.ambari.server.controller.internal.PageRequestImpl;
import org.apache.ambari.server.controller.internal.TemporalInfoImpl;
@@ -75,6 +76,12 @@ public abstract class BaseRequest implements Request {
private static final int DEFAULT_PAGE_SIZE = 20;
/**
+ * Associated resource renderer.
+ * Will default to the default renderer if non is specified.
+ */
+ private Renderer m_renderer;
+
+ /**
* Logger instance.
*/
private final static Logger LOG = LoggerFactory.getLogger(Request.class);
@@ -104,11 +111,15 @@ public abstract class BaseRequest implements Request {
Result result;
try {
+ parseRenderer();
parseQueryPredicate();
result = getRequestHandler().handleRequest(this);
} catch (InvalidQueryException e) {
result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST,
"Unable to compile query predicate: " + e.getMessage()));
+ } catch (IllegalArgumentException e) {
+ result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST,
+ "Invalid Request: " + e.getMessage()));
}
if (! result.getStatus().isErrorState()) {
@@ -186,6 +197,11 @@ public abstract class BaseRequest implements Request {
}
@Override
+ public Renderer getRenderer() {
+ return m_renderer;
+ }
+
+ @Override
public Map<String, List<String>> getHttpHeaders() {
return m_headers.getRequestHeaders();
}
@@ -229,12 +245,6 @@ public abstract class BaseRequest implements Request {
}
@Override
- public boolean isMinimal() {
- String minimal = m_uriInfo.getQueryParameters().getFirst(QueryLexer.QUERY_MINIMAL);
- return minimal != null && minimal.equalsIgnoreCase("true");
- }
-
- @Override
public RequestBody getBody() {
return m_body;
}
@@ -245,8 +255,7 @@ public abstract class BaseRequest implements Request {
* @return the result post processor
*/
protected ResultPostProcessor getResultPostProcessor() {
- //todo: inject
- return new ResultPostProcessorImpl(this);
+ return m_renderer.getResultPostProcessor(this);
}
/**
@@ -260,6 +269,16 @@ public abstract class BaseRequest implements Request {
}
/**
+ * Check to see if 'minimal_response=true' is specified in the query string.
+ *
+ * @return true if 'minimal_response=true' is specified, false otherwise
+ */
+ private boolean isMinimal() {
+ String minimal = m_uriInfo.getQueryParameters().getFirst(QueryLexer.QUERY_MINIMAL);
+ return minimal != null && minimal.equalsIgnoreCase("true");
+ }
+
+ /**
* 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.
@@ -281,6 +300,17 @@ public abstract class BaseRequest implements Request {
}
/**
+ * Parse the query string for the {@link QueryLexer#QUERY_FORMAT} property and obtain
+ * a renderer from the associated resource definition based on this property value.
+ */
+ private void parseRenderer() {
+ String rendererName = isMinimal() ? "minimal" :
+ m_uriInfo.getQueryParameters().getFirst(QueryLexer.QUERY_FORMAT);
+ m_renderer = m_resource.getResourceDefinition().
+ getRenderer(rendererName);
+ }
+
+ /**
* Obtain the underlying request handler for the request.
*
* @return the request handler
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseService.java
index 8bf7836..8953796 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseService.java
@@ -66,9 +66,7 @@ public abstract class BaseService {
protected Response handleRequest(HttpHeaders headers, String body, UriInfo uriInfo,
Request.Type requestType, ResourceInstance resource) {
- Result result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.OK));
- boolean minimal = false;
-
+ Result result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.OK));
try {
Set<RequestBody> requestBodySet = getBodyParser().parse(body);
@@ -79,7 +77,6 @@ public abstract class BaseService {
Request request = getRequestFactory().createRequest(
headers, requestBody, uriInfo, requestType, resource);
- minimal = request.isMinimal();
result = request.process();
}
} catch (BodyParseException e) {
@@ -87,7 +84,7 @@ public abstract class BaseService {
}
return Response.status(result.getStatus().getStatusCode()).entity(
- getResultSerializer().serialize(result, minimal)).build();
+ getResultSerializer().serialize(result)).build();
}
/**
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/services/Request.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/Request.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/Request.java
index f2de36e..bb53cf6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/Request.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/Request.java
@@ -18,6 +18,7 @@
package org.apache.ambari.server.api.services;
+import org.apache.ambari.server.api.query.render.Renderer;
import org.apache.ambari.server.api.resources.ResourceDefinition;
import org.apache.ambari.server.api.resources.ResourceInstance;
import org.apache.ambari.server.controller.spi.PageRequest;
@@ -116,9 +117,10 @@ public interface Request {
public PageRequest getPageRequest();
/**
- * Is the minimal response parameter specified as true.
+ * Obtain the renderer for the request.
*
- * @return true if the minimal response parameter is specified as true
+ * @return renderer instance
*/
- public boolean isMinimal();
+ public Renderer getRenderer();
+
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ResultPostProcessorImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ResultPostProcessorImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ResultPostProcessorImpl.java
index c02e0a2..bfb1e57 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ResultPostProcessorImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ResultPostProcessorImpl.java
@@ -91,6 +91,17 @@ public class ResultPostProcessorImpl implements ResultPostProcessor {
for (TreeNode<Resource> child : node.getChildren()) {
processNode(child, href);
}
+
+ finalizeNode(node);
+ }
+
+ /**
+ * Allows subclasses to finalize node
+ *
+ * @param node node to finalize
+ */
+ protected void finalizeNode(TreeNode<Resource> node) {
+ // no-op
}
/**
@@ -119,5 +130,4 @@ public class ResultPostProcessorImpl implements ResultPostProcessor {
// always add Request post processors since they may be returned but will not be a child
m_mapPostProcessors.put(Resource.Type.Request, new RequestResourceDefinition().getPostProcessors());
}
-
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java
index 15b2f47..35a9856 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java
@@ -53,7 +53,7 @@ public class JsonSerializer implements ResultSerializer {
@Override
- public Object serialize(Result result, boolean minimal) {
+ public Object serialize(Result result) {
try {
ByteArrayOutputStream bytesOut = init();
@@ -61,7 +61,7 @@ public class JsonSerializer implements ResultSerializer {
return serializeError(result.getStatus());
}
- processNode(result.getResultTree(), minimal);
+ processNode(result.getResultTree());
m_generator.close();
return bytesOut.toString("UTF-8");
@@ -100,13 +100,11 @@ public class JsonSerializer implements ResultSerializer {
return bytesOut;
}
- private void processNode(TreeNode<Resource> node, boolean minimal) throws IOException {
+ private void processNode(TreeNode<Resource> node) throws IOException {
if (isObject(node)) {
m_generator.writeStartObject();
- if (!minimal) {
- writeHref(node);
- }
+ writeHref(node);
Resource r = node.getObject();
if (r != null) {
@@ -118,7 +116,7 @@ public class JsonSerializer implements ResultSerializer {
}
for (TreeNode<Resource> child : node.getChildren()) {
- processNode(child, minimal);
+ processNode(child);
}
if (isArray(node)) {
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/ResultSerializer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/ResultSerializer.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/ResultSerializer.java
index 22b8c88..53b3b80 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/ResultSerializer.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/ResultSerializer.java
@@ -30,11 +30,10 @@ public interface ResultSerializer {
* Serialize the given result to a format expected by client.
*
* @param result internal result
- * @param minimal flag to indicate minimal results
*
* @return the serialized result
*/
- Object serialize(Result result, boolean minimal);
+ Object serialize(Result result);
/**
* Serialize an error result to the format expected by the client.
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNode.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNode.java b/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNode.java
index ffb41fa..3f8abdd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNode.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNode.java
@@ -100,6 +100,13 @@ public interface TreeNode<T> {
public String getProperty(String name);
/**
+ * Remove a property from the node.
+ *
+ * @param name name of property to be removed
+ */
+ public void removeProperty(String name);
+
+ /**
* Find a child node by name.
* The name may contain '/' to delimit names to find a child more then one level deep.
* To find a node named 'bar' that is a child of a child named 'foo', use the name 'foo/bar'.
http://git-wip-us.apache.org/repos/asf/ambari/blob/7eb4bc6b/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNodeImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNodeImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNodeImpl.java
index da7ead4..7c90715 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNodeImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNodeImpl.java
@@ -125,6 +125,13 @@ public class TreeNodeImpl<T> implements TreeNode<T> {
}
@Override
+ public void removeProperty(String name) {
+ if (m_mapNodeProps != null) {
+ m_mapNodeProps.remove(name);
+ }
+ }
+
+ @Override
public TreeNode<T> getChild(String name) {
if (name != null && name.contains("/")) {
int i = name.indexOf('/');