Java tutorial
/* * 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 brooklyn.catalog.internal; import static com.google.common.base.Preconditions.checkNotNull; import io.brooklyn.camp.CampPlatform; import io.brooklyn.camp.spi.AssemblyTemplate; import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator; import io.brooklyn.camp.spi.pdp.DeploymentPlan; import io.brooklyn.camp.spi.pdp.Service; import java.io.FileNotFoundException; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.NoSuchElementException; import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import brooklyn.basic.AbstractBrooklynObjectSpec; import brooklyn.camp.brooklyn.api.AssemblyTemplateSpecInstantiator; import brooklyn.catalog.BrooklynCatalog; import brooklyn.catalog.CatalogItem; import brooklyn.catalog.CatalogItem.CatalogBundle; import brooklyn.catalog.CatalogPredicates; import brooklyn.config.BrooklynServerConfig; import brooklyn.entity.proxying.EntitySpec; import brooklyn.management.ManagementContext; import brooklyn.management.classloading.BrooklynClassLoadingContext; import brooklyn.management.internal.EntityManagementUtils; import brooklyn.management.internal.ManagementContextInternal; import brooklyn.policy.Policy; import brooklyn.policy.PolicySpec; import brooklyn.util.collections.MutableMap; import brooklyn.util.collections.MutableSet; import brooklyn.util.exceptions.Exceptions; import brooklyn.util.guava.Maybe; import brooklyn.util.javalang.AggregateClassLoader; import brooklyn.util.javalang.LoadedClassLoader; import brooklyn.util.javalang.Reflections; import brooklyn.util.stream.Streams; import brooklyn.util.text.Strings; import brooklyn.util.time.Duration; import brooklyn.util.time.Time; import brooklyn.util.yaml.Yamls; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; public class BasicBrooklynCatalog implements BrooklynCatalog { private static final String POLICIES_KEY = "brooklyn.policies"; public static final String NO_VERSION = "0.0.0.SNAPSHOT"; private static final Logger log = LoggerFactory.getLogger(BasicBrooklynCatalog.class); public static class BrooklynLoaderTracker { public static final ThreadLocal<BrooklynClassLoadingContext> loader = new ThreadLocal<BrooklynClassLoadingContext>(); public static void setLoader(BrooklynClassLoadingContext val) { loader.set(val); } // TODO Stack, for recursive calls? public static void unsetLoader(BrooklynClassLoadingContext val) { loader.set(null); } public static BrooklynClassLoadingContext getLoader() { return loader.get(); } } private final ManagementContext mgmt; private CatalogDo catalog; private volatile CatalogDo manualAdditionsCatalog; private volatile LoadedClassLoader manualAdditionsClasses; public BasicBrooklynCatalog(ManagementContext mgmt) { this(mgmt, CatalogDto.newNamedInstance("empty catalog", "empty catalog", "empty catalog, expected to be reset later")); } public BasicBrooklynCatalog(ManagementContext mgmt, CatalogDto dto) { this.mgmt = checkNotNull(mgmt, "managementContext"); this.catalog = new CatalogDo(mgmt, dto); } public boolean blockIfNotLoaded(Duration timeout) { try { return getCatalog().blockIfNotLoaded(timeout); } catch (Exception e) { throw Exceptions.propagate(e); } } public void reset(CatalogDto dto) { // Unregister all existing persisted items. for (CatalogItem<?, ?> toRemove : getCatalogItems()) { if (log.isTraceEnabled()) { log.trace("Scheduling item for persistence removal: {}", toRemove.getId()); } mgmt.getRebindManager().getChangeListener().onUnmanaged(toRemove); } CatalogDo catalog = new CatalogDo(mgmt, dto); CatalogUtils.logDebugOrTraceIfRebinding(log, "Resetting " + this + " catalog to " + dto); catalog.load(mgmt, null); CatalogUtils.logDebugOrTraceIfRebinding(log, "Reloaded catalog for " + this + ", now switching"); this.catalog = catalog; // Inject management context into and persist all the new entries. for (CatalogItem<?, ?> entry : getCatalogItems()) { boolean setManagementContext = false; if (entry instanceof CatalogItemDo) { CatalogItemDo<?, ?> cid = CatalogItemDo.class.cast(entry); if (cid.getDto() instanceof CatalogItemDtoAbstract) { CatalogItemDtoAbstract<?, ?> cdto = CatalogItemDtoAbstract.class.cast(cid.getDto()); if (cdto.getManagementContext() == null) { cdto.setManagementContext((ManagementContextInternal) mgmt); } setManagementContext = true; } } if (!setManagementContext) { log.warn("Can't set management context on entry with unexpected type in catalog. type={}, " + "expected={}", entry, CatalogItemDo.class); } if (log.isTraceEnabled()) { log.trace("Scheduling item for persistence addition: {}", entry.getId()); } mgmt.getRebindManager().getChangeListener().onManaged(entry); } } /** * Resets the catalog to the given entries */ @Override public void reset(Collection<CatalogItem<?, ?>> entries) { CatalogDto newDto = CatalogDto.newDtoFromCatalogItems(entries, "explicit-catalog-reset"); reset(newDto); } public CatalogDo getCatalog() { return catalog; } protected CatalogItemDo<?, ?> getCatalogItemDo(String symbolicName, String version) { String fixedVersionId = getFixedVersionId(symbolicName, version); if (fixedVersionId == null) { //no items with symbolicName exist return null; } String versionedId = CatalogUtils.getVersionedId(symbolicName, fixedVersionId); CatalogItemDo<?, ?> item = null; //TODO should remove "manual additions" bucket; just have one map a la osgi if (manualAdditionsCatalog != null) item = manualAdditionsCatalog.getIdCache().get(versionedId); if (item == null) item = catalog.getIdCache().get(versionedId); return item; } private String getFixedVersionId(String symbolicName, String version) { if (!DEFAULT_VERSION.equals(version)) { return version; } else { return getDefaultVersion(symbolicName); } } private String getDefaultVersion(String symbolicName) { Iterable<CatalogItem<Object, Object>> versions = getCatalogItems( CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName))); Collection<CatalogItem<Object, Object>> orderedVersions = sortVersionsDesc(versions); if (!orderedVersions.isEmpty()) { return orderedVersions.iterator().next().getVersion(); } else { return null; } } private <T, SpecT> Collection<CatalogItem<T, SpecT>> sortVersionsDesc( Iterable<CatalogItem<T, SpecT>> versions) { return ImmutableSortedSet.orderedBy(CatalogItemComparator.<T, SpecT>getInstance()).addAll(versions).build(); } @Override @Deprecated public CatalogItem<?, ?> getCatalogItem(String symbolicName) { return getCatalogItem(symbolicName, DEFAULT_VERSION); } @Override public CatalogItem<?, ?> getCatalogItem(String symbolicName, String version) { if (symbolicName == null) return null; checkNotNull(version, "version"); CatalogItemDo<?, ?> itemDo = getCatalogItemDo(symbolicName, version); if (itemDo == null) return null; return itemDo.getDto(); } @Override @Deprecated public void deleteCatalogItem(String id) { //Delete only if installed through the //deprecated methods. Don't support DEFAULT_VERSION for delete. deleteCatalogItem(id, NO_VERSION); } @Override public void deleteCatalogItem(String symbolicName, String version) { log.debug("Deleting manual catalog item from " + mgmt + ": " + symbolicName + ":" + version); checkNotNull(symbolicName, "id"); checkNotNull(version, "version"); if (DEFAULT_VERSION.equals(version)) { throw new IllegalStateException( "Deleting items with unspecified version (argument DEFAULT_VERSION) not supported."); } CatalogItem<?, ?> item = getCatalogItem(symbolicName, version); CatalogItemDtoAbstract<?, ?> itemDto = getAbstractCatalogItem(item); if (itemDto == null) { throw new NoSuchElementException("No catalog item found with id " + symbolicName); } if (manualAdditionsCatalog == null) loadManualAdditionsCatalog(); manualAdditionsCatalog.deleteEntry(itemDto); // Ensure the cache is de-populated getCatalog().deleteEntry(itemDto); // And indicate to the management context that it should be removed. if (log.isTraceEnabled()) { log.trace("Scheduling item for persistence removal: {}", itemDto.getId()); } mgmt.getRebindManager().getChangeListener().onUnmanaged(itemDto); } @Override @Deprecated public <T, SpecT> CatalogItem<T, SpecT> getCatalogItem(Class<T> type, String id) { return getCatalogItem(type, id, DEFAULT_VERSION); } @SuppressWarnings("unchecked") @Override public <T, SpecT> CatalogItem<T, SpecT> getCatalogItem(Class<T> type, String id, String version) { if (id == null || version == null) return null; CatalogItem<?, ?> result = getCatalogItem(id, version); if (result == null) return null; if (type == null || type.isAssignableFrom(result.getCatalogItemJavaType())) return (CatalogItem<T, SpecT>) result; return null; } @Override public ClassLoader getRootClassLoader() { return catalog.getRootClassLoader(); } /** * Loads this catalog. No effect if already loaded. */ public void load() { log.debug("Loading catalog for " + mgmt); getCatalog().load(mgmt, null); if (log.isDebugEnabled()) { log.debug("Loaded catalog for " + mgmt + ": " + catalog + "; search classpath is " + catalog.getRootClassLoader()); } } @SuppressWarnings("unchecked") @Override public <T, SpecT> SpecT createSpec(CatalogItem<T, SpecT> item) { CatalogItemDo<T, SpecT> loadedItem = (CatalogItemDo<T, SpecT>) getCatalogItemDo(item.getSymbolicName(), item.getVersion()); if (loadedItem == null) return null; Class<SpecT> specType = loadedItem.getSpecType(); if (specType == null) return null; String yaml = loadedItem.getPlanYaml(); if (yaml != null) { DeploymentPlan plan = makePlanFromYaml(yaml); BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, item); SpecT spec; switch (item.getCatalogItemType()) { case TEMPLATE: case ENTITY: spec = createEntitySpec(loadedItem.getSymbolicName(), plan, loader); break; case POLICY: spec = createPolicySpec(plan, loader); break; default: throw new RuntimeException( "Only entity & policy catalog items are supported. Unsupported catalog item type " + item.getCatalogItemType()); } ((AbstractBrooklynObjectSpec<?, ?>) spec).catalogItemId(item.getId()); return spec; } // revert to legacy mechanism SpecT spec = null; try { if (loadedItem.getJavaType() != null) { SpecT specT = (SpecT) Reflections.findMethod(specType, "create", Class.class).invoke(null, loadedItem.loadJavaClass(mgmt)); spec = specT; } } catch (Exception e) { Exceptions.propagateIfFatal(e); throw new IllegalStateException("Unsupported creation of spec type " + specType + "; it must have a public static create(Class) method", e); } if (spec == null) throw new IllegalStateException("Unknown how to create instance of " + this); return spec; } @SuppressWarnings("unchecked") private <T, SpecT> SpecT createEntitySpec(String symbolicName, DeploymentPlan plan, BrooklynClassLoadingContext loader) { CampPlatform camp = BrooklynServerConfig.getCampPlatform(mgmt).get(); // TODO should not register new AT each time we instantiate from the same plan; use some kind of cache AssemblyTemplate at; BrooklynLoaderTracker.setLoader(loader); try { at = camp.pdp().registerDeploymentPlan(plan); } finally { BrooklynLoaderTracker.unsetLoader(loader); } try { AssemblyTemplateInstantiator instantiator = at.getInstantiator().newInstance(); if (instantiator instanceof AssemblyTemplateSpecInstantiator) { return (SpecT) ((AssemblyTemplateSpecInstantiator) instantiator).createNestedSpec(at, camp, loader, MutableSet.of(symbolicName)); } throw new IllegalStateException( "Unable to instantiate YAML; incompatible instantiator " + instantiator + " for " + at); } catch (Exception e) { throw Exceptions.propagate(e); } } @SuppressWarnings("unchecked") private <T, SpecT> SpecT createPolicySpec(DeploymentPlan plan, BrooklynClassLoadingContext loader) { //Would ideally re-use io.brooklyn.camp.brooklyn.spi.creation.BrooklynEntityDecorationResolver.PolicySpecResolver //but it is CAMP specific and there is no easy way to get hold of it. Object policies = checkNotNull(plan.getCustomAttributes().get(POLICIES_KEY), "policy config"); if (!(policies instanceof Iterable<?>)) { throw new IllegalStateException("The value of " + POLICIES_KEY + " must be an Iterable."); } Object policy = Iterables.getOnlyElement((Iterable<?>) policies); Map<String, Object> policyConfig; if (policy instanceof String) { policyConfig = ImmutableMap.<String, Object>of("type", policy); } else if (policy instanceof Map) { policyConfig = (Map<String, Object>) policy; } else { throw new IllegalStateException("Policy exepcted to be string or map. Unsupported object type " + policy.getClass().getName() + " (" + policy.toString() + ")"); } String policyType = (String) checkNotNull( Yamls.getMultinameAttribute(policyConfig, "policy_type", "policyType", "type"), "policy type"); Map<String, Object> brooklynConfig = (Map<String, Object>) policyConfig.get("brooklyn.config"); PolicySpec<? extends Policy> spec = PolicySpec.create(loader.loadClass(policyType, Policy.class)); if (brooklynConfig != null) { spec.configure(brooklynConfig); } return (SpecT) spec; } @SuppressWarnings("unchecked") @Override /** @deprecated since 0.7.0 use {@link #createSpec(CatalogItem)} */ @Deprecated public <T, SpecT> Class<? extends T> loadClass(CatalogItem<T, SpecT> item) { if (log.isDebugEnabled()) log.debug("Loading class for catalog item " + item); checkNotNull(item); CatalogItemDo<?, ?> loadedItem = getCatalogItemDo(item.getSymbolicName(), item.getVersion()); if (loadedItem == null) throw new NoSuchElementException("Unable to load '" + item.getId() + "' to instantiate it"); return (Class<? extends T>) loadedItem.getJavaClass(); } @SuppressWarnings("unchecked") @Override /** @deprecated since 0.7.0 use {@link #createSpec(CatalogItem)} */ @Deprecated public <T> Class<? extends T> loadClassByType(String typeName, Class<T> typeClass) { final CatalogItem<?, ?> resultI = getCatalogItemForType(typeName); if (resultI == null) { throw new NoSuchElementException("Unable to find catalog item for type " + typeName); } return (Class<? extends T>) loadClass(resultI); } @Deprecated /** @deprecated since 0.7.0 only used by other deprecated items */ private <T, SpecT> CatalogItemDtoAbstract<T, SpecT> getAbstractCatalogItem(CatalogItem<T, SpecT> item) { while (item instanceof CatalogItemDo) item = ((CatalogItemDo<T, SpecT>) item).itemDto; if (item == null) return null; if (item instanceof CatalogItemDtoAbstract) return (CatalogItemDtoAbstract<T, SpecT>) item; throw new IllegalStateException( "Cannot unwrap catalog item '" + item + "' (type " + item.getClass() + ") to restore DTO"); } private CatalogItemDtoAbstract<?, ?> getAbstractCatalogItem(String yaml) { DeploymentPlan plan = makePlanFromYaml(yaml); @SuppressWarnings("rawtypes") Maybe<Map> possibleCatalog = plan.getCustomAttribute("brooklyn.catalog", Map.class, true); MutableMap<String, Object> catalog = MutableMap.of(); if (possibleCatalog.isPresent()) { @SuppressWarnings("unchecked") Map<String, Object> catalog2 = (Map<String, Object>) possibleCatalog.get(); catalog.putAll(catalog2); } Collection<CatalogBundle> libraries = Collections.emptyList(); Maybe<Object> possibleLibraries = catalog.getMaybe("libraries"); if (possibleLibraries.isAbsent()) possibleLibraries = catalog.getMaybe("brooklyn.libraries"); if (possibleLibraries.isPresentAndNonNull()) { if (!(possibleLibraries.get() instanceof Collection)) throw new IllegalArgumentException("Libraries should be a list, not " + possibleLibraries.get()); libraries = CatalogItemDtoAbstract.parseLibraries((Collection<?>) possibleLibraries.get()); } final String id = (String) catalog.getMaybe("id").orNull(); final String version = Strings.toString(catalog.getMaybe("version").orNull()); final String symbolicName = (String) catalog.getMaybe("symbolicName").orNull(); final String name = (String) catalog.getMaybe("name").orNull(); final String displayName = (String) catalog.getMaybe("displayName").orNull(); final String description = (String) catalog.getMaybe("description").orNull(); final String iconUrl = (String) catalog.getMaybe("iconUrl").orNull(); final String iconUrlUnderscore = (String) catalog.getMaybe("icon_url").orNull(); if ((Strings.isNonBlank(id) || Strings.isNonBlank(symbolicName)) && Strings.isNonBlank(displayName) && Strings.isNonBlank(name) && !name.equals(displayName)) { log.warn( "Name property will be ignored due to the existence of displayName and at least one of id, symbolicName"); } final String catalogSymbolicName; if (Strings.isNonBlank(symbolicName)) { catalogSymbolicName = symbolicName; } else if (Strings.isNonBlank(id)) { if (Strings.isNonBlank(id) && CatalogUtils.looksLikeVersionedId(id)) { catalogSymbolicName = CatalogUtils.getIdFromVersionedId(id); } else { catalogSymbolicName = id; } } else if (Strings.isNonBlank(name)) { catalogSymbolicName = name; } else if (Strings.isNonBlank(plan.getName())) { catalogSymbolicName = plan.getName(); } else if (plan.getServices().size() == 1) { Service svc = Iterables.getOnlyElement(plan.getServices()); if (Strings.isBlank(svc.getServiceType())) { throw new IllegalStateException("CAMP service type not expected to be missing for " + svc); } catalogSymbolicName = svc.getServiceType(); } else { log.error("Can't infer catalog item symbolicName from the following plan:\n" + yaml); throw new IllegalStateException("Can't infer catalog item symbolicName from catalog item description"); } final String catalogVersion; if (CatalogUtils.looksLikeVersionedId(id)) { catalogVersion = CatalogUtils.getVersionFromVersionedId(id); if (version != null && !catalogVersion.equals(version)) { throw new IllegalArgumentException("Discrepency between version set in id " + catalogVersion + " and version property " + version); } } else if (Strings.isNonBlank(version)) { catalogVersion = version; } else { log.warn("No version specified for catalog item " + catalogSymbolicName + ". Using default value."); catalogVersion = null; } final String catalogDisplayName; if (Strings.isNonBlank(displayName)) { catalogDisplayName = displayName; } else if (Strings.isNonBlank(name)) { catalogDisplayName = name; } else if (Strings.isNonBlank(plan.getName())) { catalogDisplayName = plan.getName(); } else { catalogDisplayName = null; } final String catalogDescription; if (Strings.isNonBlank(description)) { catalogDescription = description; } else if (Strings.isNonBlank(plan.getDescription())) { catalogDescription = plan.getDescription(); } else { catalogDescription = null; } final String catalogIconUrl; if (Strings.isNonBlank(iconUrl)) { catalogIconUrl = iconUrl; } else if (Strings.isNonBlank(iconUrlUnderscore)) { catalogIconUrl = iconUrlUnderscore; } else { catalogIconUrl = null; } CatalogUtils.installLibraries(mgmt, libraries); String versionedId = CatalogUtils.getVersionedId(catalogSymbolicName, catalogVersion); BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, versionedId, libraries); AbstractBrooklynObjectSpec<?, ?> spec = createSpec(catalogSymbolicName, plan, loader); CatalogItemDtoAbstract<?, ?> dto = createItemBuilder(spec, catalogSymbolicName, catalogVersion) .libraries(libraries).displayName(catalogDisplayName).description(catalogDescription) .iconUrl(catalogIconUrl).plan(yaml).build(); dto.setManagementContext((ManagementContextInternal) mgmt); return dto; } private AbstractBrooklynObjectSpec<?, ?> createSpec(String symbolicName, DeploymentPlan plan, BrooklynClassLoadingContext loader) { if (isPolicyPlan(plan)) { return createPolicySpec(plan, loader); } else { return createEntitySpec(symbolicName, plan, loader); } } private CatalogItemBuilder<?> createItemBuilder(AbstractBrooklynObjectSpec<?, ?> spec, String itemId, String version) { if (spec instanceof EntitySpec) { if (isApplicationSpec((EntitySpec<?>) spec)) { return CatalogItemBuilder.newTemplate(itemId, version); } else { return CatalogItemBuilder.newEntity(itemId, version); } } else if (spec instanceof PolicySpec) { return CatalogItemBuilder.newPolicy(itemId, version); } else { throw new IllegalStateException("Unknown spec type " + spec.getClass().getName() + " (" + spec + ")"); } } private boolean isApplicationSpec(EntitySpec<?> spec) { return !Boolean.TRUE.equals(spec.getConfig().get(EntityManagementUtils.WRAPPER_APP_MARKER)); } private boolean isPolicyPlan(DeploymentPlan plan) { return plan.getCustomAttributes().containsKey(POLICIES_KEY); } private DeploymentPlan makePlanFromYaml(String yaml) { CampPlatform camp = BrooklynServerConfig.getCampPlatform(mgmt).get(); return camp.pdp().parseDeploymentPlan(Streams.newReaderWithContents(yaml)); } @Override public CatalogItem<?, ?> addItem(String yaml) { return addItem(yaml, false); } @Override public CatalogItem<?, ?> addItem(String yaml, boolean forceUpdate) { log.debug("Adding manual catalog item to " + mgmt + ": " + yaml); checkNotNull(yaml, "yaml"); CatalogItemDtoAbstract<?, ?> itemDto = getAbstractCatalogItem(yaml); checkItemNotExists(itemDto, forceUpdate); if (manualAdditionsCatalog == null) loadManualAdditionsCatalog(); manualAdditionsCatalog.addEntry(itemDto); // Ensure the cache is populated and it is persisted by the management context getCatalog().addEntry(itemDto); // Request that the management context persist the item. if (log.isTraceEnabled()) { log.trace("Scheduling item for persistence addition: {}", itemDto.getId()); } mgmt.getRebindManager().getChangeListener().onManaged(itemDto); return itemDto; } private void checkItemNotExists(CatalogItem<?, ?> itemDto, boolean forceUpdate) { if (!forceUpdate && getCatalogItemDo(itemDto.getSymbolicName(), itemDto.getVersion()) != null) { throw new IllegalStateException( "Updating existing catalog entries is forbidden: " + itemDto.getSymbolicName() + ":" + itemDto.getVersion() + ". Use forceUpdate argument to override."); } } @Override @Deprecated /** @deprecated see super */ public void addItem(CatalogItem<?, ?> item) { //assume forceUpdate for backwards compatibility log.debug("Adding manual catalog item to " + mgmt + ": " + item); checkNotNull(item, "item"); CatalogUtils.installLibraries(mgmt, item.getLibraries()); if (manualAdditionsCatalog == null) loadManualAdditionsCatalog(); manualAdditionsCatalog.addEntry(getAbstractCatalogItem(item)); } @Override @Deprecated /** @deprecated see super */ public CatalogItem<?, ?> addItem(Class<?> type) { //assume forceUpdate for backwards compatibility log.debug("Adding manual catalog item to " + mgmt + ": " + type); checkNotNull(type, "type"); if (manualAdditionsCatalog == null) loadManualAdditionsCatalog(); manualAdditionsClasses.addClass(type); return manualAdditionsCatalog.classpath.addCatalogEntry(type); } private synchronized void loadManualAdditionsCatalog() { if (manualAdditionsCatalog != null) return; CatalogDto manualAdditionsCatalogDto = CatalogDto.newNamedInstance("Manual Catalog Additions", "User-additions to the catalog while Brooklyn is running, " + "created " + Time.makeDateString(), "manual-additions"); CatalogDo manualAdditionsCatalog = catalog.addCatalog(manualAdditionsCatalogDto); if (manualAdditionsCatalog == null) { // not hard to support, but slightly messy -- probably have to use ID's to retrieve the loaded instance // for now block once, then retry log.warn("Blocking until catalog is loaded before changing it"); boolean loaded = blockIfNotLoaded(Duration.TEN_SECONDS); if (!loaded) log.warn("Catalog still not loaded after delay; subsequent operations may fail"); manualAdditionsCatalog = catalog.addCatalog(manualAdditionsCatalogDto); if (manualAdditionsCatalog == null) { throw new UnsupportedOperationException( "Catalogs cannot be added until the base catalog is loaded, and catalog is taking a while to load!"); } } log.debug("Creating manual additions catalog for " + mgmt + ": " + manualAdditionsCatalog); manualAdditionsClasses = new LoadedClassLoader(); ((AggregateClassLoader) manualAdditionsCatalog.classpath.getLocalClassLoader()) .addFirst(manualAdditionsClasses); // expose when we're all done this.manualAdditionsCatalog = manualAdditionsCatalog; } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public <T, SpecT> Iterable<CatalogItem<T, SpecT>> getCatalogItems() { if (!getCatalog().isLoaded()) { // some callers use this to force the catalog to load (maybe when starting as hot_backup without a catalog ?) log.debug("Forcing catalog load on access of catalog items"); load(); } return ImmutableList.copyOf((Iterable) catalog.getIdCache().values()); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public <T, SpecT> Iterable<CatalogItem<T, SpecT>> getCatalogItems( Predicate<? super CatalogItem<T, SpecT>> filter) { Iterable<CatalogItemDo<T, SpecT>> filtered = Iterables.filter((Iterable) catalog.getIdCache().values(), (Predicate<CatalogItem<T, SpecT>>) (Predicate) filter); return Iterables.transform(filtered, BasicBrooklynCatalog.<T, SpecT>itemDoToDto()); } private static <T, SpecT> Function<CatalogItemDo<T, SpecT>, CatalogItem<T, SpecT>> itemDoToDto() { return new Function<CatalogItemDo<T, SpecT>, CatalogItem<T, SpecT>>() { @Override public CatalogItem<T, SpecT> apply(@Nullable CatalogItemDo<T, SpecT> item) { return item.getDto(); } }; } transient CatalogXmlSerializer serializer; public String toXmlString() { if (serializer == null) loadSerializer(); return serializer.toString(catalog.dto); } private synchronized void loadSerializer() { if (serializer == null) serializer = new CatalogXmlSerializer(); } public void resetCatalogToContentsAtConfiguredUrl() { CatalogDto dto = null; String catalogUrl = mgmt.getConfig().getConfig(BrooklynServerConfig.BROOKLYN_CATALOG_URL); try { if (!Strings.isEmpty(catalogUrl)) { dto = CatalogDto.newDtoFromUrl(catalogUrl); if (log.isDebugEnabled()) { log.debug("Loading catalog from {}: {}", catalogUrl, catalog); } } } catch (Exception e) { if (Throwables.getRootCause(e) instanceof FileNotFoundException) { Maybe<Object> nonDefaultUrl = mgmt.getConfig() .getConfigRaw(BrooklynServerConfig.BROOKLYN_CATALOG_URL, true); if (nonDefaultUrl.isPresentAndNonNull() && !"".equals(nonDefaultUrl.get())) { log.warn( "Could not find catalog XML specified at {}; using default (local classpath) catalog. Error was: {}", nonDefaultUrl, e); } else { if (log.isDebugEnabled()) { log.debug( "No default catalog file available at {}; trying again using local classpath to populate catalog. Error was: {}", catalogUrl, e); } } } else { log.warn("Error importing catalog XML at " + catalogUrl + "; using default (local classpath) catalog. Error was: " + e, e); } } if (dto == null) { // retry, either an error, or was blank dto = CatalogDto.newDefaultLocalScanningDto(CatalogClasspathDo.CatalogScanningModes.ANNOTATIONS); if (log.isDebugEnabled()) { log.debug("Loaded default (local classpath) catalog: " + catalog); } } reset(dto); } @Deprecated public CatalogItem<?, ?> getCatalogItemForType(String typeName) { final CatalogItem<?, ?> resultI; final BrooklynCatalog catalog = mgmt.getCatalog(); if (CatalogUtils.looksLikeVersionedId(typeName)) { //All catalog identifiers of the form xxxx:yyyy are composed of symbolicName+version. //No javaType is allowed as part of the identifier. resultI = CatalogUtils.getCatalogItemOptionalVersion(mgmt, typeName); } else { //Usually for catalog items with javaType (that is items from catalog.xml) //the symbolicName and javaType match because symbolicName (was ID) //is not specified explicitly. But could be the case that there is an item //whose symbolicName is explicitly set to be different from the javaType. //Note that in the XML the attribute is called registeredTypeName. Iterable<CatalogItem<Object, Object>> resultL = catalog .getCatalogItems(CatalogPredicates.javaType(Predicates.equalTo(typeName))); if (!Iterables.isEmpty(resultL)) { //Push newer versions in front of the list (not that there should //be more than one considering the items are coming from catalog.xml). resultI = sortVersionsDesc(resultL).iterator().next(); if (log.isDebugEnabled() && Iterables.size(resultL) > 1) { log.debug("Found " + Iterables.size(resultL) + " matches in catalog for type " + typeName + "; returning the result with preferred version, " + resultI); } } else { //As a last resort try searching for items with the same symbolicName supposedly //different from the javaType. resultI = catalog.getCatalogItem(typeName, BrooklynCatalog.DEFAULT_VERSION); if (resultI != null) { if (resultI.getJavaType() == null) { throw new NoSuchElementException("Unable to find catalog item for type " + typeName + ". There is an existing catalog item with ID " + resultI.getId() + " but it doesn't define a class type."); } } } } return resultI; } }