blob: 609116970f5b011e10567665f9798c3450055b37 [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.controller.internal;
import org.apache.ambari.server.controller.spi.ProviderModule;
import org.apache.ambari.server.controller.spi.*;
import org.apache.ambari.server.controller.utilities.PredicateBuilder;
import org.apache.ambari.server.controller.utilities.PredicateHelper;
import org.apache.ambari.server.controller.utilities.PropertyHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* Default cluster controller implementation.
*/
public class ClusterControllerImpl implements ClusterController {
private final static Logger LOG =
LoggerFactory.getLogger(ClusterControllerImpl.class);
/**
* Module of providers for this controller.
*/
private final ProviderModule providerModule;
/**
* Map of resource providers keyed by resource type.
*/
private final Map<Resource.Type, ResourceProvider> resourceProviders =
new HashMap<Resource.Type, ResourceProvider>();
/**
* Map of property provider lists keyed by resource type.
*/
private final Map<Resource.Type, List<PropertyProvider>> propertyProviders =
new HashMap<Resource.Type, List<PropertyProvider>>();
/**
* Map of schemas keyed by resource type.
*/
private final Map<Resource.Type, Schema> schemas =
new HashMap<Resource.Type, Schema>();
// ----- Constructors ------------------------------------------------------
public ClusterControllerImpl(ProviderModule providerModule) {
this.providerModule = providerModule;
}
// ----- ClusterController -------------------------------------------------
@Override
public Iterable<Resource> getResources(Resource.Type type, Request request, Predicate predicate)
throws UnsupportedPropertyException,
SystemException,
NoSuchParentResourceException,
NoSuchResourceException {
ResourceProvider provider = ensureResourceProvider(type);
ensurePropertyProviders(type);
Set<Resource> resources;
if (provider == null) {
resources = Collections.emptySet();
} else {
LOG.info("Using resource provider "
+ provider.getClass().getName()
+ " for request type " + type.toString());
checkProperties(type, request, predicate);
resources = provider.getResources(request, predicate);
resources = populateResources(type, resources, request, predicate);
}
return new ResourceIterable(resources, predicate);
}
@Override
public Schema getSchema(Resource.Type type) {
Schema schema;
synchronized (schemas) {
schema = schemas.get(type);
if (schema == null) {
schema = new SchemaImpl(ensureResourceProvider(type));
schemas.put(type, schema);
}
}
return schema;
}
@Override
public RequestStatus createResources(Resource.Type type, Request request)
throws UnsupportedPropertyException,
SystemException,
ResourceAlreadyExistsException,
NoSuchParentResourceException {
ResourceProvider provider = ensureResourceProvider(type);
if (provider != null) {
checkProperties(type, request, null);
return provider.createResources(request);
}
return null;
}
@Override
public RequestStatus updateResources(Resource.Type type, Request request, Predicate predicate)
throws UnsupportedPropertyException,
SystemException,
NoSuchResourceException,
NoSuchParentResourceException {
ResourceProvider provider = ensureResourceProvider(type);
if (provider != null) {
if (!checkProperties(type, request, predicate)) {
predicate = resolvePredicate(type, predicate);
if (predicate == null) {
return null;
}
}
return provider.updateResources(request, predicate);
}
return null;
}
@Override
public RequestStatus deleteResources(Resource.Type type, Predicate predicate)
throws UnsupportedPropertyException,
SystemException,
NoSuchResourceException,
NoSuchParentResourceException {
ResourceProvider provider = ensureResourceProvider(type);
if (provider != null) {
if (!checkProperties(type, null, predicate)) {
predicate = resolvePredicate(type, predicate);
if (predicate == null) {
return null;
}
}
return provider.deleteResources(predicate);
}
return null;
}
// ----- helper methods ----------------------------------------------------
/**
* Check to make sure that all the property ids specified in the given request and
* predicate are supported by the resource provider or property providers for the
* given type.
*
* @param type the resource type
* @param request the request
* @param predicate the predicate
*
* @return true if all of the properties specified in the request and predicate are supported by
* the resource provider for the given type; false if any of the properties specified in
* the request and predicate are not supported by the resource provider but are supported
* by a property provider for the given type.
*
* @throws UnsupportedPropertyException thrown if any of the properties specified in the request
* and predicate are not supported by either the resource
* provider or a property provider for the given type
*/
private boolean checkProperties(Resource.Type type, Request request, Predicate predicate)
throws UnsupportedPropertyException {
Set<String> requestPropertyIds = request == null ? new HashSet<String>() :
PropertyHelper.getAssociatedPropertyIds(request);
if (predicate != null) {
requestPropertyIds.addAll(PredicateHelper.getPropertyIds(predicate));
}
if (requestPropertyIds.size() > 0) {
ResourceProvider provider = ensureResourceProvider(type);
requestPropertyIds = provider.checkPropertyIds(requestPropertyIds);
if (requestPropertyIds.size() > 0) {
List<PropertyProvider> propertyProviders = ensurePropertyProviders(type);
for (PropertyProvider propertyProvider : propertyProviders) {
requestPropertyIds = propertyProvider.checkPropertyIds(requestPropertyIds);
if (requestPropertyIds.size() == 0) {
return false;
}
}
throw new UnsupportedPropertyException(type, requestPropertyIds);
}
}
return true;
}
/**
* Check to see if any of the property ids specified in the given request and
* predicate are handled by an associated property provider. if so, then use
* the given predicate to obtain a new predicate that can be completely
* processed by an update or delete operation on a resource provider for
* the given resource type. This means that the new predicate should only
* reference the key property ids for this type.
*
* @param type the resource type
* @param predicate the predicate
*
* @return the given predicate if a new one is not required; a new predicate if required
*
* @throws UnsupportedPropertyException thrown if any of the properties specified in the request
* and predicate are not supported by either the resource
* provider or a property provider for the given type
*
* @throws SystemException thrown for internal exceptions
* @throws NoSuchResourceException if the resource that is requested doesn't exist
* @throws NoSuchParentResourceException if a parent resource of the requested resource doesn't exist
*/
private Predicate resolvePredicate(Resource.Type type, Predicate predicate)
throws UnsupportedPropertyException,
SystemException,
NoSuchResourceException,
NoSuchParentResourceException{
ResourceProvider provider = ensureResourceProvider(type);
Set<String> keyPropertyIds = new HashSet<String>(provider.getKeyPropertyIds().values());
Request readRequest = PropertyHelper.getReadRequest(keyPropertyIds);
Iterable<Resource> resources = getResources(type, readRequest, predicate);
PredicateBuilder pb = new PredicateBuilder();
PredicateBuilder.PredicateBuilderWithPredicate pbWithPredicate = null;
for (Resource resource : resources) {
if (pbWithPredicate != null) {
pb = pbWithPredicate.or();
}
pb = pb.begin();
pbWithPredicate = null;
for (String keyPropertyId : keyPropertyIds) {
if (pbWithPredicate != null) {
pb = pbWithPredicate.and();
}
pbWithPredicate =
pb.property(keyPropertyId).equals((Comparable) resource.getPropertyValue(keyPropertyId));
}
if (pbWithPredicate != null) {
pbWithPredicate = pbWithPredicate.end();
}
}
return pbWithPredicate == null ? null : pbWithPredicate.toPredicate();
}
/**
* Populate the given resources from the associated property providers. This
* method may filter the resources based on the predicate and return a subset
* of the given resources.
*
* @param type the resource type
* @param resources the resources to be populated
* @param request the request
* @param predicate the predicate
*
* @return the set of resources that were successfully populated
*
* @throws SystemException if unable to populate the resources
*/
private Set<Resource> populateResources(Resource.Type type,
Set<Resource> resources,
Request request,
Predicate predicate) throws SystemException {
Set<Resource> keepers = resources;
for (PropertyProvider propertyProvider : propertyProviders.get(type)) {
if (providesRequestProperties(propertyProvider, request, predicate)) {
keepers = propertyProvider.populateResources(keepers, request, predicate);
}
}
return keepers;
}
/**
* Indicates whether or not the given property provider can service the given request.
*
* @param provider the property provider
* @param request the request
* @param predicate the predicate
*
* @return true if the given provider can service the request
*/
private boolean providesRequestProperties(PropertyProvider provider, Request request, Predicate predicate) {
Set<String> requestPropertyIds = new HashSet<String>(request.getPropertyIds());
if (requestPropertyIds.size() == 0) {
return true;
}
requestPropertyIds.addAll(PredicateHelper.getPropertyIds(predicate));
int size = requestPropertyIds.size();
return size > provider.checkPropertyIds(requestPropertyIds).size();
}
/**
* Get the resource provider for the given type, creating it if required.
*
* @param type the resource type
*
* @return the resource provider
*/
private ResourceProvider ensureResourceProvider(Resource.Type type) {
synchronized (resourceProviders) {
if (!resourceProviders.containsKey(type)) {
resourceProviders.put(type, providerModule.getResourceProvider(type));
}
}
return resourceProviders.get(type);
}
/**
* Get the list of property providers for the given type.
*
* @param type the resource type
*
* @return the list of property providers
*/
private List<PropertyProvider> ensurePropertyProviders(Resource.Type type) {
synchronized (propertyProviders) {
if (!propertyProviders.containsKey(type)) {
propertyProviders.put(type, providerModule.getPropertyProviders(type));
}
}
return propertyProviders.get(type);
}
// ----- ResourceIterable inner class --------------------------------------
private static class ResourceIterable implements Iterable<Resource> {
/**
* The resources to iterate over.
*/
private final Set<Resource> resources;
/**
* The predicate used to filter the set.
*/
private final Predicate predicate;
// ----- Constructors ----------------------------------------------------
/**
* Create a ResourceIterable.
*
* @param resources the set of resources to iterate over
* @param predicate the predicate used to filter the set of resources
*/
private ResourceIterable(Set<Resource> resources, Predicate predicate) {
this.resources = resources;
this.predicate = predicate;
}
// ----- Iterable --------------------------------------------------------
@Override
public Iterator<Resource> iterator() {
return new ResourceIterator(resources, predicate);
}
}
// ----- ResourceIterator inner class --------------------------------------
private static class ResourceIterator implements Iterator<Resource> {
/**
* The underlying iterator.
*/
private final Iterator<Resource> iterator;
/**
* The predicate used to filter the resource being iterated over.
*/
private final Predicate predicate;
/**
* The next resource.
*/
private Resource nextResource;
// ----- Constructors ----------------------------------------------------
/**
* Create a new ResourceIterator.
*
* @param resources the set of resources to iterate over
* @param predicate the predicate used to filter the set of resources
*/
private ResourceIterator(Set<Resource> resources, Predicate predicate) {
this.iterator = resources.iterator();
this.predicate = predicate;
this.nextResource = getNextResource();
}
// ----- Iterator --------------------------------------------------------
@Override
public boolean hasNext() {
return nextResource != null;
}
@Override
public Resource next() {
if (nextResource == null) {
throw new NoSuchElementException("Iterator has no more elements.");
}
Resource currentResource = nextResource;
this.nextResource = getNextResource();
return currentResource;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Remove not supported.");
}
// ----- helper methods --------------------------------------------------
/**
* Get the next resource.
*
* @return the next resource.
*/
private Resource getNextResource() {
while (iterator.hasNext()) {
Resource next = iterator.next();
if (predicate == null || predicate.evaluate(next)) {
return next;
}
}
return null;
}
}
}