catalog.bom parsing can result in RegisteredType instances instead of CatalogItem
added to non-persisting BasicBrooklynTypeRegistry, currently without validation; with quite a few tests, and previous failing cobundle-reference tests now uncommented. deletion support added as beta to TypeRegistry, works alongside Catalog deletion. not done, lots of tests failing still, but lots working, and shape of code pretty good. (even if the two-path code in BasicBrooklynCatalog is ugly, it's private and we'll be able to simplify a lot if/when we cut the old catalog code) need to next: perform some validation on the added registered types, as a secondary phase so we can add all types first, and populate supertypes then, which should allow all tests to pass, Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/9885cff8 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/9885cff8 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/9885cff8 Branch: refs/heads/master Commit: 9885cff87b47cefe78c26755cf7ed8e82a1dbd4d Parents: 4318237 Author: Alex Heneveld <[email protected]> Authored: Thu Jun 29 12:31:03 2017 +0100 Committer: Alex Heneveld <[email protected]> Committed: Thu Jun 29 12:31:03 2017 +0100 ---------------------------------------------------------------------- .../brooklyn/api/catalog/BrooklynCatalog.java | 24 ++ .../camp/brooklyn/AbstractYamlTest.java | 10 +- .../camp/brooklyn/ReferencedYamlTest.java | 6 +- .../CatalogYamlEntityOsgiTypeRegistryTest.java | 88 +++++++ .../brooklyn/catalog/CatalogYamlEntityTest.java | 4 +- .../catalog/internal/BasicBrooklynCatalog.java | 249 +++++++++++++------ .../catalog/internal/CatalogBundleLoader.java | 2 + .../brooklyn/core/mgmt/ha/OsgiManager.java | 9 +- .../core/typereg/BasicBrooklynTypeRegistry.java | 22 +- .../core/typereg/RegisteredTypePredicates.java | 19 ++ .../brooklyn/core/typereg/RegisteredTypes.java | 27 +- .../brooklyn/util/core/osgi/BundleMaker.java | 6 +- 12 files changed, 372 insertions(+), 94 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9885cff8/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java b/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java index 5bb9e1e..d2cd6d5 100644 --- a/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java +++ b/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java @@ -19,13 +19,17 @@ package org.apache.brooklyn.api.catalog; import java.util.Collection; +import java.util.Map; import java.util.NoSuchElementException; +import java.util.Set; import javax.annotation.Nullable; import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec; import org.apache.brooklyn.api.typereg.ManagedBundle; +import org.apache.brooklyn.api.typereg.RegisteredType; +import com.google.common.annotations.Beta; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Predicate; @@ -92,6 +96,26 @@ public interface BrooklynCatalog { */ AbstractBrooklynObjectSpec<?, ?> peekSpec(CatalogItem<?, ?> item); + /** Adds the given registered types defined in a bundle's catalog BOM; + * no validation performed, so caller should do that subsequently after + * loading all potential inter-dependent types. + * <p> + * Nothing is returned but caller can retrieve the results by searching the + * type registry for types with the same containing bundle. + */ + @Beta // method may move elsewhere + public void addTypesFromBundleBom(String yaml, ManagedBundle bundle, boolean forceUpdate); + + /** Performs YAML validation on the given set of types, returning a map whose keys are + * those types where validation failed, mapped to a collection of errors. + * An empty map result indicates no validation errors in the types passed in. + * <p> + * Validation may be side-effecting in that it sets metadata and refines supertypes + * for the given registered type. + */ + @Beta + public Map<RegisteredType,Set<Exception>> validateTypes(Iterable<RegisteredType> typesToValidate); + /** * Adds an item (represented in yaml) to the catalog. * Fails if the same version exists in catalog. http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9885cff8/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java index c69f481..00b9d4e 100644 --- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java @@ -97,7 +97,7 @@ public abstract class AbstractYamlTest { } return builder.build(); } - + /** Override to enable OSGi in the management context for all tests in the class. */ protected boolean disableOsgi() { return true; @@ -254,7 +254,15 @@ public abstract class AbstractYamlTest { return Iterables.size(mgmt().getTypeRegistry().getMatching(filter)); } + /** forcibly update items when adding to catalog (default is not to do this) */ public void forceCatalogUpdate() { forceUpdate = true; } + + /** whether when adding to catalog to forcibly update */ + public final boolean isForceUpdate() { + return forceUpdate; + } + + } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9885cff8/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ReferencedYamlTest.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ReferencedYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ReferencedYamlTest.java index 3480368..e22a935 100644 --- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ReferencedYamlTest.java +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ReferencedYamlTest.java @@ -169,7 +169,7 @@ public class ReferencedYamlTest extends AbstractYamlTest { checkChildEntitySpec(app, entityName); } - @Test(groups="WIP") // references to earlier items only work with short form syntax + @Test // long form discouraged but references should now still work public void testYamlReferencingEarlierItemLongFormEntity() throws Exception { addCatalogItems( "brooklyn.catalog:", @@ -222,7 +222,7 @@ public class ReferencedYamlTest extends AbstractYamlTest { checkChildEntitySpec(app, entityName); } - @Test(groups="WIP") //Not able to use caller provided catalog items when referencing entity specs (as opposed to catalog meta) + @Test // references to co-bundled items work even in nested url yaml public void testYamlReferencingEarlierItemInUrl() throws Exception { addCatalogItems( "brooklyn.catalog:", @@ -245,7 +245,7 @@ public class ReferencedYamlTest extends AbstractYamlTest { checkChildEntitySpec(app, entityName); } - @Test(groups="WIP") //Not able to use caller provided catalog items when referencing entity specs (as opposed to catalog meta) + @Test // reference to co-bundled items work also in nested url yaml as a type public void testYamlReferencingEarlierItemInUrlAsType() throws Exception { addCatalogItems( "brooklyn.catalog:", http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9885cff8/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java new file mode 100644 index 0000000..82a6095 --- /dev/null +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityOsgiTypeRegistryTest.java @@ -0,0 +1,88 @@ +/* + * 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.brooklyn.camp.brooklyn.catalog; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.util.zip.ZipEntry; + +import org.apache.brooklyn.api.typereg.RegisteredType; +import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog; +import org.apache.brooklyn.core.mgmt.ha.OsgiBundleInstallationResult; +import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; +import org.apache.brooklyn.core.typereg.BasicManagedBundle; +import org.apache.brooklyn.core.typereg.RegisteredTypePredicates; +import org.apache.brooklyn.entity.stock.BasicEntity; +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.core.osgi.BundleMaker; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.exceptions.ReferenceWithError; +import org.apache.brooklyn.util.osgi.VersionedName; +import org.testng.annotations.Test; + +import com.google.common.collect.Iterables; + +/** Variant of parent tests using OSGi, bundles, and type registry, instead of lightweight non-osgi catalog */ +@Test +public class CatalogYamlEntityOsgiTypeRegistryTest extends CatalogYamlEntityTest { + + // use OSGi here + @Override protected boolean disableOsgi() { return false; } + + // use type registry appraoch + @Override + protected void addCatalogItems(String catalogYaml) { + try { + BundleMaker bundleMaker = new BundleMaker(mgmt()); + File bf = bundleMaker.createTempZip("test", MutableMap.of( + new ZipEntry(BasicBrooklynCatalog.CATALOG_BOM), new ByteArrayInputStream(catalogYaml.getBytes()))); + ReferenceWithError<OsgiBundleInstallationResult> b = ((ManagementContextInternal)mgmt()).getOsgiManager().get().installDeferredStart( + new BasicManagedBundle(bundleName(), bundleVersion(), null), + new FileInputStream(bf)); + // bundle not started (no need), and BOM not installed above; do it explicitly below + // testing the type registry approach instead + mgmt().getCatalog().addTypesFromBundleBom(catalogYaml, b.get().getMetadata(), isForceUpdate()); + } catch (Exception e) { + throw Exceptions.propagate(e); + } + } + + protected void deleteCatalogEntity(String catalogItem) { + mgmt().getCatalog().deleteCatalogItem(catalogItem, TEST_VERSION); + } + + protected String bundleName() { return "sample-bundle"; } + protected String bundleVersion() { return BasicBrooklynCatalog.NO_VERSION; } + + @Test // basic test that this approach to adding types works + public void testAddTypes() throws Exception { + String symbolicName = "my.catalog.app.id.load"; + addCatalogEntity(IdAndVersion.of(symbolicName, TEST_VERSION), BasicEntity.class.getName()); + + Iterable<RegisteredType> itemsInstalled = mgmt().getTypeRegistry().getMatching(RegisteredTypePredicates.containingBundle(new VersionedName(bundleName(), bundleVersion()))); + Asserts.assertSize(itemsInstalled, 1); + RegisteredType item = mgmt().getTypeRegistry().get(symbolicName, TEST_VERSION); + Asserts.assertEquals(item, Iterables.getOnlyElement(itemsInstalled), "Wrong item; installed: "+itemsInstalled); + } + + // many other tests from super now run, with the type registry approach instead of catalog item / catalog approach + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9885cff8/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java index 08e2432..bcd3dc2 100644 --- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java @@ -694,11 +694,11 @@ public class CatalogYamlEntityTest extends AbstractYamlTest { } } - private void addCatalogEntity(String symbolicName, String entityType) { + protected void addCatalogEntity(String symbolicName, String entityType) { addCatalogEntity(IdAndVersion.of(symbolicName, TEST_VERSION), entityType); } - private void addCatalogEntity(IdAndVersion idAndVersion, String serviceType) { + protected void addCatalogEntity(IdAndVersion idAndVersion, String serviceType) { addCatalogItems( "brooklyn.catalog:", " id: " + idAndVersion.id, http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9885cff8/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java index c027268..c311664 100644 --- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java +++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java @@ -48,6 +48,7 @@ import org.apache.brooklyn.api.location.Location; import org.apache.brooklyn.api.location.LocationSpec; import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.api.mgmt.classloading.BrooklynClassLoadingContext; +import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry.RegisteredTypeKind; import org.apache.brooklyn.api.typereg.ManagedBundle; import org.apache.brooklyn.api.typereg.OsgiBundleWithUrl; import org.apache.brooklyn.api.typereg.RegisteredType; @@ -58,16 +59,19 @@ import org.apache.brooklyn.core.mgmt.ha.OsgiBundleInstallationResult; import org.apache.brooklyn.core.mgmt.ha.OsgiManager; import org.apache.brooklyn.core.mgmt.internal.CampYamlParser; import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; +import org.apache.brooklyn.core.typereg.BasicBrooklynTypeRegistry; import org.apache.brooklyn.core.typereg.BasicManagedBundle; +import org.apache.brooklyn.core.typereg.BasicRegisteredType; +import org.apache.brooklyn.core.typereg.BasicTypeImplementationPlan; import org.apache.brooklyn.core.typereg.BrooklynTypePlanTransformer; import org.apache.brooklyn.core.typereg.RegisteredTypeNaming; +import org.apache.brooklyn.core.typereg.RegisteredTypes; import org.apache.brooklyn.util.collections.MutableList; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.collections.MutableSet; import org.apache.brooklyn.util.core.ResourceUtils; import org.apache.brooklyn.util.core.flags.TypeCoercions; import org.apache.brooklyn.util.core.osgi.BundleMaker; -import org.apache.brooklyn.util.core.osgi.Osgis; import org.apache.brooklyn.util.core.task.Tasks; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.exceptions.UserFacingException; @@ -89,6 +93,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.Yaml; +import com.google.common.annotations.Beta; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Optional; @@ -281,8 +286,22 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { return itemDo.getDto(); } + private static ThreadLocal<Boolean> deletingCatalogItem = new ThreadLocal<>(); @Override public void deleteCatalogItem(String symbolicName, String version) { + if (!Boolean.TRUE.equals(deletingCatalogItem.get())) { + // while we switch from catalog to type registry, make sure deletion covers both; + // thread local lets us call to other once then he calls us and we do other code path + deletingCatalogItem.set(true); + try { + ((BasicBrooklynTypeRegistry) mgmt.getTypeRegistry()).delete( + mgmt.getTypeRegistry().get(symbolicName, version) ); + return; + } finally { + deletingCatalogItem.remove(); + } + } + log.debug("Deleting manual catalog item from "+mgmt+": "+symbolicName + ":" + version); checkNotNull(symbolicName, "id"); checkNotNull(version, "version"); @@ -447,12 +466,6 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { return (Maybe) getFirstAs(map, Map.class, firstKey, otherKeys); } - private List<CatalogItemDtoAbstract<?,?>> collectCatalogItems(String yaml, ManagedBundle containingBundle) { - List<CatalogItemDtoAbstract<?, ?>> result = MutableList.of(); - collectCatalogItems(yaml, containingBundle, result, ImmutableMap.of()); - return result; - } - public static Map<?,?> getCatalogMetadata(String yaml) { Map<?,?> itemDef = Yamls.getAs(Yamls.parseAll(yaml), Map.class); return getFirstAsMap(itemDef, "brooklyn.catalog").orNull(); @@ -481,15 +494,17 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { return new VersionedName(bundle, version); } - private void collectCatalogItems(String yaml, ManagedBundle containingBundle, List<CatalogItemDtoAbstract<?, ?>> result, Map<?, ?> parentMeta) { + /** See comments on {@link #collectCatalogItemsFromItemMetadataBlock(String, ManagedBundle, Map, List, boolean, Map, int, boolean)}; + * this is a shell around that that parses the `brooklyn.catalog` header on the BOM YAML file */ + private void collectCatalogItemsFromCatalogBomRoot(String yaml, ManagedBundle containingBundle, List<CatalogItemDtoAbstract<?, ?>> result, boolean requireValidation, Map<?, ?> parentMeta, int depth, boolean force) { Map<?,?> itemDef = Yamls.getAs(Yamls.parseAll(yaml), Map.class); Map<?,?> catalogMetadata = getFirstAsMap(itemDef, "brooklyn.catalog").orNull(); if (catalogMetadata==null) log.warn("No `brooklyn.catalog` supplied in catalog request; using legacy mode for "+itemDef); catalogMetadata = MutableMap.copyOf(catalogMetadata); - collectCatalogItems(Yamls.getTextOfYamlAtPath(yaml, "brooklyn.catalog").getMatchedYamlTextOrWarn(), - containingBundle, catalogMetadata, result, parentMeta, 0); + collectCatalogItemsFromItemMetadataBlock(Yamls.getTextOfYamlAtPath(yaml, "brooklyn.catalog").getMatchedYamlTextOrWarn(), + containingBundle, catalogMetadata, result, requireValidation, parentMeta, 0, force); itemDef.remove("brooklyn.catalog"); catalogMetadata.remove("item"); @@ -504,12 +519,46 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { if (rootItemYaml.startsWith(match)) rootItemYaml = Strings.removeFromStart(rootItemYaml, match); else rootItemYaml = Strings.replaceAllNonRegex(rootItemYaml, "\n"+match, ""); } - collectCatalogItems("item:\n"+makeAsIndentedObject(rootItemYaml), containingBundle, rootItem, result, catalogMetadata, 1); + collectCatalogItemsFromItemMetadataBlock("item:\n"+makeAsIndentedObject(rootItemYaml), containingBundle, rootItem, result, requireValidation, catalogMetadata, 1, force); } } + /** + * Expects item metadata, containing an `item` containing the definition, + * and/or `items` containing a list of item metadata (recursing with depth). + * + * Supports two modes: + * + * * CatalogItems validated and returned, but not added to catalog here; + * caller does that, and CI instances are persisted and loaded directly after rebind + * + * * RegisteredTypes added to (unpersisted) type registry; + * caller than validates, optionally removes broken ones, + * given the ability to add multiple interdependent BOMs/bundles and then validate; + * bundles with BOMs are persisted instead of catalog items + * + * I (Alex) think the first one should be considered legacy, and removed once we do + * everything with bundles. (At that point we can kill nearly ALL the code in this package, + * needing just a subset of the parsing and validation routines in this class which can + * be tidied up a lot.) + * + * Parameters suggest other combinations besides the above, but they aren't guaranteed to work. + * This is a private method and expect to clean it up a lot as per above. + * + * @param sourceYaml - metadata source for reference + * @param containingBundle - bundle where this is being loaded, or null + * @param itemMetadata - map of this item metadata reap + * @param result - list where items should be added, or add to type registry if null + * @param requireValidation - whether to require items to be validated; if false items might not be valid, + * and/or their catalog item types might not be set correctly yet; caller should normally validate later + * (useful if we have to load a bunch of things before they can all be validated) + * @param parentMetadata - inherited metadata + * @param depth - depth this is running in + * @param force - whether to force the catalog addition (only applies if result is null) + */ @SuppressWarnings("unchecked") - private void collectCatalogItems(String sourceYaml, ManagedBundle containingBundle, Map<?,?> itemMetadata, List<CatalogItemDtoAbstract<?, ?>> result, Map<?,?> parentMetadata, int depth) { + private void collectCatalogItemsFromItemMetadataBlock(String sourceYaml, ManagedBundle containingBundle, Map<?,?> itemMetadata, List<CatalogItemDtoAbstract<?, ?>> result, boolean requireValidation, + Map<?,?> parentMetadata, int depth, boolean force) { if (sourceYaml==null) sourceYaml = new Yaml().dump(itemMetadata); @@ -613,18 +662,18 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { int count = 0; for (Object ii: checkType(items, "items", List.class)) { if (ii instanceof String) { - collectUrlReferencedCatalogItems((String) ii, containingBundle, result, catalogMetadata); + collectUrlReferencedCatalogItems((String) ii, containingBundle, result, requireValidation, catalogMetadata, depth+1, force); } else { Map<?,?> i = checkType(ii, "entry in items list", Map.class); - collectCatalogItems(Yamls.getTextOfYamlAtPath(sourceYaml, "items", count).getMatchedYamlTextOrWarn(), - containingBundle, i, result, catalogMetadata, depth+1); + collectCatalogItemsFromItemMetadataBlock(Yamls.getTextOfYamlAtPath(sourceYaml, "items", count).getMatchedYamlTextOrWarn(), + containingBundle, i, result, requireValidation, catalogMetadata, depth+1, force); } count++; } } if (url != null) { - collectUrlReferencedCatalogItems(checkType(url, "include in catalog meta", String.class), containingBundle, result, catalogMetadata); + collectUrlReferencedCatalogItems(checkType(url, "include in catalog meta", String.class), containingBundle, result, requireValidation, catalogMetadata, depth+1, force); } if (item==null) return; @@ -649,8 +698,10 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { } PlanInterpreterGuessingType planInterpreter = new PlanInterpreterGuessingType(null, item, sourceYaml, itemType, libraryBundles, result).reconstruct(); + Exception resolutionError = null; if (!planInterpreter.isResolved()) { - throw Exceptions.create("Could not resolve definition of item" + // don't throw yet, we may be able to add it in an unresolved state + resolutionError = Exceptions.create("Could not resolve definition of item" + (Strings.isNonBlank(id) ? " '"+id+"'" : Strings.isNonBlank(symbolicName) ? " '"+symbolicName+"'" : Strings.isNonBlank(name) ? " '"+name+"'" : "") // better not to show yaml, takes up lots of space, and with multiple plan transformers there might be multiple errors; // some of the errors themselves may reproduce it @@ -658,7 +709,9 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { // + ":\n"+sourceYaml , planInterpreter.getErrors()); } + // now allowed to be null here itemType = planInterpreter.getCatalogItemType(); + Map<?, ?> itemAsMap = planInterpreter.getItem(); // the "plan yaml" includes the services: ... or brooklyn.policies: ... outer key, // as opposed to the rawer { type: foo } map without that outer key which is valid as item input @@ -785,24 +838,59 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { final String deprecated = getFirstAs(catalogMetadata, String.class, "deprecated").orNull(); final Boolean catalogDeprecated = Boolean.valueOf(deprecated); - // run again now that we know the ID + // run again now that we know the ID to catch recursive definitions and possibly other mistakes (itemType inconsistency?) planInterpreter = new PlanInterpreterGuessingType(id, item, sourceYaml, itemType, libraryBundles, result).reconstruct(); - if (!planInterpreter.isResolved()) { - throw new IllegalStateException("Could not resolve plan once id and itemType are known (recursive reference?): "+sourceYaml); + if (resolutionError==null && !planInterpreter.isResolved()) { + resolutionError = new IllegalStateException("Plan resolution breaks after id and itemType are set; is there a recursive reference or other type inconsistency?\n"+sourceYaml); } String sourcePlanYaml = planInterpreter.getPlanYaml(); - CatalogItemDtoAbstract<?, ?> dto = createItemBuilder(itemType, symbolicName, version) - .libraries(libraryBundles) - .displayName(displayName) - .description(description) - .deprecated(catalogDeprecated) - .iconUrl(catalogIconUrl) - .plan(sourcePlanYaml) - .build(); - - dto.setManagementContext((ManagementContextInternal) mgmt); - result.add(dto); + if (result==null) { + // horrible API but basically if `result` is null then add to local unpersisted registry instead, + // without forcing resolution and ignoring errors; this lets us deal with forward references, but + // we'll have to do a validation step subsequently. (already we let bundles deal with persistence, + // just need TODO to make sure we delete previously-persisted things which now come through this path.) + // NB: when everything is a bundle and we've removed all scanning then this can be the _only_ path + // and code can be massively simpler + + if (resolutionError!=null) { + if (requireValidation) { + throw Exceptions.propagate(resolutionError); + } + // warn? add as "unresolved" ? just do nothing? + } + String format = null; // could support specifying format + // TODO bean? or spec?? + // TODO learn supertypes, or find later + Class<?> javaType = null; + List<Class<?>> superTypes = MutableList.<Class<?>>of().appendIfNotNull(javaType); + BasicRegisteredType type = (BasicRegisteredType) RegisteredTypes.newInstance( + RegisteredTypeKind.SPEC, + symbolicName, version, new BasicTypeImplementationPlan(format, sourcePlanYaml), + superTypes, containingBundle, libraryBundles, + displayName, description, catalogIconUrl, catalogDeprecated); + // TODO tags ? + + ((BasicBrooklynTypeRegistry) mgmt.getTypeRegistry()).addToLocalUnpersistedTypeRegistry(type, force); + + } else { + if (resolutionError!=null) { + // if there was an error, throw it here + throw Exceptions.propagate(resolutionError); + } + + CatalogItemDtoAbstract<?, ?> dto = createItemBuilder(itemType, symbolicName, version) + .libraries(libraryBundles) + .displayName(displayName) + .description(description) + .deprecated(catalogDeprecated) + .iconUrl(catalogIconUrl) + .plan(sourcePlanYaml) + .build(); + + dto.setManagementContext((ManagementContextInternal) mgmt); + result.add(dto); + } } protected static Collection<CatalogBundle> resolveWherePossible(ManagementContext mgmt, Collection<CatalogBundle> libraryBundles) { @@ -827,7 +915,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { return wrapped!=null && wrapped.equalsIgnoreCase("true"); } - private void collectUrlReferencedCatalogItems(String url, ManagedBundle containingBundle, List<CatalogItemDtoAbstract<?, ?>> result, Map<Object, Object> parentMeta) { + private void collectUrlReferencedCatalogItems(String url, ManagedBundle containingBundle, List<CatalogItemDtoAbstract<?, ?>> result, boolean requireValidation, Map<Object, Object> parentMeta, int depth, boolean force) { @SuppressWarnings("unchecked") List<?> parentLibrariesRaw = MutableList.copyOf(getFirstAs(parentMeta, List.class, "brooklyn.libraries", "libraries").orNull()); Collection<CatalogBundle> parentLibraries = CatalogItemDtoAbstract.parseLibraries(parentLibrariesRaw); @@ -839,7 +927,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { Exceptions.propagateIfFatal(e); throw new IllegalStateException("Remote catalog url " + url + " can't be fetched.", e); } - collectCatalogItems(yaml, containingBundle, result, parentMeta); + collectCatalogItemsFromCatalogBomRoot(yaml, containingBundle, result, requireValidation, parentMeta, depth, force); } @SuppressWarnings("unchecked") @@ -946,7 +1034,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { List<Exception> errors = MutableList.of(); List<Exception> entityErrors = MutableList.of(); - public PlanInterpreterGuessingType(@Nullable String id, Object item, String itemYaml, @Nullable CatalogItemType optionalCiType, + public PlanInterpreterGuessingType(@Nullable String id, Object item, String itemYaml, @Nullable CatalogItemType optionalCiType, Collection<CatalogBundle> libraryBundles, List<CatalogItemDtoAbstract<?,?>> itemsDefinedSoFar) { // ID is useful to prevent recursive references (possibly only supported for entities?) this.id = id; @@ -1016,52 +1104,54 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { else candidateYaml = key + ":\n" + makeAsIndentedList(itemYaml); } - // first look in collected items, if a key is given String type = (String) item.get("type"); - - if (type!=null && key!=null) { - for (CatalogItemDtoAbstract<?,?> candidate: itemsDefinedSoFar) { - if (candidateCiType == candidate.getCatalogItemType() && - (type.equals(candidate.getSymbolicName()) || type.equals(candidate.getId()))) { - // matched - exit - catalogItemType = candidateCiType; - planYaml = candidateYaml; - resolved = true; - return true; - } - } - } - { - // legacy routine; should be the same as above code added in 0.12 because: - // if type is symbolic_name, the type will match above, and version will be null so any version allowed to match - // if type is symbolic_name:version, the id will match, and the version will also have to match - // SHOULD NEVER NEED THIS - remove during or after 0.13 - String typeWithId = type; - String version = null; - if (CatalogUtils.looksLikeVersionedId(type)) { - version = CatalogUtils.getVersionFromVersionedId(type); - type = CatalogUtils.getSymbolicNameFromVersionedId(type); - } + if (itemsDefinedSoFar!=null) { + // first look in collected items, if a key is given + if (type!=null && key!=null) { for (CatalogItemDtoAbstract<?,?> candidate: itemsDefinedSoFar) { if (candidateCiType == candidate.getCatalogItemType() && (type.equals(candidate.getSymbolicName()) || type.equals(candidate.getId()))) { - if (version==null || version.equals(candidate.getVersion())) { - log.error("Lookup of '"+type+"' version '"+version+"' only worked using legacy routines; please advise Brooklyn community so they understand why"); - // matched - exit - catalogItemType = candidateCiType; - planYaml = candidateYaml; - resolved = true; - return true; + // matched - exit + catalogItemType = candidateCiType; + planYaml = candidateYaml; + resolved = true; + return true; + } + } + } + { + // legacy routine; should be the same as above code added in 0.12 because: + // if type is symbolic_name, the type will match above, and version will be null so any version allowed to match + // if type is symbolic_name:version, the id will match, and the version will also have to match + // SHOULD NEVER NEED THIS - remove during or after 0.13 + String typeWithId = type; + String version = null; + if (CatalogUtils.looksLikeVersionedId(type)) { + version = CatalogUtils.getVersionFromVersionedId(type); + type = CatalogUtils.getSymbolicNameFromVersionedId(type); + } + if (type!=null && key!=null) { + for (CatalogItemDtoAbstract<?,?> candidate: itemsDefinedSoFar) { + if (candidateCiType == candidate.getCatalogItemType() && + (type.equals(candidate.getSymbolicName()) || type.equals(candidate.getId()))) { + if (version==null || version.equals(candidate.getVersion())) { + log.error("Lookup of '"+type+"' version '"+version+"' only worked using legacy routines; please advise Brooklyn community so they understand why"); + // matched - exit + catalogItemType = candidateCiType; + planYaml = candidateYaml; + resolved = true; + return true; + } } } } + + type = typeWithId; + // above line is a change to behaviour; previously we proceeded below with the version dropped in code above; + // but that seems like a bug as the code below will have ignored version. + // likely this means we are now stricter about loading things that reference new versions, but correctly so. } - - type = typeWithId; - // above line is a change to behaviour; previously we proceeded below with the version dropped in code above; - // but that seems like a bug as the code below will have ignored version. - // likely this means we are now stricter about loading things that reference new versions, but correctly so. } // then try parsing plan - this will use loader @@ -1257,9 +1347,10 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { @Override public List<? extends CatalogItem<?,?>> addItems(String yaml, ManagedBundle bundle, boolean forceUpdate) { - log.debug("Adding manual catalog item to "+mgmt+": "+yaml); + log.debug("Adding catalog item to "+mgmt+": "+yaml); checkNotNull(yaml, "yaml"); - List<CatalogItemDtoAbstract<?, ?>> result = collectCatalogItems(yaml, bundle); + List<CatalogItemDtoAbstract<?, ?>> result = MutableList.of(); + collectCatalogItemsFromCatalogBomRoot(yaml, bundle, result, true, ImmutableMap.of(), 0, false); // do this at the end for atomic updates; if there are intra-yaml references, we handle them specially for (CatalogItemDtoAbstract<?, ?> item: result) { @@ -1271,6 +1362,18 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { return result; } + @Override @Beta + public void addTypesFromBundleBom(String yaml, ManagedBundle bundle, boolean forceUpdate) { + log.debug("Adding catalog item to "+mgmt+": "+yaml); + checkNotNull(yaml, "yaml"); + collectCatalogItemsFromCatalogBomRoot(yaml, bundle, null, false, MutableMap.of(), 0, forceUpdate); + } + + @Override @Beta + public Map<RegisteredType,Set<Exception>> validateTypes(Iterable<RegisteredType> typesToValidate) { + return MutableMap.of(); + } + private CatalogItem<?,?> addItemDto(CatalogItemDtoAbstract<?, ?> itemDto, boolean forceUpdate) { CatalogItem<?, ?> existingDto = checkItemAllowedAndIfSoReturnAnyDuplicate(itemDto, true, forceUpdate); if (existingDto!=null) { http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9885cff8/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleLoader.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleLoader.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleLoader.java index 3af87b9..f42dac1 100644 --- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleLoader.java +++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleLoader.java @@ -77,6 +77,8 @@ public class CatalogBundleLoader { if (null != bom) { LOG.debug("Found catalog BOM in {} {} {}", CatalogUtils.bundleIds(bundle)); String bomText = readBom(bom); + // TODO use addTypesFromBundleBom; but when should we do validation? after all bundles are loaded? + // OR maybe deprecate/remove this experiment in favour of explicitly installed and managed bundles? catalogItems = this.managementContext.getCatalog().addItems(bomText, mb, force); for (CatalogItem<?, ?> item : catalogItems) { LOG.debug("Added to catalog: {}, {}", item.getSymbolicName(), item.getVersion()); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9885cff8/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java index a73c016..6fe1068 100644 --- a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java +++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java @@ -46,6 +46,7 @@ import org.apache.brooklyn.core.catalog.internal.CatalogBundleLoader; import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.server.BrooklynServerConfig; import org.apache.brooklyn.core.server.BrooklynServerPaths; +import org.apache.brooklyn.core.typereg.RegisteredTypePredicates; import org.apache.brooklyn.util.collections.MutableList; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.collections.MutableSet; @@ -345,13 +346,7 @@ public class OsgiManager { @Beta public Iterable<RegisteredType> getTypesFromBundle(final VersionedName vn) { - final String bundleId = vn.toString(); - return mgmt.getTypeRegistry().getMatching(new Predicate<RegisteredType>() { - @Override - public boolean apply(RegisteredType input) { - return bundleId.equals(input.getContainingBundle()); - } - }); + return mgmt.getTypeRegistry().getMatching(RegisteredTypePredicates.containingBundle(vn)); } /** @deprecated since 0.12.0 use {@link #install(ManagedBundle, InputStream, boolean, boolean)} */ http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9885cff8/core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java index 468664b..bcb24ad 100644 --- a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistry.java @@ -32,7 +32,6 @@ import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry; import org.apache.brooklyn.api.typereg.RegisteredType; import org.apache.brooklyn.api.typereg.RegisteredType.TypeImplementationPlan; import org.apache.brooklyn.api.typereg.RegisteredTypeLoadingContext; -import org.apache.brooklyn.api.typereg.ManagedBundle; import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog; import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder; import org.apache.brooklyn.core.catalog.internal.CatalogUtils; @@ -41,6 +40,8 @@ import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.collections.MutableSet; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.guava.Maybe; +import org.apache.brooklyn.util.osgi.VersionedName; +import org.apache.brooklyn.util.text.BrooklynVersionSyntax; import org.apache.brooklyn.util.text.Identifiers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,7 +50,6 @@ import com.google.common.annotations.Beta; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry { @@ -57,7 +57,6 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry { private static final Logger log = LoggerFactory.getLogger(BasicBrooklynTypeRegistry.class); private ManagementContext mgmt; - private Map<String,ManagedBundle> uploadedBundles = MutableMap.of(); private Map<String,RegisteredType> localRegisteredTypes = MutableMap.of(); public BasicBrooklynTypeRegistry(ManagementContext mgmt) { @@ -326,5 +325,22 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry { throw new IllegalStateException("Cannot add "+type+" to catalog; different "+oldType+" is already present"); } } + + @Beta // API stabilising + public void delete(RegisteredType type) { + if (localRegisteredTypes.remove(type.getId()) != null) { + return ; + } + mgmt.getCatalog().deleteCatalogItem(type.getSymbolicName(), type.getVersion()); + } + + @Beta // API stabilising + public void delete(String id) { + if (localRegisteredTypes.remove(id) != null) { + return ; + } + VersionedName vn = VersionedName.fromString(id); + mgmt.getCatalog().deleteCatalogItem(vn.getSymbolicName(), vn.getVersionString()); + } } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9885cff8/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicates.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicates.java b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicates.java index e480cd1..1d67638 100644 --- a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicates.java +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicates.java @@ -30,6 +30,7 @@ import org.apache.brooklyn.api.typereg.RegisteredType; import org.apache.brooklyn.api.typereg.RegisteredTypeLoadingContext; import org.apache.brooklyn.core.mgmt.entitlement.Entitlements; import org.apache.brooklyn.util.collections.CollectionFunctionals; +import org.apache.brooklyn.util.osgi.VersionedName; import com.google.common.base.Function; import com.google.common.base.Predicate; @@ -256,4 +257,22 @@ public class RegisteredTypePredicates { } } + public static Predicate<? super RegisteredType> containingBundle(VersionedName versionedName) { + return new ContainingBundle(versionedName); + } + public static Predicate<? super RegisteredType> containingBundle(String versionedName) { + return new ContainingBundle(VersionedName.fromString(versionedName)); + } + private static class ContainingBundle implements Predicate<RegisteredType> { + private final VersionedName bundle; + + public ContainingBundle(VersionedName bundle) { + this.bundle = bundle; + } + @Override + public boolean apply(@Nullable RegisteredType item) { + return bundle.equalsOsgi(item.getContainingBundle()); + } + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9885cff8/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java index 72feedd..e3a8540 100644 --- a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java @@ -19,8 +19,10 @@ package org.apache.brooklyn.core.typereg; import java.lang.reflect.Method; +import java.util.Collection; import java.util.Comparator; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; @@ -28,6 +30,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.brooklyn.api.catalog.CatalogItem; +import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle; import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec; import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.api.objs.BrooklynObject; @@ -125,21 +128,35 @@ public class RegisteredTypes { } /** Convenience for {@link #bean(String, String, TypeImplementationPlan)} when there is a single known java signature/super type */ public static RegisteredType bean(@Nonnull String symbolicName, @Nonnull String version, @Nonnull TypeImplementationPlan plan, @Nonnull Class<?> superType) { - if (superType==null) log.warn("Deprecated use of RegisteredTypes API passing null name/version", new Exception("Location of deprecated use, wrt "+symbolicName+":"+version+" "+plan)); + if (superType==null) log.warn("Deprecated use of RegisteredTypes API passing null supertype", new Exception("Location of deprecated use, wrt "+symbolicName+":"+version+" "+plan)); return addSuperType(bean(symbolicName, version, plan), superType); } /** Preferred mechanism for defining a spec {@link RegisteredType}. * Callers should also {@link #addSuperTypes(RegisteredType, Iterable)} on the result.*/ public static RegisteredType spec(@Nonnull String symbolicName, @Nonnull String version, @Nonnull TypeImplementationPlan plan) { - if (symbolicName==null || version==null) log.warn("Deprecated use of RegisteredTypes API passing null name/version", new Exception("Location of deprecated use, wrt "+plan)); + if (symbolicName==null || version==null) log.warn("Deprecated use of RegisteredTypes API passing null supertype", new Exception("Location of deprecated use, wrt "+plan)); return new BasicRegisteredType(RegisteredTypeKind.SPEC, symbolicName, version, plan); } - /** Convenience for {@link #cpec(String, String, TypeImplementationPlan)} when there is a single known java signature/super type */ + /** Convenience for {@link #spec(String, String, TypeImplementationPlan)} when there is a single known java signature/super type */ public static RegisteredType spec(@Nonnull String symbolicName, @Nonnull String version, @Nonnull TypeImplementationPlan plan, @Nonnull Class<?> superType) { - if (superType==null) log.warn("Deprecated use of RegisteredTypes API passing null name/version", new Exception("Location of deprecated use, wrt "+symbolicName+":"+version+" "+plan)); + if (superType==null) log.warn("Deprecated use of RegisteredTypes API passing null supertype", new Exception("Location of deprecated use, wrt "+symbolicName+":"+version+" "+plan)); return addSuperType(spec(symbolicName, version, plan), superType); } + public static RegisteredType newInstance(@Nonnull RegisteredTypeKind kind, @Nonnull String symbolicName, @Nonnull String version, + @Nonnull TypeImplementationPlan plan, @Nonnull List<Class<?>> superTypes, + ManagedBundle containingBundle, Collection<CatalogBundle> libraryBundles, + String displayName, String description, String catalogIconUrl, boolean catalogDeprecated) { + BasicRegisteredType result = new BasicRegisteredType(kind, symbolicName, version, plan); + addSuperTypes(result, superTypes); + result.containingBundle = containingBundle.getVersionedName().toString(); + result.bundles.addAll(libraryBundles); + result.displayName = displayName; + result.description = description; + result.iconUrl = catalogIconUrl; + result.deprecated = catalogDeprecated; + return result; + } /** Creates an anonymous {@link RegisteredType} for plan-instantiation-only use. */ @Beta @@ -306,6 +323,7 @@ public class RegisteredTypes { * Queries recursively the given types (either {@link Class} or {@link RegisteredType}) * to see whether any inherit from the given {@link Class} */ public static boolean isAnyTypeSubtypeOf(Set<Object> candidateTypes, Class<?> superType) { + if (superType == Object.class) return true; return isAnyTypeOrSuperSatisfying(candidateTypes, Predicates.assignableFrom(superType)); } @@ -480,4 +498,5 @@ public class RegisteredTypes { if (item==null) return null; return item.getIconUrl(); } + } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9885cff8/core/src/main/java/org/apache/brooklyn/util/core/osgi/BundleMaker.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/util/core/osgi/BundleMaker.java b/core/src/main/java/org/apache/brooklyn/util/core/osgi/BundleMaker.java index ebcc220..37aa5a1 100644 --- a/core/src/main/java/org/apache/brooklyn/util/core/osgi/BundleMaker.java +++ b/core/src/main/java/org/apache/brooklyn/util/core/osgi/BundleMaker.java @@ -361,7 +361,7 @@ public class BundleMaker { ZipOutputStream zout = null; ZipFile zf = null; try { - zout = new JarOutputStream(new FileOutputStream(f2), mf); + zout = mf!=null ? new JarOutputStream(new FileOutputStream(f2), mf) : new ZipOutputStream(new FileOutputStream(f2)); writeZipEntries(zout, files); } catch (IOException e) { throw Exceptions.propagate("Unable to read/write for "+nameHint, e); @@ -376,4 +376,8 @@ public class BundleMaker { return createTempBundle(nameHint, manifestOf(mf), files); } + public File createTempZip(String nameHint, Map<ZipEntry, InputStream> files) { + return createTempBundle(nameHint, (Manifest)null, files); + } + }
