brooklyn.entity.drivers.downloads.DownloadProducerFromProperties Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of brooklyn-core Show documentation
Show all versions of brooklyn-core Show documentation
Entity implementation classes, events, and other core elements
package brooklyn.entity.drivers.downloads;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import brooklyn.config.StringConfigMap;
import brooklyn.entity.Entity;
import brooklyn.entity.basic.Attributes;
import brooklyn.entity.drivers.EntityDriver;
import brooklyn.entity.drivers.downloads.DownloadResolverManager.DownloadRequirement;
import brooklyn.entity.drivers.downloads.DownloadResolverManager.DownloadTargets;
import brooklyn.util.text.Strings;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
* Based on the contents of brooklyn properties, sets up rules for resolving where to
* download artifacts from, for installing entities.
*
* By default, these rules override the DOWNLOAD_URL defined on the entities in code.
* Global properties can be specified that apply to all entities. Entity-specific properties
* can also be specified (which override the global properties for that entity type).
*
* Below is an example of realistic configuration for an enterprise who have an in-house
* repository that must be used for everything, rather than going out to the public internet.
*
* {@code
* // FIXME Check format for including addonname- only if addonname is non-null?
* // FIXME Use this in a testng test case
* brooklyn.downloads.all.url=http://downloads.acme.com/brookyn/repository/${simpletype}/${simpletype}-${addon?? addon-}${version}.${fileSuffix!.tar.gz}
* }
*
*
* To illustrate the features and variations one can use, below is an example of global
* properties that can be specified. The semicolon-separated list of URLs will be tried in-order
* until one succeeds. The fallback url says to use that if all other URLs fail (or no others are
* specified).
*
* {@code
* brooklyn.downloads.all.url=http://myurl1/${simpletype}-${version}.tar.gz; http://myurl2/${simpletype}-${version}.tar.gz
* brooklyn.downloads.all.fallbackurl=http://myurl3/${simpletype}-${version}.tar.gz
* }
*
*
* Similarly, entity-specific properties can be defined. All "global properties" will also apply
* to this entity type, unless explicitly overridden.
*
* {@code
* brooklyn.downloads.entity.tomcatserver.url=http://mytomcaturl1/tomcat-${version}.tar.gz
* brooklyn.downloads.entity.tomcatserver.fallbackurl=http://myurl2/tomcat-${version}.tar.gz
* }
*
*
* Downloads for entity-specific add-ons can also be defined. All "global properties" will also apply
* to this entity type, unless explicitly overridden.
*
* {@code
* brooklyn.downloads.entity.nginxcontroller.addon.stickymodule.url=http://myurl1/nginx-stickymodule-${version}.tar.gz
* brooklyn.downloads.entity.nginxcontroller.addon.stickymodule.fallbackurl=http://myurl2/nginx-stickymodule-${version}.tar.gz
* }
*
*
* If no explicit URLs are supplied, then by default it will use the DOWNLOAD_URL attribute
* of the entity (if supplied), followed by the fallbackurl if that fails.
*
* A URL can be a "template", where things of the form ${version} will be substituted for the value
* of "version" provided for that entity. The freemarker template engine is used to convert URLs
* (see rules = generateRules();
BasicDownloadTargets.Builder result = BasicDownloadTargets.builder();
for (Rule rule : rules) {
if (rule.matches(downloadRequirement.getEntityDriver(), downloadRequirement.getAddonName())) {
result.addAll(rule.resolve(downloadRequirement));
}
}
return result.build();
}
/**
* Produces a set of URL-generating rules, based on the brooklyn properties. These
* rules will be applied in-order until one of them returns a non-empty result.
*/
private List generateRules() {
List result = Lists.newArrayList();
Map subconfig = filterAndStripPrefix(config.asMapWithStringKeys(), DOWNLOAD_CONF_PREFIX);
// If exists, use things like:
// brooklyn.downloads.all.fallbackurl=...
// brooklyn.downloads.all.url=...
// But only if not overridden by more entity-specify value
Map forall = filterAndStripPrefix(subconfig, "all.");
String fallbackUrlForAll = forall.get("fallbackurl");
String urlForAll = forall.get("url");
// If exists, use things like:
// brooklyn.downloads.entity.JBoss7Server.url=...
Map forSpecificEntities = filterAndStripPrefix(subconfig, "entity.");
Map> splitBySpecificEntity = splitByPrefix(forSpecificEntities);
for (Map.Entry> entry : splitBySpecificEntity.entrySet()) {
String entityType = entry.getKey();
Map forentity = entry.getValue();
String urlForEntity = forentity.get("url");
if (urlForEntity == null) urlForEntity = urlForAll;
String fallbackUrlForEntity = forentity.get("fallbackurl");
if (fallbackUrlForEntity == null) fallbackUrlForEntity = fallbackUrlForAll;
result.add(new EntitySpecificRule(entityType, urlForEntity, fallbackUrlForEntity));
// If exists, use things like:
// brooklyn.downloads.entity.nginxcontroller.addon.stickymodule.url=...
Map forSpecificAddons = filterAndStripPrefix(forentity, "addon.");
Map> splitBySpecificAddon = splitByPrefix(forSpecificAddons);
for (Map.Entry> entry2 : splitBySpecificAddon.entrySet()) {
String addonName = entry2.getKey();
Map foraddon = entry2.getValue();
String urlForAddon = foraddon.get("url");
if (urlForAddon == null) urlForAddon = urlForEntity;
String fallbackUrlForAddon = foraddon.get("fallbackurl");
if (fallbackUrlForEntity == null) fallbackUrlForAddon = fallbackUrlForEntity;
result.add(new EntityAddonSpecificRule(entityType, addonName, urlForAddon, fallbackUrlForAddon));
}
}
if (!forall.isEmpty()) {
result.add(new UniversalRule(urlForAll, fallbackUrlForAll));
}
return result;
}
/**
* Returns a sub-map of config for keys that started with the given prefix, but where the returned
* map's keys do not include the prefix.
*/
private static Map filterAndStripPrefix(Map config, String prefix) {
Map result = Maps.newLinkedHashMap();
for (Map.Entry entry : config.entrySet()) {
String key = entry.getKey();
if (key.startsWith(prefix)) {
Object value = entry.getValue();
result.put(key.substring(prefix.length()), (value == null) ? null : value.toString());
}
}
return result;
}
/**
* Splits the map up into multiple maps, using the key's prefix up to the first dot to
* tell which map to include it in. This prefix is used as the key in the map-of-maps, and
* is omitted in the contained map.
*
* For example, given [a.b:v1, a.c:v2, d.e:v3], it will return [ a:[b:v1, c:v2], d:[e:v3] ]
*/
private static Map> splitByPrefix(Map config) {
Map> result = Maps.newLinkedHashMap();
for (Map.Entry entry : config.entrySet()) {
String key = entry.getKey();
String keysuffix = key.substring(key.indexOf(".")+1);
String keyprefix = key.substring(0, key.length()-keysuffix.length()-1);
String value = entry.getValue();
Map submap = result.get(keyprefix);
if (submap == null) {
submap = Maps.newLinkedHashMap();
result.put(keyprefix, submap);
}
submap.put(keysuffix, value);
}
return result;
}
/**
* Resolves the download url, given an EntityDriver, with the following rules:
*
* - If url is not null, split and trim it on ";" and use
*
- If url is null, retrive entity's Attributes.DOWNLOAD_URL and use if non-null
*
- If fallbackUrl is not null, split and trim it on ";" and use
*
*
* For each of the resulting Strings, transforms them (using freemarker syntax for
* substitutions). Returns the list.
*/
private static abstract class Rule {
private final String url;
private final String fallbackUrl;
Rule(String url, String fallbackUrl) {
this.url = url;
this.fallbackUrl = fallbackUrl;
}
abstract boolean matches(EntityDriver driver, String addon);
DownloadTargets resolve(DownloadRequirement req) {
EntityDriver driver = req.getEntityDriver();
List primaries = Lists.newArrayList();
List fallbacks = Lists.newArrayList();
if (Strings.isEmpty(url)) {
String defaulturl = driver.getEntity().getAttribute(Attributes.DOWNLOAD_URL);
if (defaulturl != null) primaries.add(defaulturl);
} else {
String[] parts = url.split(";");
for (String part : parts) {
if (!part.isEmpty()) primaries.add(part.trim());
}
}
if (fallbackUrl != null) {
String[] parts = fallbackUrl.split(";");
for (String part : parts) {
if (!part.isEmpty()) fallbacks.add(part.trim());
}
}
BasicDownloadTargets.Builder result = BasicDownloadTargets.builder();
for (String baseurl : primaries) {
result.addPrimary(DownloadSubstituters.substitute(req, baseurl));
}
for (String baseurl : fallbacks) {
result.addFallback(DownloadSubstituters.substitute(req, baseurl));
}
return result.build();
}
}
/**
* Rule for generating URLs that applies to all entities, if a more specific rule
* did not exist or failed to find a match.
*/
private static class UniversalRule extends Rule {
UniversalRule(String url, String fallbackUrl) {
super(url, fallbackUrl);
}
@Override
boolean matches(EntityDriver driver, String addon) {
return true;
}
}
/**
* Rule for generating URLs that applies to only the entity of the given type.
*/
private static class EntitySpecificRule extends Rule {
private final String entityType;
EntitySpecificRule(String entityType, String url, String fallbackUrl) {
super(url, fallbackUrl);
this.entityType = checkNotNull(entityType, "entityType");
}
@Override
boolean matches(EntityDriver driver, String addon) {
String actualType = driver.getEntity().getEntityType().getName();
String actualSimpleType = actualType.substring(actualType.lastIndexOf(".")+1);
return addon == null && entityType.equalsIgnoreCase(actualSimpleType);
}
}
/**
* Rule for generating URLs that applies to only the entity of the given type.
*/
private static class EntityAddonSpecificRule extends Rule {
private final String entityType;
private final String addonName;
EntityAddonSpecificRule(String entityType, String addonName, String url, String fallbackUrl) {
super(url, fallbackUrl);
this.entityType = checkNotNull(entityType, "entityType");
this.addonName = checkNotNull(addonName, "addonName");
}
@Override
boolean matches(EntityDriver driver, String addon) {
String actualType = driver.getEntity().getEntityType().getName();
String actualSimpleType = actualType.substring(actualType.lastIndexOf(".")+1);
return addonName.equals(addon) && entityType.equalsIgnoreCase(actualSimpleType);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy