You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2015/12/21 13:10:56 UTC

[06/12] incubator-brooklyn git commit: type registry: adds validation, best version support, aliases, tags

type registry: adds validation, best version support, aliases, tags

and a simple registry with lookup tests


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/420ea303
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/420ea303
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/420ea303

Branch: refs/heads/master
Commit: 420ea303498f56b38b59ef221f45bf02ddcf19f3
Parents: 1265796
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Nov 26 10:04:02 2015 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu Dec 17 12:36:16 2015 +0000

----------------------------------------------------------------------
 .../api/typereg/BrooklynTypeRegistry.java       |  28 ++-
 .../brooklyn/api/typereg/RegisteredType.java    |   8 +-
 .../core/catalog/internal/CatalogUtils.java     |  10 +-
 .../typereg/AbstractTypePlanTransformer.java    |   2 +-
 .../core/typereg/BasicBrooklynTypeRegistry.java | 115 ++++++++++--
 .../core/typereg/BasicRegisteredType.java       |  34 +++-
 .../core/typereg/RegisteredTypePredicates.java  |  89 ++++++++-
 .../brooklyn/core/typereg/RegisteredTypes.java  | 137 ++++++++++----
 .../internal/CatalogItemComparatorTest.java     |  12 +-
 .../typereg/BasicBrooklynTypeRegistryTest.java  | 186 +++++++++++++++++++
 .../typereg/RegisteredTypePredicatesTest.java   |  19 +-
 .../BrooklynEntityDecorationResolver.java       |   2 +-
 .../brooklyn/test/lite/CampYamlLiteTest.java    |   2 +-
 .../rest/resources/CatalogResource.java         |   2 +-
 .../util/collections/CollectionFunctionals.java |  23 ++-
 .../collections/CollectionFunctionalsTest.java  |  24 +++
 16 files changed, 585 insertions(+), 108 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/api/src/main/java/org/apache/brooklyn/api/typereg/BrooklynTypeRegistry.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/typereg/BrooklynTypeRegistry.java b/api/src/main/java/org/apache/brooklyn/api/typereg/BrooklynTypeRegistry.java
