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);
+    }
+    
 }

Reply via email to