blob: 040a1f1d8f1ab001f674ba5e819a6e75e34585bb [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.handlers;
import org.apache.ambari.server.api.resources.ResourceInstance;
import org.apache.ambari.server.api.resources.ResourceInstanceFactory;
import org.apache.ambari.server.api.resources.ResourceInstanceFactoryImpl;
import org.apache.ambari.server.api.services.*;
import org.apache.ambari.server.api.services.Request;
import org.apache.ambari.server.api.util.TreeNode;
import org.apache.ambari.server.controller.spi.*;
import java.util.*;
/**
* Handler for creates that are applied to the results of a query.
*/
public class QueryCreateHandler extends BaseManagementHandler {
private RequestHandler m_readHandler = new ReadHandler();
@Override
public Result handleRequest(Request request) {
Result queryResult = getReadHandler().handleRequest(request);
if (queryResult.getStatus().isErrorState() ||
queryResult.getResultTree().getChildren().isEmpty()) {
// if query result has error state or contains no resources just return it.
// currently returns 200 for case where query returns no rows
return queryResult;
}
Map<Resource.Type, Set<Map<String, Object>>> mapProperties;
try {
mapProperties = buildCreateSet(request, queryResult);
} catch (IllegalArgumentException e) {
return createInvalidRequestResult(e.getMessage());
}
if (mapProperties.size() != 1) {
return createInvalidRequestResult(mapProperties.size() == 0 ?
"A minimum of one sub-resource must be specified for creation." :
"Multiple sub-resource types may not be created in the same request.");
}
// only get first element because we currently only support creation of a single sub-resource type
final Map.Entry<Resource.Type, Set<Map<String, Object>>> entry = mapProperties.entrySet().iterator().next();
ResourceInstance createResource = getResourceFactory().createResource(
entry.getKey(), request.getResource().getKeyValueMap());
RequestBody requestBody = new RequestBody();
requestBody.setBody(request.getBody().getBody());
for (Map<String, Object> map : entry.getValue()) {
requestBody.addPropertySet(new NamedPropertySet("", map));
}
return persist(createResource,
requestBody
);
}
/**
* Build the property set for all sub-resource to be created.
* This includes determining the sub-resource type and creating a property set for each matching parent.
*
* @param request the current request
* @param queryResult the result of the query for matching parents
*
* @return a map of sub-resource types to be created and their associated properties
*
* @throws IllegalArgumentException if no sub-resource type was specified or it is not a valid
* sub-resource of the parent.
*/
private Map<Resource.Type, Set<Map<String, Object>>> buildCreateSet(Request request, Result queryResult)
throws IllegalArgumentException {
Set<NamedPropertySet> setRequestProps = request.getBody().getNamedPropertySets();
HashMap<Resource.Type, Set<Map<String, Object>>> mapProps =
new HashMap<Resource.Type, Set<Map<String, Object>>>();
ResourceInstance resource = request.getResource();
Resource.Type type = resource.getResourceDefinition().getType();
ClusterController controller = getClusterController();
String resourceKeyProperty = controller.getSchema(type).getKeyPropertyId(type);
TreeNode<Resource> tree = queryResult.getResultTree();
Collection<TreeNode<Resource>> treeChildren = tree.getChildren();
for (TreeNode<Resource> node : treeChildren) {
Resource r = node.getObject();
Object keyVal = r.getPropertyValue(resourceKeyProperty);
for (NamedPropertySet namedProps : setRequestProps) {
for (Map.Entry<String, Object> entry : namedProps.getProperties().entrySet()) {
Set<Map<String, Object>> set = (Set<Map<String, Object>>) entry.getValue();
for (Map<String, Object> map : set) {
Map<String, Object> mapResourceProps = new HashMap<String, Object>(map);
Resource.Type createType = getCreateType(resource, entry.getKey());
mapResourceProps.put(controller.getSchema(createType).
getKeyPropertyId(resource.getResourceDefinition().getType()), keyVal);
Set<Map<String, Object>> setCreateProps = mapProps.get(createType);
if (setCreateProps == null) {
setCreateProps = new HashSet<Map<String, Object>>();
mapProps.put(createType, setCreateProps);
}
setCreateProps.add(mapResourceProps);
}
}
}
}
return mapProps;
}
/**
* Determine the sub-resource type(s) to be created.
*
* @param resource the requests resource instance
* @param subResourceName the name of the sub-resource to be created
* @return the resource type
*
* @throws IllegalArgumentException if the specified sub-resource name is empty or it is not a valid
* sub-resource of the parent.
*/
private Resource.Type getCreateType(ResourceInstance resource, String subResourceName) throws IllegalArgumentException{
if (subResourceName == null || subResourceName.equals("")) {
throw new IllegalArgumentException("A sub-resource name must be supplied.");
}
ResourceInstance res = resource.getSubResources().get(subResourceName);
if (res == null) {
throw new IllegalArgumentException("The specified sub-resource name is not valid: '" + subResourceName + "'.");
}
return res.getResourceDefinition().getType();
}
/**
* Convenience method to create a result for invalid requests.
*
* @param msg message indicating why the request is invalid
*
* @return a request with a 400 status and msg set
*/
private Result createInvalidRequestResult(String msg) {
return new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST, "Invalid Request: " + msg));
}
@Override
protected Result persist(ResourceInstance resource, RequestBody body) {
Result result;
try {
RequestStatus status = getPersistenceManager().create(resource, body);
result = createResult(status);
if (result.isSynchronous()) {
result.setResultStatus(new ResultStatus(ResultStatus.STATUS.CREATED));
} else {
result.setResultStatus(new ResultStatus(ResultStatus.STATUS.ACCEPTED));
}
} catch (UnsupportedPropertyException e) {
result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST, e));
} catch (ResourceAlreadyExistsException e) {
result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.CONFLICT, e));
} catch (NoSuchParentResourceException e) {
result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.NOT_FOUND, e));
} catch (SystemException e) {
result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.SERVER_ERROR, e));
}
return result;
}
/**
* Get the resource factory instance.
* @return a factory for creating resource instances
*/
protected ResourceInstanceFactory getResourceFactory() {
//todo: inject
return new ResourceInstanceFactoryImpl();
}
/**
* Read handler instance. Used for obtaining matching parents which match the query.
*
* @return read handler instance
*/
protected RequestHandler getReadHandler() {
return m_readHandler;
}
}