index 78c49d8..eb59d27 100644
--- a/api/src/main/java/org/apache/brooklyn/api/typereg/BrooklynTypeRegistry.java
+++ b/api/src/main/java/org/apache/brooklyn/api/typereg/BrooklynTypeRegistry.java
@@ -23,7 +23,9 @@ import javax.annotation.Nullable;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
+import org.apache.brooklyn.util.guava.Maybe;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Predicate;
 
 
@@ -48,23 +50,29 @@ public interface BrooklynTypeRegistry {
      * taking the best version if the version is null or a default marker,
      * returning null if no matches are found. */
     RegisteredType get(String symbolicName, String version);
-    /** as {@link #get(String, String)} but allows <code>"name:version"</code> 
-     * (the {@link RegisteredType#getId()}) in addition to the unversioned name,
-     * using a default marker if no version can be inferred */
+    /** as {@link #get(String, String)} but the given string here 
+     * is allowed to match any of:
+     * <li>the given string as an ID including version (<code>"name:version"</code>) 
+     * <li>the symbolic name unversioned, or
+     * <li>an alias */
     RegisteredType get(String symbolicNameWithOptionalVersion);
-    
-    // TODO remove
-//    /** as {@link #get(String, String)}, but applying the optionally supplied {@link RegisteredTypeLoadingContext} */ 
-//    RegisteredType get(String symbolicName, String version, @Nullable RegisteredTypeLoadingContext context);
-//    /** as {@link #get(String)}, but applying the optionally supplied {@link RegisteredTypeLoadingContext} */ 
-//    RegisteredType get(String symbolicNameWithOptionalVersion, @Nullable RegisteredTypeLoadingContext context);
+
+    /** as {@link #get(String)} but further filtering for the additional context */
+    public RegisteredType get(String symbolicNameOrAliasWithOptionalVersion, RegisteredTypeLoadingContext context);
+    /** returns a wrapper of the result of {@link #get(String, RegisteredTypeLoadingContext)} 
+     * including a detailed message if absent */
+    public Maybe<RegisteredType> getMaybe(String symbolicNameOrAliasWithOptionalVersion, RegisteredTypeLoadingContext context);
 
     // NB the seemingly more correct generics <T,SpecT extends AbstractBrooklynObjectSpec<T,SpecT>> 
     // cause compile errors, not in Eclipse, but in maven (?) 
-    // TODO do these belong here, or in a separate master TypePlanTransformer ?  see also BrooklynTypePlanTransformer 
+    // TODO do these belong here, or in a separate master TypePlanTransformer ?  see also BrooklynTypePlanTransformer
+    @Beta
     <SpecT extends AbstractBrooklynObjectSpec<?,?>> SpecT createSpec(RegisteredType type, @Nullable RegisteredTypeLoadingContext optionalContext, @Nullable Class<SpecT> optionalSpecSuperType);
+    @Beta
     <SpecT extends AbstractBrooklynObjectSpec<?,?>> SpecT createSpecFromPlan(@Nullable String planFormat, Object planData, @Nullable RegisteredTypeLoadingContext optionalContext, @Nullable Class<SpecT> optionalSpecSuperType);
+    @Beta
     <T> T createBean(RegisteredType type, @Nullable RegisteredTypeLoadingContext optionalContext, @Nullable Class<T> optionalResultSuperType);
+    @Beta
     <T> T createBeanFromPlan(String planFormat, Object planData, @Nullable RegisteredTypeLoadingContext optionalConstraint, @Nullable Class<T> optionalBeanSuperType);
     
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredType.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredType.java b/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredType.java
index 2674736..29b64d3 100644
--- a/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredType.java
+++ b/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredType.java
@@ -21,8 +21,6 @@ package org.apache.brooklyn.api.typereg;
 import java.util.Collection;
 import java.util.Set;
 
-import javax.annotation.Nullable;
-
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.objs.BrooklynObject;
@@ -75,6 +73,12 @@ public interface RegisteredType extends Identifiable {
      */
     boolean isDisabled();
 
+    /** Alias words defined for this type */
+    Set<String> getAliases();
+
+    /** Tags attached to this item */
+    Set<Object> getTags();
+    
     /** @return implementation details, so that the framework can find a suitable {@link BrooklynTypePlanTransformer} 
      * which can then use this object to instantiate this type */
     TypeImplementationPlan getPlan();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java
index ef455c6..078e1d7 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java
@@ -43,6 +43,7 @@ import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.mgmt.rebind.RebindManagerImpl.RebindTracker;
 import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
 import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts;
+import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
 import org.apache.brooklyn.core.typereg.RegisteredTypes;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.text.Strings;
@@ -85,7 +86,7 @@ public class CatalogUtils {
         ManagementContext mgmt = ((EntityInternal)entity).getManagementContext();
         String catId = entity.getCatalogItemId();
         if (Strings.isBlank(catId)) return JavaBrooklynClassLoadingContext.create(mgmt);
-        RegisteredType cat = RegisteredTypes.validate(mgmt.getTypeRegistry().get(catId), RegisteredTypeLoadingContexts.spec(Entity.class));
+        RegisteredType cat = RegisteredTypes.validate(mgmt.getTypeRegistry().get(catId), RegisteredTypeLoadingContexts.spec(Entity.class)).get();
         if (cat==null) {
             log.warn("Cannot load "+catId+" to get classloader for "+entity+"; will try with standard loader, but might fail subsequently");
             return JavaBrooklynClassLoadingContext.create(mgmt);
@@ -267,9 +268,10 @@ public class CatalogUtils {
     }
 
     public static boolean isBestVersion(ManagementContext mgmt, CatalogItem<?,?> item) {
-        RegisteredType bestVersion = mgmt.getTypeRegistry().get(item.getSymbolicName(), BrooklynCatalog.DEFAULT_VERSION);
-        if (bestVersion==null) return false;
-        return (bestVersion.getVersion().equals(item.getVersion()));
+        RegisteredType best = RegisteredTypes.getBestVersion(mgmt.getTypeRegistry().getAll(
+            RegisteredTypePredicates.symbolicName(item.getSymbolicName())));
+        if (best==null) return false;
+        return (best.getVersion().equals(item.getVersion()));
     }
 
     /** @deprecated since 0.9.0 use {@link BrooklynTypeRegistry#get(String, org.apache.brooklyn.api.typereg.BrooklynTypeRegistry.RegisteredTypeKind, Class)} */

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
index ee49d39..283b7da 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
@@ -128,7 +128,7 @@ public abstract class AbstractTypePlanTransformer implements BrooklynTypePlanTra
     protected <T> T validate(T createdObject, RegisteredType type, RegisteredTypeLoadingContext constraint) {
         if (createdObject==null) return null;
         try {
-            return RegisteredTypes.validate(createdObject, type, constraint);
+            return RegisteredTypes.validate(createdObject, type, constraint).get();
         } catch (Exception e) {
             Exceptions.propagateIfFatal(e);
             throw new IllegalStateException("Created incompatible object: "+Exceptions.collapseText(e), e);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/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 b36be34..a642861 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
@@ -18,6 +18,7 @@
  */
 package org.apache.brooklyn.core.typereg;
 
+import java.util.Map;
 import java.util.Set;
 
 import javax.annotation.Nullable;
@@ -34,7 +35,8 @@ import org.apache.brooklyn.api.typereg.RegisteredTypeLoadingContext;
 import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
 import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
-import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.test.Asserts;
+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;
@@ -43,16 +45,17 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.api.client.util.Preconditions;
+import com.google.common.annotations.Beta;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.Iterables;
 
 public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry {
 
-    @SuppressWarnings("unused")
     private static final Logger log = LoggerFactory.getLogger(BasicBrooklynTypeRegistry.class);
     
     private ManagementContext mgmt;
+    private Map<String,RegisteredType> localRegisteredTypes = MutableMap.of();
 
     public BasicBrooklynTypeRegistry(ManagementContext mgmt) {
         this.mgmt = mgmt;
@@ -62,40 +65,95 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry {
         return getAll(Predicates.alwaysTrue());
     }
     
+    private Iterable<RegisteredType> getAllWithoutCatalog(Predicate<? super RegisteredType> filter) {
+        // TODO thread safety
+        // TODO optimisation? make indexes and look up?
+        return Iterables.filter(localRegisteredTypes.values(), filter);
+    }
+
+    private RegisteredType getExactWithoutLegacyCatalog(String symbolicName, String version, RegisteredTypeLoadingContext constraint) {
+        // TODO look in any nested/private registries
+        RegisteredType item = localRegisteredTypes.get(symbolicName+":"+version);
+        return RegisteredTypes.validate(item, constraint).orNull();
+    }
+
     @SuppressWarnings("deprecation")
     @Override
     public Iterable<RegisteredType> getAll(Predicate<? super RegisteredType> filter) {
-        return Iterables.filter(Iterables.transform(mgmt.getCatalog().getCatalogItems(), RegisteredTypes.CI_TO_RT), filter);
+        return Iterables.filter(Iterables.concat(
+                getAllWithoutCatalog(filter),
+                Iterables.transform(mgmt.getCatalog().getCatalogItems(), RegisteredTypes.CI_TO_RT)), 
+            filter);
     }
 
     @SuppressWarnings("deprecation")
-    private RegisteredType get(String symbolicName, String version, RegisteredTypeLoadingContext constraint) {
-        // probably constraint is not useful?
-        if (constraint==null) constraint = RegisteredTypeLoadingContexts.any();
+    private Maybe<RegisteredType> getSingle(String symbolicNameOrAliasIfNoVersion, final String versionFinal, final RegisteredTypeLoadingContext contextFinal) {
+        RegisteredTypeLoadingContext context = contextFinal;
+        if (context==null) context = RegisteredTypeLoadingContexts.any();
+        String version = versionFinal;
         if (version==null) version = BrooklynCatalog.DEFAULT_VERSION;
+
+        RegisteredType type;
+        if (!BrooklynCatalog.DEFAULT_VERSION.equals(version)) {
+            type = getExactWithoutLegacyCatalog(symbolicNameOrAliasIfNoVersion, version, context);
+            if (type!=null) return Maybe.of(type);
+        }
         
-        // TODO lookup here, using constraints
+        if (BrooklynCatalog.DEFAULT_VERSION.equals(version)) {
+            Iterable<RegisteredType> types = getAll(Predicates.and(RegisteredTypePredicates.symbolicName(symbolicNameOrAliasIfNoVersion), 
+                RegisteredTypePredicates.satisfies(context)));
+            if (Iterables.isEmpty(types)) {
+                // look for alias if no exact symbolic name match AND no version is specified
+                types = getAll(Predicates.and(RegisteredTypePredicates.alias(symbolicNameOrAliasIfNoVersion), 
+                    RegisteredTypePredicates.satisfies(context) ) );
+                // if there are multiple symbolic names then throw?
+                Set<String> uniqueSymbolicNames = MutableSet.of();
+                for (RegisteredType t: types) {
+                    uniqueSymbolicNames.add(t.getSymbolicName());
+                }
+                if (uniqueSymbolicNames.size()>1) {
+                    log.warn("Multiple matches found for alias '"+symbolicNameOrAliasIfNoVersion+"': "+uniqueSymbolicNames+"; "
+                        + "picking highest version across different symbolic names. Use symbolic_name:version syntax to prevent this lookup.");
+                }
+            }
+            if (!Iterables.isEmpty(types)) {
+                type = RegisteredTypes.getBestVersion(types);
+                if (type!=null) return Maybe.of(type);
+            }
+        }
         
         // fallback to catalog
-        CatalogItem<?, ?> item = mgmt.getCatalog().getCatalogItem(symbolicName, version);
-        // TODO apply constraint
-        return RegisteredTypes.CI_TO_RT.apply( item );
+        CatalogItem<?, ?> item = mgmt.getCatalog().getCatalogItem(symbolicNameOrAliasIfNoVersion, version);
+        if (item!=null) 
+            return Maybe.of( RegisteredTypes.CI_TO_RT.apply( item ) );
+        
+        return Maybe.absent("No matches for "+symbolicNameOrAliasIfNoVersion+
+            (versionFinal!=null ? ":"+versionFinal : "")+
+            (contextFinal!=null ? " ("+contextFinal+")" : "") );
     }
 
     @Override
     public RegisteredType get(String symbolicName, String version) {
-        return get(symbolicName, version, null);
+        return getSingle(symbolicName, version, null).orNull();
     }
     
-    private RegisteredType get(String symbolicNameWithOptionalVersion, RegisteredTypeLoadingContext constraint) {
-        // probably constraint is not useful?
+    @Override
+    public RegisteredType get(String symbolicNameWithOptionalVersion, RegisteredTypeLoadingContext context) {
+        return getMaybe(symbolicNameWithOptionalVersion, context).orNull();
+    }
+    @Override
+    public Maybe<RegisteredType> getMaybe(String symbolicNameWithOptionalVersion, RegisteredTypeLoadingContext context) {
+        Maybe<RegisteredType> r1 = null;
         if (CatalogUtils.looksLikeVersionedId(symbolicNameWithOptionalVersion)) {
             String symbolicName = CatalogUtils.getSymbolicNameFromVersionedId(symbolicNameWithOptionalVersion);
             String version = CatalogUtils.getVersionFromVersionedId(symbolicNameWithOptionalVersion);
-            return get(symbolicName, version, constraint);
-        } else {
-            return get(symbolicNameWithOptionalVersion, BrooklynCatalog.DEFAULT_VERSION, constraint);
+            r1 = getSingle(symbolicName, version, context);
+            if (r1.isPresent()) return r1;
         }
+
+        Maybe<RegisteredType> r2 = getSingle(symbolicNameWithOptionalVersion, BrooklynCatalog.DEFAULT_VERSION, context);
+        if (r2.isPresent() || r1==null) return r2;
+        return r1;
     }
 
     @Override
@@ -191,7 +249,8 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry {
             }
             if (constraint.getAlreadyEncounteredTypes().contains(type.getSymbolicName())) {
                 // avoid recursive cycle
-                // TODO implement using java if permitted
+                // TODO create type using java if permitted?
+                // OR remove this creator from those permitted
             }
         }
         constraint = RegisteredTypeLoadingContexts.withBeanSuperType(constraint, optionalResultSuperType);
@@ -207,4 +266,26 @@ public class BasicBrooklynTypeRegistry implements BrooklynTypeRegistry {
             optionalConstraint, optionalBeanSuperType);
     }
 
+    @Beta // API is stabilising
+    public void addToLocalUnpersistedTypeRegistry(RegisteredType type, boolean canForce) {
+        Preconditions.checkNotNull(type);
+        Preconditions.checkNotNull(type.getSymbolicName());
+        Preconditions.checkNotNull(type.getVersion());
+        Preconditions.checkNotNull(type.getId());
+        if (!type.getId().equals(type.getSymbolicName()+":"+type.getVersion()))
+            Asserts.fail("Registered type "+type+" has ID / symname mismatch");
+        
+        RegisteredType oldType = localRegisteredTypes.get(type.getId());
+        if (oldType==null || canForce) {
+            log.debug("Inserting "+type+" into "+this);
+            localRegisteredTypes.put(type.getId(), type);
+        } else {
+            if (oldType == type) {
+                // ignore if same instance
+                // (equals not yet implemented, so would be the same, but misleading)
+                return;
+            }
+            throw new IllegalStateException("Cannot add "+type+" to catalog; different "+oldType+" is already present");
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java
index 3905d65..05f0773 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java
@@ -36,17 +36,20 @@ import com.google.common.collect.ImmutableSet;
 /** Instances are usually created by methods in {@link RegisteredTypes}. */
 public class BasicRegisteredType implements RegisteredType {
 
+    final RegisteredTypeKind kind;
     final String symbolicName;
     final String version;
-    final RegisteredTypeKind kind;
     
-    Set<Object> superTypes = MutableSet.of();
-    List<OsgiBundleWithUrl> bundles = MutableList.of();
+    final List<OsgiBundleWithUrl> bundles = MutableList.of();
     String displayName;
     String description;
     String iconUrl;
+    
+    final Set<Object> superTypes = MutableSet.of();
     boolean deprecated;
     boolean disabled;
+    final Set<String> aliases = MutableSet.of();
+    final Set<Object> tags = MutableSet.of();
     
     TypeImplementationPlan implementationPlan;
 
@@ -63,6 +66,11 @@ public class BasicRegisteredType implements RegisteredType {
     public String getId() {
         return symbolicName + (version!=null ? ":"+version : "");
     }
+    
+    @Override
+    public RegisteredTypeKind getKind() {
+        return kind;
+    }
 
     @Override
     public String getSymbolicName() {
@@ -73,11 +81,6 @@ public class BasicRegisteredType implements RegisteredType {
     public String getVersion() {
         return version;
     }
-
-    @Override
-    public RegisteredTypeKind getKind() {
-        return kind;
-    }
     
     @Override
     public Collection<OsgiBundleWithUrl> getLibraries() {
@@ -100,6 +103,11 @@ public class BasicRegisteredType implements RegisteredType {
     }
     
     @Override
+    public Set<Object> getSuperTypes() {
+        return ImmutableSet.copyOf(superTypes);
+    }
+
+    @Override
     public boolean isDisabled() {
         return disabled;
     }
@@ -110,10 +118,16 @@ public class BasicRegisteredType implements RegisteredType {
     }
     
     @Override
-    public Set<Object> getSuperTypes() {
-        return ImmutableSet.copyOf(superTypes);
+    public Set<String> getAliases() {
+        return ImmutableSet.copyOf(aliases);
+    }
+
+    @Override
+    public Set<Object> getTags() {
+        return ImmutableSet.copyOf(tags);
     }
 
+    
     @Beta  // TODO depending how useful this is, it might be better to replace by a static WeakHashMap in RegisteredTypes
     public ConfigBag getCache() {
         return cache;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/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 ebeccd7..bf6fb6f 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
@@ -20,14 +20,15 @@ package org.apache.brooklyn.core.typereg;
 
 import javax.annotation.Nullable;
 
-import org.apache.brooklyn.api.catalog.BrooklynCatalog;
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.policy.Policy;
 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 com.google.common.base.Function;
 import com.google.common.base.Predicate;
@@ -93,6 +94,9 @@ public class RegisteredTypePredicates {
         }
     }
 
+    public static Predicate<RegisteredType> symbolicName(final String name) {
+        return symbolicName(Predicates.equalTo(name));
+    }
     public static Predicate<RegisteredType> symbolicName(final Predicate<? super String> filter) {
         return new SymbolicNameMatches(filter);
     }
@@ -109,6 +113,63 @@ public class RegisteredTypePredicates {
         }
     }
 
+    public static Predicate<RegisteredType> version(final String name) {
+        return version(Predicates.equalTo(name));
+    }
+    public static Predicate<RegisteredType> version(final Predicate<? super String> filter) {
+        return new versionMatches(filter);
+    }
+    
+    private static class versionMatches implements Predicate<RegisteredType> {
+        private final Predicate<? super String> filter;
+        
+        public versionMatches(Predicate<? super String> filter) {
+            this.filter = filter;
+        }
+        @Override
+        public boolean apply(@Nullable RegisteredType item) {
+            return (item != null) && filter.apply(item.getVersion());
+        }
+    }
+
+    public static Predicate<RegisteredType> alias(final String alias) {
+        return aliases(CollectionFunctionals.any(Predicates.equalTo(alias)));
+    }
+    public static Predicate<RegisteredType> aliases(final Predicate<Iterable<String>> filter) {
+        return new AliasesMatch(filter);
+    }
+    
+    private static class AliasesMatch implements Predicate<RegisteredType> {
+        private final Predicate<Iterable<String>> filter;
+        
+        public AliasesMatch(Predicate<Iterable<String>> filter) {
+            this.filter = filter;
+        }
+        @Override
+        public boolean apply(@Nullable RegisteredType item) {
+            return (item != null) && filter.apply(item.getAliases());
+        }
+    }
+
+    public static Predicate<RegisteredType> tag(final Object tag) {
+        return tags(CollectionFunctionals.any(Predicates.equalTo(tag)));
+    }
+    public static Predicate<RegisteredType> tags(final Predicate<Iterable<Object>> filter) {
+        return new TagsMatch(filter);
+    }
+    
+    private static class TagsMatch implements Predicate<RegisteredType> {
+        private final Predicate<Iterable<Object>> filter;
+        
+        public TagsMatch(Predicate<Iterable<Object>> filter) {
+            this.filter = filter;
+        }
+        @Override
+        public boolean apply(@Nullable RegisteredType item) {
+            return (item != null) && filter.apply(item.getTags());
+        }
+    }
+
     public static <T> Predicate<RegisteredType> anySuperType(final Predicate<Class<T>> filter) {
         return new AnySuperTypeMatches(filter);
     }
@@ -158,7 +219,6 @@ public class RegisteredTypePredicates {
     public static Predicate<RegisteredType> isBestVersion(final ManagementContext mgmt) {
         return new IsBestVersion(mgmt);
     }
-
     private static class IsBestVersion implements Predicate<RegisteredType> {
         private final ManagementContext mgmt;
 
@@ -170,11 +230,28 @@ public class RegisteredTypePredicates {
             return isBestVersion(mgmt, item);
         }
     }
- 
     public static boolean isBestVersion(ManagementContext mgmt, RegisteredType item) {
-        RegisteredType bestVersion = mgmt.getTypeRegistry().get(item.getSymbolicName(), BrooklynCatalog.DEFAULT_VERSION);
-        if (bestVersion==null) return false;
-        return (bestVersion.getVersion().equals(item.getVersion()));
+        if (item==null) return false;
+        Iterable<RegisteredType> matches = mgmt.getTypeRegistry().getAll(
+            RegisteredTypePredicates.symbolicName(item.getSymbolicName()) );
+        if (!matches.iterator().hasNext()) return false;
+        RegisteredType best = RegisteredTypes.getBestVersion(matches);
+        return (best.getVersion().equals(item.getVersion()));
+    }
+    
+    public static Predicate<RegisteredType> satisfies(RegisteredTypeLoadingContext context) {
+        return new SatisfiesContext(context);
+    }
+    private static class SatisfiesContext implements Predicate<RegisteredType> {
+        private final RegisteredTypeLoadingContext context;
+
+        public SatisfiesContext(RegisteredTypeLoadingContext context) {
+            this.context = context;
+        }
+        @Override
+        public boolean apply(@Nullable RegisteredType item) {
+            return RegisteredTypes.validate(item, context).isPresent();
+        }
     }
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/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 18f8f43..3a89e60 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,6 +19,8 @@
 package org.apache.brooklyn.core.typereg;
 
 import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
@@ -30,7 +32,6 @@ import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry.RegisteredTypeKind;
-import org.apache.brooklyn.api.typereg.OsgiBundleWithUrl;
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.api.typereg.RegisteredType.TypeImplementationPlan;
 import org.apache.brooklyn.api.typereg.RegisteredTypeLoadingContext;
@@ -39,15 +40,18 @@ import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
 import org.apache.brooklyn.core.typereg.JavaClassNameTypePlanTransformer.JavaClassNameTypeImplementationPlan;
+import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.text.NaturalOrderComparator;
+import org.apache.brooklyn.util.text.VersionComparator;
 import org.apache.brooklyn.util.yaml.Yamls;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ComparisonChain;
 import com.google.common.reflect.TypeToken;
 
 /**
@@ -88,16 +92,18 @@ public class RegisteredTypes {
         }
         
         BasicRegisteredType type = (BasicRegisteredType) spec(item.getSymbolicName(), item.getVersion(), impl, item.getCatalogItemJavaType());
-        type.bundles = item.getLibraries()==null ? ImmutableList.<OsgiBundleWithUrl>of() : ImmutableList.<OsgiBundleWithUrl>copyOf(item.getLibraries());
         type.displayName = item.getDisplayName();
         type.description = item.getDescription();
         type.iconUrl = item.getIconUrl();
+        
+        if (item.getLibraries()!=null) type.bundles.addAll(item.getLibraries());
         type.disabled = item.isDisabled();
         type.deprecated = item.isDeprecated();
+        if (item.getLibraries()!=null) type.bundles.addAll(item.getLibraries());
+        // aliases aren't on item
+        if (item.tags()!=null) type.tags.addAll(item.tags().getTags());
 
-        // TODO
-        // probably not: javaType, specType, registeredTypeName ...
-        // maybe: tags ?
+        // these things from item we ignore: javaType, specType, registeredTypeName ...
         return type;
     }
 
@@ -135,7 +141,6 @@ public class RegisteredTypes {
         }
         return type;
     }
-
     @Beta
     public static RegisteredType addSuperType(RegisteredType type, @Nullable RegisteredType superType) {
         if (superType!=null) {
@@ -146,6 +151,53 @@ public class RegisteredTypes {
         }
         return type;
     }
+    @Beta
+    public static RegisteredType addSuperTypes(RegisteredType type, Iterable<Object> superTypesAsClassOrRegisteredType) {
+        if (superTypesAsClassOrRegisteredType!=null) {
+            for (Object superType: superTypesAsClassOrRegisteredType) {
+                if (superType==null) {
+                    // nothing
+                } else if (superType instanceof Class) {
+                    addSuperType(type, (Class<?>)superType);
+                } else if (superType instanceof RegisteredType) {
+                    addSuperType(type, (RegisteredType)superType);
+                } else {
+                    throw new IllegalStateException(superType+" supplied as a supertype of "+type+" but it is not a supported supertype");
+                }
+            }
+        }
+        return type;
+    }
+
+    @Beta
+    public static RegisteredType addAlias(RegisteredType type, String alias) {
+        if (alias!=null) {
+            ((BasicRegisteredType)type).aliases.add( alias );
+        }
+        return type;
+    }
+    @Beta
+    public static RegisteredType addAliases(RegisteredType type, Iterable<String> aliases) {
+        if (aliases!=null) {
+            for (String alias: aliases) addAlias(type, alias);
+        }
+        return type;
+    }
+
+    @Beta
+    public static RegisteredType addTag(RegisteredType type, Object tag) {
+        if (tag!=null) {
+            ((BasicRegisteredType)type).tags.add( tag );
+        }
+        return type;
+    }
+    @Beta
+    public static RegisteredType addTags(RegisteredType type, Iterable<?> tags) {
+        if (tags!=null) {
+            for (Object tag: tags) addTag(type, tag);
+        }
+        return type;
+    }
 
     /** returns the implementation data for a spec if it is a string (e.g. plan yaml or java class name); else throws */
     @Beta
@@ -232,16 +284,16 @@ public class RegisteredTypes {
         return false;
     }
 
-    public static RegisteredType validate(RegisteredType item, final RegisteredTypeLoadingContext constraint) {
-        if (item==null || constraint==null) return item;
+    public static Maybe<RegisteredType> validate(RegisteredType item, final RegisteredTypeLoadingContext constraint) {
+        if (item==null || constraint==null) return Maybe.of(item);
         if (constraint.getExpectedKind()!=null && !constraint.getExpectedKind().equals(item.getKind()))
-            throw new IllegalStateException(item+" is not the expected kind "+constraint.getExpectedKind());
+            return Maybe.absent(item+" is not the expected kind "+constraint.getExpectedKind());
         if (constraint.getExpectedJavaSuperType()!=null) {
             if (!isSubtypeOf(item, constraint.getExpectedJavaSuperType())) {
-                throw new IllegalStateException(item+" is not for the expected type "+constraint.getExpectedJavaSuperType());
+                return Maybe.absent(item+" is not for the expected type "+constraint.getExpectedJavaSuperType());
             }
         }
-        return item;
+        return Maybe.of(item);
     }
 
     /** 
@@ -258,68 +310,87 @@ public class RegisteredTypes {
         return true;
     }
 
-    public static <T> T validate(final T object, final RegisteredType type, final RegisteredTypeLoadingContext constraint) {
+    public static RegisteredType getBestVersion(Iterable<RegisteredType> types) {
+        if (types==null || !types.iterator().hasNext()) return null;
+        return Collections.max(MutableList.copyOf(types), RegisteredTypeComparator.INSTANCE);
+    }
+    
+    public static class RegisteredTypeComparator implements Comparator<RegisteredType> {
+        public static Comparator<RegisteredType> INSTANCE = new RegisteredTypeComparator();
+        private RegisteredTypeComparator() {}
+        @Override
+        public int compare(RegisteredType o1, RegisteredType o2) {
+            return ComparisonChain.start()
+                .compareTrueFirst(o1.isDisabled(), o2.isDisabled())
+                .compareTrueFirst(o1.isDeprecated(), o2.isDeprecated())
+                .compare(o1.getSymbolicName(), o2.getSymbolicName(), NaturalOrderComparator.INSTANCE)
+                .compare(o1.getVersion(), o2.getVersion(), VersionComparator.INSTANCE)
+                .result();
+        }
+    }
+
+    public static <T> Maybe<T> validate(final T object, final RegisteredType type, final RegisteredTypeLoadingContext constraint) {
         RegisteredTypeKind kind = type!=null ? type.getKind() : constraint!=null ? constraint.getExpectedKind() : null;
         if (kind==null) {
             if (object instanceof AbstractBrooklynObjectSpec) kind=RegisteredTypeKind.SPEC;
             else kind=RegisteredTypeKind.BEAN;
         }
-        return new RegisteredTypeKindVisitor<T>() {
+        return new RegisteredTypeKindVisitor<Maybe<T>>() {
             @Override
-            protected T visitSpec() {
+            protected Maybe<T> visitSpec() {
                 return validateSpec(object, type, constraint);
             }
 
             @Override
-            protected T visitBean() {
+            protected Maybe<T> visitBean() {
                 return validateBean(object, type, constraint);
             }
         }.visit(kind);
     }
 
-    private static <T> T validateBean(T object, RegisteredType type, final RegisteredTypeLoadingContext constraint) {
-        if (object==null) return null;
+    private static <T> Maybe<T> validateBean(T object, RegisteredType type, final RegisteredTypeLoadingContext constraint) {
+        if (object==null) return Maybe.absent("object is null");
         
         if (type!=null) {
             if (type.getKind()!=RegisteredTypeKind.BEAN)
-                throw new IllegalStateException("Validating a bean when type is "+type.getKind()+" "+type);
+                return Maybe.absent("Validating a bean when type is "+type.getKind()+" "+type);
             if (!isSubtypeOf(object.getClass(), type))
-                throw new IllegalStateException(object+" does not have all the java supertypes of "+type);
+                return Maybe.absent(object+" does not have all the java supertypes of "+type);
         }
 
         if (constraint!=null) {
             if (constraint.getExpectedKind()!=RegisteredTypeKind.BEAN)
-                throw new IllegalStateException("Validating a bean when constraint expected "+constraint.getExpectedKind());
+                return Maybe.absent("Validating a bean when constraint expected "+constraint.getExpectedKind());
             if (constraint.getExpectedJavaSuperType()!=null && !constraint.getExpectedJavaSuperType().isInstance(object))
-                throw new IllegalStateException(object+" is not of the expected java supertype "+constraint.getExpectedJavaSuperType());
+                return Maybe.absent(object+" is not of the expected java supertype "+constraint.getExpectedJavaSuperType());
         }
         
-        return object;
+        return Maybe.of(object);
     }
 
-    private static <T> T validateSpec(T object, RegisteredType rType, final RegisteredTypeLoadingContext constraint) {
-        if (object==null) return null;
+    private static <T> Maybe<T> validateSpec(T object, RegisteredType rType, final RegisteredTypeLoadingContext constraint) {
+        if (object==null) return Maybe.absent("object is null");
         
         if (!(object instanceof AbstractBrooklynObjectSpec)) {
-            throw new IllegalStateException("Found "+object+" when expecting a spec");
+            Maybe.absent("Found "+object+" when expecting a spec");
         }
         Class<?> targetType = ((AbstractBrooklynObjectSpec<?,?>)object).getType();
         
         if (targetType==null) {
-            throw new IllegalStateException("Spec "+object+" does not have a target type");
+            Maybe.absent("Spec "+object+" does not have a target type");
         }
         
         if (rType!=null) {
             if (rType.getKind()!=RegisteredTypeKind.SPEC)
-                throw new IllegalStateException("Validating a spec when type is "+rType.getKind()+" "+rType);
+                Maybe.absent("Validating a spec when type is "+rType.getKind()+" "+rType);
             if (!isSubtypeOf(targetType, rType))
-                throw new IllegalStateException(object+" does not have all the java supertypes of "+rType);
+                Maybe.absent(object+" does not have all the java supertypes of "+rType);
         }
 
         if (constraint!=null) {
             if (constraint.getExpectedJavaSuperType()!=null) {
                 if (!constraint.getExpectedJavaSuperType().isAssignableFrom(targetType)) {
-                    throw new IllegalStateException(object+" does not target the expected java supertype "+constraint.getExpectedJavaSuperType());
+                    Maybe.absent(object+" does not target the expected java supertype "+constraint.getExpectedJavaSuperType());
                 }
                 if (constraint.getExpectedJavaSuperType().isAssignableFrom(BrooklynObjectInternal.class)) {
                     // don't check spec type; any spec is acceptable
@@ -328,15 +399,15 @@ public class RegisteredTypes {
                     Class<? extends AbstractBrooklynObjectSpec<?, ?>> specType = RegisteredTypeLoadingContexts.lookupSpecTypeForTarget( (Class<? extends BrooklynObject>) constraint.getExpectedJavaSuperType());
                     if (specType==null) {
                         // means a problem in our classification of spec types!
-                        throw new IllegalStateException(object+" is returned as spec for unexpected java supertype "+constraint.getExpectedJavaSuperType());
+                        Maybe.absent(object+" is returned as spec for unexpected java supertype "+constraint.getExpectedJavaSuperType());
                     }
                     if (!specType.isAssignableFrom(object.getClass())) {
-                        throw new IllegalStateException(object+" is not a spec of the expected java supertype "+constraint.getExpectedJavaSuperType());
+                        Maybe.absent(object+" is not a spec of the expected java supertype "+constraint.getExpectedJavaSuperType());
                     }
                 }
             }
         }
-        return object;
+        return Maybe.of(object);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogItemComparatorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogItemComparatorTest.java b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogItemComparatorTest.java
index e7d43cd..3c8ce83 100644
--- a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogItemComparatorTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogItemComparatorTest.java
@@ -21,22 +21,25 @@ package org.apache.brooklyn.core.catalog.internal;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
-import org.testng.annotations.Test;
 import org.apache.brooklyn.api.catalog.CatalogItem;
-import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder;
-import org.apache.brooklyn.core.catalog.internal.CatalogItemComparator;
+import org.testng.annotations.Test;
 
 public class CatalogItemComparatorTest {
     private static final String RC2 = "10.5.8-rc2";
     private static final String STABLE = "10.5.8";
 
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     @Test
     public void testComparison() {
         compare("0.0.1", "0.0.2", 1);
         compare("0.0.2", "0.0.1", -1);
         compare("0.0.1-qual", "0.0.2", 1);
         compare("0.0.1.qual", "0.0.2", 1);
-        compare("0.0.1-qual", "0.0.1_qual", 0);
+        
+        // NB: semantics of this changed in 090-SNAPSHOT not to be 0 unless identical
+        // (remove when we're used to this)
+//        compare("0.0.1-qual", "0.0.1_qual", 0);
+        
         compare("0.0.1.qual", "0.0.1.qual", 0);
         compare("0.0.1", "0.0.2-SNAPSHOT", -1);
         compare("0.0.1", "0.0.2.SNAPSHOT", -1);
@@ -70,6 +73,7 @@ public class CatalogItemComparatorTest {
         assertTrue(cmp.compare(v(RC2), v("10.5.8-beta1")) == cmp.compare(v(RC2), v("10.5.8-beta3")));
     }
     
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     private void compare(String v1, String v2, int expected) {
         CatalogItemComparator cmp = CatalogItemComparator.INSTANCE;
         assertEquals(cmp.compare(v(v1), v(v2)), expected);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/core/src/test/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistryTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistryTest.java b/core/src/test/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistryTest.java
new file mode 100644
index 0000000..a62ab7e
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/typereg/BasicBrooklynTypeRegistryTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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.core.typereg;
+
+import org.apache.brooklyn.api.typereg.RegisteredType;
+import org.apache.brooklyn.core.test.BrooklynMgmtUnitTestSupport;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+
+public class BasicBrooklynTypeRegistryTest extends BrooklynMgmtUnitTestSupport {
+
+    private BasicBrooklynTypeRegistry registry() {
+        return (BasicBrooklynTypeRegistry) mgmt.getTypeRegistry();
+    }
+    
+    private void add(RegisteredType type) {
+        add(type, false);
+    }
+    private void add(RegisteredType type, boolean canForce) {
+        registry().addToLocalUnpersistedTypeRegistry(type, canForce);
+    }
+    
+    private final static RegisteredType SAMPLE_TYPE = RegisteredTypes.bean("item.A", "1", new BasicTypeImplementationPlan("ignore", null), String.class);
+    private final static RegisteredType SAMPLE_TYPE2 = RegisteredTypes.bean("item.A", "2", new BasicTypeImplementationPlan("ignore", null), String.class);
+    
+    @Test
+    public void testAddAndGet() {
+        Assert.assertFalse( Iterables.contains(registry().getAll(), SAMPLE_TYPE) );
+        Assert.assertNull( registry().get(SAMPLE_TYPE.getSymbolicName(), SAMPLE_TYPE.getVersion()) );
+        Assert.assertNull( registry().get(SAMPLE_TYPE.getId()) );
+        add(SAMPLE_TYPE);
+        
+        Assert.assertTrue( Iterables.contains(registry().getAll(), SAMPLE_TYPE) );
+        Assert.assertEquals( registry().get(SAMPLE_TYPE.getSymbolicName(), SAMPLE_TYPE.getVersion()), SAMPLE_TYPE );
+        Assert.assertEquals( registry().get(SAMPLE_TYPE.getId()), SAMPLE_TYPE );
+        
+        Assert.assertTrue( Iterables.contains(registry().getAll(
+            RegisteredTypePredicates.symbolicName(SAMPLE_TYPE.getSymbolicName())), SAMPLE_TYPE) );
+    }
+
+    @Test
+    public void testCantAddSameIdUnlessSameInstanceOrForced() {
+        add(SAMPLE_TYPE);
+        RegisteredType sampleTypeClone = RegisteredTypes.bean("item.A", "1", new BasicTypeImplementationPlan("ignore", null), String.class);
+        add(sampleTypeClone, true);
+        Assert.assertNotEquals( registry().get(SAMPLE_TYPE.getId()), SAMPLE_TYPE );
+        
+        add(SAMPLE_TYPE, true);
+        Assert.assertEquals( registry().get(SAMPLE_TYPE.getId()), SAMPLE_TYPE );
+
+        try {
+            add(sampleTypeClone);
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Exception e) {
+            Asserts.expectedFailureContains(e, SAMPLE_TYPE.getSymbolicName());
+        }
+        
+        // only one entry
+        Assert.assertEquals( Iterables.size(registry().getAll(
+            RegisteredTypePredicates.symbolicName(SAMPLE_TYPE.getSymbolicName()))), 1);
+        // unversioned request returns sample
+        Assert.assertEquals( registry().get(SAMPLE_TYPE.getSymbolicName()), SAMPLE_TYPE );
+    }
+
+    @Test
+    public void testGettingBestVersion() {
+        add(SAMPLE_TYPE);
+        add(SAMPLE_TYPE2);
+        
+        Assert.assertTrue( Iterables.contains(registry().getAll(), SAMPLE_TYPE) );
+        Assert.assertTrue( Iterables.contains(registry().getAll(), SAMPLE_TYPE2) );
+        Assert.assertEquals( registry().get(SAMPLE_TYPE.getId()), SAMPLE_TYPE );
+        Assert.assertEquals( registry().get(SAMPLE_TYPE2.getId()), SAMPLE_TYPE2 );
+        Assert.assertNotEquals( registry().get(SAMPLE_TYPE2.getId()), SAMPLE_TYPE );
+        
+        Assert.assertEquals( Iterables.size(registry().getAll(
+            RegisteredTypePredicates.symbolicName(SAMPLE_TYPE.getSymbolicName()))), 2);
+        
+        // unversioned request returns latest
+        Assert.assertEquals( registry().get(SAMPLE_TYPE.getSymbolicName()), SAMPLE_TYPE2 );
+    }
+
+    @Test
+    public void testGetWithFilter() {
+        add(SAMPLE_TYPE);
+        
+        Assert.assertEquals( Iterables.size(registry().getAll(Predicates.and(
+            RegisteredTypePredicates.symbolicName(SAMPLE_TYPE.getSymbolicName()),
+            RegisteredTypePredicates.subtypeOf(String.class)
+            ))), 1 );
+        Assert.assertTrue( Iterables.isEmpty(registry().getAll(Predicates.and(
+                RegisteredTypePredicates.symbolicName(SAMPLE_TYPE.getSymbolicName()),
+                RegisteredTypePredicates.subtypeOf(Integer.class)
+            ))) );
+    }
+    
+    @Test
+    public void testGetWithContext() {
+        add(SAMPLE_TYPE);
+        Assert.assertEquals( registry().get(SAMPLE_TYPE.getId(),  
+            RegisteredTypeLoadingContexts.bean(String.class)), SAMPLE_TYPE );
+        Assert.assertEquals( registry().get(SAMPLE_TYPE.getId(),  
+            RegisteredTypeLoadingContexts.bean(Integer.class)), null );
+    }
+
+    @Test
+    public void testAlias() {
+        add(SAMPLE_TYPE);
+        add(SAMPLE_TYPE2);
+        
+        RegisteredType sampleType15WithAliases = RegisteredTypes.addAliases(
+            RegisteredTypes.bean("item.A", "1.1", new BasicTypeImplementationPlan("ignore", null), String.class),
+            MutableList.of("my_a", "the_a"));
+        add(sampleType15WithAliases);
+        Assert.assertEquals(sampleType15WithAliases.getAliases(), MutableSet.of("my_a", "the_a"));
+        
+        Assert.assertEquals( Iterables.size(registry().getAll(
+            RegisteredTypePredicates.symbolicName(SAMPLE_TYPE.getSymbolicName()))), 3);
+        
+        Assert.assertEquals( registry().get("my_a"), sampleType15WithAliases );
+        Assert.assertEquals( registry().get("the_a"), sampleType15WithAliases );
+        Assert.assertEquals( registry().get(sampleType15WithAliases.getId()), sampleType15WithAliases );
+        
+        // but unadorned type still returns v2
+        Assert.assertEquals( registry().get(sampleType15WithAliases.getSymbolicName()), SAMPLE_TYPE2 );
+        
+        // and filters work
+        Assert.assertEquals( registry().getAll(RegisteredTypePredicates.alias("the_a")),
+            MutableList.of(sampleType15WithAliases) );
+        Assert.assertEquals( registry().get("my_a",  
+            RegisteredTypeLoadingContexts.bean(String.class)), sampleType15WithAliases );
+        Assert.assertEquals( registry().get("my_a",  
+            RegisteredTypeLoadingContexts.bean(Integer.class)), null );
+    }
+
+    @Test
+    public void testTags() {
+        add(SAMPLE_TYPE);
+        add(SAMPLE_TYPE2);
+        
+        RegisteredType sampleType15WithTags = RegisteredTypes.addTags(
+            RegisteredTypes.bean("item.A", "1.1", new BasicTypeImplementationPlan("ignore", null), String.class),
+            MutableList.of("my_a", "the_a"));
+        add(sampleType15WithTags);
+        Assert.assertEquals(sampleType15WithTags.getTags(), MutableSet.of("my_a", "the_a"));
+        
+        Assert.assertEquals( Iterables.size(registry().getAll(
+            RegisteredTypePredicates.symbolicName(SAMPLE_TYPE.getSymbolicName()))), 3);
+        
+        Assert.assertEquals( registry().get(sampleType15WithTags.getId()), sampleType15WithTags );
+        
+        // and filters work
+        Assert.assertEquals( registry().getAll(RegisteredTypePredicates.tag("the_a")),
+            MutableList.of(sampleType15WithTags) );
+        
+        // but can't lookup by tag as a get
+        Assert.assertEquals( registry().get("my_a"), null );
+        
+        // and unadorned type still returns v2
+        Assert.assertEquals( registry().get(sampleType15WithTags.getSymbolicName()), SAMPLE_TYPE2 );
+        
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/core/src/test/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicatesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicatesTest.java b/core/src/test/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicatesTest.java
index 145c056..9523946 100644
--- a/core/src/test/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicatesTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicatesTest.java
@@ -24,28 +24,13 @@ import static org.testng.Assert.assertTrue;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder;
-import org.apache.brooklyn.core.entity.Entities;
-import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
-import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
+import org.apache.brooklyn.core.test.BrooklynMgmtUnitTestSupport;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Predicates;
 
 
-public class RegisteredTypePredicatesTest {
-    private LocalManagementContext mgmt;
-    
-    @BeforeMethod(alwaysRun = true)
-    public void setUp() throws Exception {
-        mgmt = LocalManagementContextForTests.newInstance();
-    }
-    
-    @AfterMethod(alwaysRun = true)
-    public void tearDown() throws Exception {
-        if (mgmt != null) Entities.destroyAll(mgmt);
-    }
+public class RegisteredTypePredicatesTest extends BrooklynMgmtUnitTestSupport {
 
     @Test
     public void testDisplayName() {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
index 0147b9d..acde8ec 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
@@ -115,7 +115,7 @@ public abstract class BrooklynEntityDecorationResolver<DT> {
             String policyType = decoLoader.getTypeName().get();
             ManagementContext mgmt = instantiator.loader.getManagementContext();
             
-            RegisteredType item = RegisteredTypes.validate(mgmt.getTypeRegistry().get(policyType), RegisteredTypeLoadingContexts.spec(Policy.class));
+            RegisteredType item = RegisteredTypes.validate(mgmt.getTypeRegistry().get(policyType), RegisteredTypeLoadingContexts.spec(Policy.class)).get();
             PolicySpec<?> spec;
             if (item!=null) {
                 spec = mgmt.getTypeRegistry().createSpec(item, null, PolicySpec.class);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java
index d8e4b1d..57adfeb 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java
@@ -230,7 +230,7 @@ public class CampYamlLiteTest {
     }
 
     private void assertMgmtHasSampleMyCatalogApp(String symbolicName, String bundleUrl) {
-        RegisteredType item = RegisteredTypes.validate(mgmt.getTypeRegistry().get(symbolicName), RegisteredTypeLoadingContexts.spec(Entity.class));
+        RegisteredType item = RegisteredTypes.validate(mgmt.getTypeRegistry().get(symbolicName), RegisteredTypeLoadingContexts.spec(Entity.class)).get();
         assertNotNull(item, "failed to load item with id=" + symbolicName + " from catalog. Entries were: " +
                 Joiner.on(",").join(mgmt.getTypeRegistry().getAll()));
         assertEquals(item.getSymbolicName(), symbolicName);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
index ecbeffb..03b05f9 100644
--- a/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
+++ b/usage/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
@@ -149,7 +149,7 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
                 Entitlements.getEntitlementContext().user());
         }
         try {
-            RegisteredType item = RegisteredTypes.validate(mgmt().getTypeRegistry().get(entityId), RegisteredTypeLoadingContexts.spec(Entity.class));
+            RegisteredType item = RegisteredTypes.validate(mgmt().getTypeRegistry().get(entityId), RegisteredTypeLoadingContexts.spec(Entity.class)).get();
             if (item==null) {
                 throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId);
             }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/utils/common/src/main/java/org/apache/brooklyn/util/collections/CollectionFunctionals.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/collections/CollectionFunctionals.java b/utils/common/src/main/java/org/apache/brooklyn/util/collections/CollectionFunctionals.java
index aa6831b..91f53f9 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/collections/CollectionFunctionals.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/collections/CollectionFunctionals.java
@@ -205,15 +205,36 @@ public class CollectionFunctionals {
 
     // ---------
     
+    /** 
+     * Returns a predicate for a collection which is true if 
+     * all elements in the collection given to the predicate
+     * which satisfies the predicate given here.
+     * <p>
+     * This will return true for the empty set.
+     * To require additionally that there is at least one
+     * use {@link #quorum(QuorumCheck, Predicate)} with
+     * {@link QuorumChecks#allAndAtLeastOne()}. */
     public static <T,TT extends Iterable<T>> Predicate<TT> all(Predicate<T> attributeSatisfies) {
         return quorum(QuorumChecks.all(), attributeSatisfies);
     }
 
+    /** Returns a predicate for a collection which is true if 
+     * there is at least one element in the collection given to the predicate
+     * which satisfies the predicate given here. 
+     */
+    public static <T,TT extends Iterable<T>> Predicate<TT> any(Predicate<T> attributeSatisfies) {
+        // implementation could be more efficient -- ie succeed fast
+        return quorum(QuorumChecks.atLeastOne(), attributeSatisfies);
+    }
+
+    /** Returns a predicate for a collection which is true if 
+     * the number of elements in the collection satisfying the predicate given here
+     * passes the {@link QuorumCheck} given here.
+     */
     public static <T,TT extends Iterable<T>> Predicate<TT> quorum(QuorumCheck quorumCheck, Predicate<T> attributeSatisfies) {
         return new QuorumSatisfies<T, TT>(quorumCheck, attributeSatisfies);
     }
 
-
     private static final class QuorumSatisfies<I,T extends Iterable<I>> implements Predicate<T> {
         private final Predicate<I> itemCheck;
         private final QuorumCheck quorumCheck;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/420ea303/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionFunctionalsTest.java
----------------------------------------------------------------------
diff --git a/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionFunctionalsTest.java b/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionFunctionalsTest.java
index 1d75297..1ce4015 100644
--- a/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionFunctionalsTest.java
+++ b/utils/common/src/test/java/org/apache/brooklyn/util/collections/CollectionFunctionalsTest.java
@@ -22,6 +22,7 @@ import org.apache.brooklyn.util.collections.CollectionFunctionals;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
@@ -55,4 +56,27 @@ public class CollectionFunctionalsTest {
         Assert.assertEquals(CollectionFunctionals.firstElement().apply(ImmutableList.of("a")), "a");
         Assert.assertEquals(CollectionFunctionals.firstElement().apply(ImmutableList.of("a", "b", "c")), "a");
     }
+
+    @Test
+    public void testAllAndAny() {
+        Assert.assertEquals(CollectionFunctionals.all(Predicates.equalTo(1)).apply(
+            MutableList.of(1, 1, 1)), true);
+        Assert.assertEquals(CollectionFunctionals.all(Predicates.equalTo(1)).apply(
+            MutableList.<Integer>of()), true);
+        Assert.assertEquals(CollectionFunctionals.all(Predicates.equalTo(1)).apply(
+            MutableList.of(1, 0, 1)), false);
+        Assert.assertEquals(CollectionFunctionals.all(Predicates.equalTo(1)).apply(
+            MutableList.of(0, 0, 0)), false);
+        
+        Assert.assertEquals(CollectionFunctionals.any(Predicates.equalTo(1)).apply(
+            MutableList.of(1, 1, 1)), true);
+        Assert.assertEquals(CollectionFunctionals.any(Predicates.equalTo(1)).apply(
+            MutableList.<Integer>of()), false);
+        Assert.assertEquals(CollectionFunctionals.any(Predicates.equalTo(1)).apply(
+            MutableList.of(1, 0, 1)), true);
+        Assert.assertEquals(CollectionFunctionals.any(Predicates.equalTo(1)).apply(
+            MutableList.of(0, 0, 0)), false);
+        
+    }
+    
 }