blob: c4ef33894f5701221d58ef24d5eb6db5f2d28e80 [file] [log] [blame]
/**
* 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.ResourceInstance;
import org.apache.ambari.server.api.services.ResultImpl;
import org.apache.ambari.server.controller.utilities.ClusterControllerHelper;
import org.apache.ambari.server.controller.utilities.PropertyHelper;
import org.apache.ambari.server.controller.predicate.AndPredicate;
import org.apache.ambari.server.controller.predicate.BasePredicate;
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;
import java.util.*;
/**
* Default read query.
*/
public class QueryImpl implements Query {
/**
* Resource instance.
*/
private ResourceInstance m_resource;
/**
* Properties of the query which make up the select portion of the query.
*/
private Set<String> m_setQueryProperties = new HashSet<String>();
/**
* Indicates that the query should include all available properties.
*/
private boolean allProperties = false;
/**
* Map that associates each property set on the query to temporal data.
*/
private Map<String, TemporalInfo> m_mapPropertyTemporalInfo = new HashMap<String, TemporalInfo>();
/**
* Map that associates categories with temporal data.
*/
private Map<String, TemporalInfo> m_mapCategoryTemporalInfo = new HashMap<String, TemporalInfo>();
/**
* Sub-resources of the resource which is being operated on.
*/
private Map<String, ResourceInstance> m_mapSubResources = new HashMap<String, ResourceInstance>();
/**
* The user supplied predicate.
*/
private Predicate m_userPredicate;
/**
* The logger.
*/
private final static Logger LOG =
LoggerFactory.getLogger(QueryImpl.class);
/**
* Constructor.
*
* @param resource the resource being operated on
*/
public QueryImpl(ResourceInstance resource) {
m_resource = resource;
}
@Override
public void addProperty(String category, String name, TemporalInfo temporalInfo) {
if (category == null && name.equals("*")) {
// wildcard
addAllProperties(temporalInfo);
} else{
if (!addPropertyToSubResource(category, name, temporalInfo)){
String propertyId = PropertyHelper.getPropertyId(category, name.equals("*") ? null : name);
addLocalProperty(propertyId);
if (temporalInfo != null) {
m_mapCategoryTemporalInfo.put(propertyId, temporalInfo);
}
}
}
}
@Override
public void addLocalProperty(String property) {
m_setQueryProperties.add(property);
}
@Override
public Result execute()
throws UnsupportedPropertyException, SystemException, NoSuchResourceException, NoSuchParentResourceException {
Result result = createResult();
Resource.Type resourceType = m_resource.getResourceDefinition().getType();
if (m_resource.getIds().get(resourceType) == null) {
addCollectionProperties(resourceType);
result.getResultTree().setProperty("isCollection", "true");
}
if (m_setQueryProperties.isEmpty() && m_mapSubResources.isEmpty()) {
//Add sub resource properties for default case where no fields are specified.
m_mapSubResources.putAll(m_resource.getSubResources());
}
if (LOG.isInfoEnabled()) {
//todo: include predicate info. Need to implement toString for all predicates.
LOG.info("Executing resource query: " + m_resource.getIds());
}
Predicate predicate = createPredicate(m_resource);
Iterable<Resource> iterResource = getClusterController().getResources(
resourceType, createRequest(), predicate);
TreeNode<Resource> tree = result.getResultTree();
int count = 1;
for (Resource resource : iterResource) {
// add a child node for the resource and provide a unique name. The name is never used.
//todo: provide a more meaningful node name
TreeNode<Resource> node = tree.addChild(resource, resource.getType() + ":" + count++);
for (Map.Entry<String, ResourceInstance> entry : m_mapSubResources.entrySet()) {
String subResCategory = entry.getKey();
ResourceInstance r = entry.getValue();
setParentIdsOnSubResource(resource, r);
TreeNode<Resource> childResult = r.getQuery().execute().getResultTree();
childResult.setName(subResCategory);
childResult.setProperty("isCollection", "false");
node.addChild(childResult);
}
}
return result;
}
@Override
public Predicate getPredicate() {
//todo: create predicate once
return createPredicate(m_resource);
}
@Override
public Set<String> getProperties() {
return Collections.unmodifiableSet(m_setQueryProperties);
}
@Override
public void setUserPredicate(Predicate predicate) {
m_userPredicate = predicate;
}
ClusterController getClusterController() {
return ClusterControllerHelper.getClusterController();
}
private void addCollectionProperties(Resource.Type resourceType) {
Schema schema = getClusterController().getSchema(resourceType);
// add pk
String property = schema.getKeyPropertyId(resourceType);
addProperty(PropertyHelper.getPropertyCategory(property), PropertyHelper.getPropertyName(property), null);
for (Resource.Type type : m_resource.getIds().keySet()) {
// add fk's
String keyPropertyId = schema.getKeyPropertyId(type);
//todo: property id can be null in some cases such as host_component queries which obtain
//todo: component sub-resources. Component will not have host fk.
//todo: refactor so that null check is not required.
if (keyPropertyId != null) {
addProperty(PropertyHelper.getPropertyCategory(keyPropertyId), PropertyHelper.getPropertyName(keyPropertyId), null);
}
}
}
private void addAllProperties(TemporalInfo temporalInfo) {
allProperties = true;
if (temporalInfo != null) {
m_mapCategoryTemporalInfo.put(null, temporalInfo);
}
for (Map.Entry<String, ResourceInstance> entry : m_resource.getSubResources().entrySet()) {
String name = entry.getKey();
if (! m_mapSubResources.containsKey(name)) {
m_mapSubResources.put(name, entry.getValue());
}
}
}
private boolean addPropertyToSubResource(String path, String property, TemporalInfo temporalInfo) {
// cases:
// - path is null, property is path (all sub-resource props will have a path)
// - path is single token and prop in non null
// (path only will presented as above case with property only)
// - path is multi level and prop is non null
boolean resourceAdded = false;
if (path == null) {
path = property;
property = null;
}
int i = path.indexOf("/");
String p = i == -1 ? path : path.substring(0, i);
ResourceInstance subResource = m_resource.getSubResources().get(p);
if (subResource != null) {
m_mapSubResources.put(p, subResource);
//todo: handle case of trailing '/' (for example fields=subResource/)
if (property != null || !path.equals(p)) {
//only add if a sub property is set or if a sub category is specified
subResource.getQuery().addProperty(i == -1 ? null : path.substring(i + 1), property, temporalInfo);
}
resourceAdded = true;
}
return resourceAdded;
}
private BasePredicate createInternalPredicate(ResourceInstance resource) {
Resource.Type resourceType = resource.getResourceDefinition().getType();
Map<Resource.Type, String> mapResourceIds = resource.getIds();
Schema schema = getClusterController().getSchema(resourceType);
Set<BasePredicate> setPredicates = new HashSet<BasePredicate>();
for (Map.Entry<Resource.Type, String> entry : mapResourceIds.entrySet()) {
if (entry.getValue() != null) {
String keyPropertyId = schema.getKeyPropertyId(entry.getKey());
if (keyPropertyId != null) {
setPredicates.add(new EqualsPredicate<String>(keyPropertyId, entry.getValue()));
}
}
}
if (setPredicates.size() == 1) {
return setPredicates.iterator().next();
} else if (setPredicates.size() > 1) {
return new AndPredicate(setPredicates.toArray(new BasePredicate[setPredicates.size()]));
} else {
return null;
}
}
private Predicate createPredicate(ResourceInstance resource) {
Predicate predicate = null;
//todo: change reference type to Predicate when predicate hierarchy is fixed
BasePredicate internalPredicate = createInternalPredicate(resource);
if (internalPredicate == null) {
if (m_userPredicate != null) {
predicate = m_userPredicate;
}
} else {
predicate = (m_userPredicate == null ? internalPredicate :
new AndPredicate((BasePredicate) m_userPredicate, internalPredicate));
}
return predicate;
}
private Request createRequest() {
Set<String> setProperties = new HashSet<String>();
Map<String, TemporalInfo> mapTemporalInfo = new HashMap<String, TemporalInfo>();
TemporalInfo globalTemporalInfo = m_mapCategoryTemporalInfo.get(null);
for (String group : m_setQueryProperties) {
TemporalInfo temporalInfo = m_mapCategoryTemporalInfo.get(group);
if (temporalInfo != null) {
mapTemporalInfo.put(group, temporalInfo);
} else if (globalTemporalInfo != null) {
mapTemporalInfo.put(group, globalTemporalInfo);
}
setProperties.add(group);
}
return PropertyHelper.getReadRequest(allProperties ? Collections.<String>emptySet() : setProperties, mapTemporalInfo);
}
private void setParentIdsOnSubResource(Resource resource, ResourceInstance r) {
Map<Resource.Type, String> mapParentIds = m_resource.getIds();
Map<Resource.Type, String> mapResourceIds = new HashMap<Resource.Type, String>(mapParentIds.size());
for (Map.Entry<Resource.Type, String> resourceIdEntry : mapParentIds.entrySet()) {
Resource.Type type = resourceIdEntry.getKey();
String value = resourceIdEntry.getValue();
if (value == null) {
Object o = resource.getPropertyValue(getClusterController().getSchema(type).getKeyPropertyId(type));
value = o == null ? null : o.toString();
}
if (value != null) {
mapResourceIds.put(type, value);
}
}
String resourceKeyProp = getClusterController().getSchema(resource.getType()).
getKeyPropertyId(resource.getType());
//todo: shouldn't use toString here
mapResourceIds.put(resource.getType(), resource.getPropertyValue(resourceKeyProp).toString());
r.setIds(mapResourceIds);
}
Result createResult() {
return new ResultImpl(true);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
QueryImpl that = (QueryImpl) o;
return m_mapCategoryTemporalInfo.equals(that.m_mapCategoryTemporalInfo) &&
m_mapPropertyTemporalInfo.equals(that.m_mapPropertyTemporalInfo) &&
m_setQueryProperties.equals(that.m_setQueryProperties) &&
m_mapSubResources.equals(that.m_mapSubResources) &&
m_resource.equals(that.m_resource) &&
m_userPredicate == null ? that.m_userPredicate == null : m_userPredicate.equals(that.m_userPredicate);
}
@Override
public int hashCode() {
int result = m_resource.hashCode();
result = 31 * result + m_setQueryProperties.hashCode();
result = 31 * result + m_mapPropertyTemporalInfo.hashCode();
result = 31 * result + m_mapCategoryTemporalInfo.hashCode();
result = 31 * result + m_mapSubResources.hashCode();
result = 31 * result + (m_userPredicate != null ? m_userPredicate.hashCode() : 0);
return result;
}
}