/**
* 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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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 java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.controller.spi.Predicate;
import org.apache.ambari.server.controller.spi.PropertyProvider;
import org.apache.ambari.server.controller.spi.Request;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.controller.spi.SystemException;
import org.apache.ambari.server.controller.utilities.StreamProvider;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Clusters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Property provider for host component resources that is used to read HTTP data from another server.
*/
public class HttpPropertyProvider extends BaseProvider implements PropertyProvider {
protected final static Logger LOG = LoggerFactory.getLogger(HttpPropertyProvider.class);
private final StreamProvider streamProvider;
private final String clusterNamePropertyId;
private final String hostNamePropertyId;
private final String componentNamePropertyId;
private final Clusters clusters;
private final Map<String, List<HttpPropertyRequest>> httpPropertyRequests;
// ----- Constructors ------------------------------------------------------
public HttpPropertyProvider(
StreamProvider stream,
Clusters clusters,
String clusterNamePropertyId,
String hostNamePropertyId,
String componentNamePropertyId,
Map<String, List<HttpPropertyRequest>> httpPropertyRequests) {
super(getSupportedProperties(httpPropertyRequests));
this.streamProvider = stream;
this.clusterNamePropertyId = clusterNamePropertyId;
this.hostNamePropertyId = hostNamePropertyId;
this.componentNamePropertyId = componentNamePropertyId;
this.clusters = clusters;
this.httpPropertyRequests = httpPropertyRequests;
}
// ----- PropertyProvider --------------------------------------------------
// get the complete set of Ambari properties that can be set by this property provider.
private static Set<String> getSupportedProperties(Map<String, List<HttpPropertyRequest>> httpPropertyRequests) {
Set<String> supportedProperties = new HashSet<>();
for (List<HttpPropertyRequest> httpPropertyRequestList : httpPropertyRequests.values()) {
for (HttpPropertyRequest httpPropertyRequest : httpPropertyRequestList) {
supportedProperties.addAll(httpPropertyRequest.getSupportedProperties());
}
}
return Collections.unmodifiableSet(supportedProperties);
}
// ----- helper methods ----------------------------------------------------
@Override
public Set<Resource> populateResources(Set<Resource> resources,
Request request, Predicate predicate) throws SystemException {
Set<String> ids = getRequestPropertyIds(request, predicate);
if (ids.size() == 0) {
return resources;
}
for (Resource resource : resources) {
String clusterName = (String) resource.getPropertyValue(clusterNamePropertyId);
String hostName = (String) resource.getPropertyValue(hostNamePropertyId);
String componentName = (String) resource.getPropertyValue(componentNamePropertyId);
if (clusterName != null && hostName != null && componentName != null &&
httpPropertyRequests.containsKey(componentName)) {
try {
Cluster cluster = clusters.getCluster(clusterName);
List<HttpPropertyRequest> httpPropertyRequestList = httpPropertyRequests.get(componentName);
for (HttpPropertyRequest httpPropertyRequest : httpPropertyRequestList) {
populateResource(httpPropertyRequest, resource, cluster, hostName);
}
} catch (AmbariException e) {
String msg = String.format("Could not load cluster with name %s.", clusterName);
LOG.debug(msg, e);
throw new SystemException(msg, e);
}
}
}
return resources;
}
// populate the given resource from the given HTTP property request.
private void populateResource(HttpPropertyRequest httpPropertyRequest, Resource resource,
Cluster cluster, String hostName) throws SystemException {
String url = httpPropertyRequest.getUrl(cluster, hostName);
try {
InputStream inputStream = streamProvider.readFrom(url);
try {
httpPropertyRequest.populateResource(resource, inputStream);
} finally {
try {
inputStream.close();
} catch (IOException ioe) {
LOG.error(String.format("Error closing HTTP response stream %s", url), ioe);
}
}
} catch (Exception e) {
LOG.debug(String.format("Error reading HTTP response from %s", url), e);
}
}
// ----- inner class : HttpPropertyRequest ---------------------------------
/**
* Represents an HTTP request to another server for properties to be
* used to populate an Ambari resource.
*/
public static abstract class HttpPropertyRequest {
private final Map<String, String> propertyMappings;
// ----- Constructors ----------------------------------------------------
protected HttpPropertyRequest(Map<String, String> propertyMappings) {
this.propertyMappings = propertyMappings;
}
// ----- PropertyRequest -------------------------------------------------
/**
* Get the names of the Ambari properties that can be set by this HTTP property request.
*
* @return the supported property names
*/
public Collection<String> getSupportedProperties() {
return propertyMappings.values();
}
/**
* Get the property name mappings from source property to Ambari property.
*
* @return the source to Ambari property name mappings
*/
protected Map<String, String> getPropertyMappings() {
return propertyMappings;
}
/**
* Get the URL used to make the HTTP request.
*
* @param cluster the cluster of the resource being populated
* @param hostName the host name of the resource being populated
* @return the URL to make the HTTP request
*
* @throws SystemException if the URL can not be obtained
*/
public abstract String getUrl(Cluster cluster, String hostName) throws SystemException;
/**
* Populate the given resource from the given input stream.
*
* @param resource the Ambari resource to populate
* @param inputStream the input stream from the HTTP request
*
* @throws SystemException if the resource can not be populated
*/
public abstract void populateResource(Resource resource, InputStream inputStream) throws SystemException;
}
}