You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by sj...@apache.org on 2014/08/27 12:02:20 UTC

[3/6] git commit: Support for adding YAML defined policies to the catalog.

Support for adding YAML defined policies to the catalog.

OSGi support included.


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

Branch: refs/heads/master
Commit: eb1c98679eea88a3957bfc85eac09ab3ee12c358
Parents: e4f6ce3
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Fri Aug 15 22:22:31 2014 +0300
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Tue Aug 26 18:37:15 2014 +0300

----------------------------------------------------------------------
 .../catalog/internal/BasicBrooklynCatalog.java  | 149 +++++++----
 .../catalog/internal/CatalogItemBuilder.java    |  90 +++++++
 .../java/brooklyn/osgi/tests/SimplePolicy.java  |  36 +++
 .../osgi/brooklyn-test-osgi-entities.jar        | Bin 10891 -> 14663 bytes
 .../BrooklynEntityDecorationResolver.java       |  22 +-
 .../camp/brooklyn/CatalogYamlEntityTest.java    | 254 +++++++++++++++++++
 .../camp/brooklyn/CatalogYamlPolicyTest.java    | 170 +++++++++++++
 .../brooklyn/camp/brooklyn/CatalogYamlTest.java | 250 ------------------
 .../rest/domain/CatalogPolicySummary.java       |   3 +-
 .../rest/transform/CatalogTransformer.java      |   3 +-
 .../src/main/java/brooklyn/util/yaml/Yamls.java |  17 +-
 11 files changed, 693 insertions(+), 301 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb1c9867/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
index 176975f..f3c3f40 100644
--- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
@@ -18,6 +18,7 @@
  */
 package brooklyn.catalog.internal;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import io.brooklyn.camp.CampPlatform;
 import io.brooklyn.camp.spi.AssemblyTemplate;
 import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator;
@@ -40,6 +41,8 @@ import brooklyn.catalog.CatalogPredicates;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.management.ManagementContext;
 import brooklyn.management.classloading.BrooklynClassLoadingContext;
+import brooklyn.policy.Policy;
+import brooklyn.policy.PolicySpec;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
@@ -50,15 +53,17 @@ import brooklyn.util.stream.Streams;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
+import brooklyn.util.yaml.Yamls;
 
 import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 
 public class BasicBrooklynCatalog implements BrooklynCatalog {
+    private static final String POLICIES_KEY = "brooklyn.policies";
 
     private static final Logger log = LoggerFactory.getLogger(BasicBrooklynCatalog.class);
 
@@ -85,7 +90,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     private volatile LoadedClassLoader manualAdditionsClasses;
 
     public BasicBrooklynCatalog(final ManagementContext mgmt, final CatalogDto dto) {
-        this.mgmt = Preconditions.checkNotNull(mgmt, "managementContext");
+        this.mgmt = checkNotNull(mgmt, "managementContext");
         this.catalog = new CatalogDo(mgmt, dto);
     }
 
@@ -124,7 +129,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     @Override
     public void deleteCatalogItem(String id) {
         log.debug("Deleting manual catalog item from "+mgmt+": "+id);
-        Preconditions.checkNotNull(id, "id");
+        checkNotNull(id, "id");
         CatalogItem<?, ?> item = getCatalogItem(id);
         CatalogItemDtoAbstract<?,?> itemDto = getAbstractCatalogItem(item);
         if (itemDto == null) {
@@ -167,38 +172,23 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     @Override
     public <T, SpecT> SpecT createSpec(CatalogItem<T, SpecT> item) {
         CatalogItemDo<T,SpecT> loadedItem = (CatalogItemDo<T, SpecT>) getCatalogItemDo(item.getId());
-        
+
         Class<SpecT> specType = loadedItem.getSpecType();
         if (specType==null) return null;
-            
+
         String yaml = loadedItem.getPlanYaml();
         SpecT spec = null;
-            
+
         if (yaml!=null) {
             DeploymentPlan plan = makePlanFromYaml(yaml);
-            CampPlatform camp = BrooklynServerConfig.getCampPlatform(mgmt).get();
-            
-            // TODO should not register new AT each time we instantiate from the same plan; use some kind of cache
-            AssemblyTemplate at;
-            BrooklynClassLoadingContext loader = loadedItem.newClassLoadingContext(mgmt);
-            BrooklynLoaderTracker.setLoader(loader);
-            try {
-                at = camp.pdp().registerDeploymentPlan(plan);
-            } finally {
-                BrooklynLoaderTracker.unsetLoader(loader);
-            }
-            
-            try {
-                AssemblyTemplateInstantiator instantiator = at.getInstantiator().newInstance();
-                if (instantiator instanceof AssemblyTemplateSpecInstantiator) {
-                    return (SpecT) ((AssemblyTemplateSpecInstantiator)instantiator).createSpec(at, camp);
-                }
-                throw new IllegalStateException("Unable to instantiate YAML; incompatible instantiator "+instantiator+" for "+at);
-            } catch (Exception e) {
-                throw Exceptions.propagate(e);
+            switch (item.getCatalogItemType()) {
+                case ENTITY:return createEntitySpec(loadedItem, plan);
+                case POLICY:return createPolicySpec(loadedItem, plan);
+                default: throw new RuntimeException("Only entity & policy catalog items are supported. Unsupported catalog item type " + item.getCatalogItemType());
             }
+
         }
-            
+
         // revert to legacy mechanism
         try {
             if (loadedItem.getJavaType()!=null) {
@@ -215,7 +205,64 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
 
         return spec;
     }
-    
+
+    @SuppressWarnings("unchecked")
+    private <T, SpecT> SpecT createEntitySpec(
+            CatalogItemDo<T, SpecT> loadedItem, DeploymentPlan plan) {
+        CampPlatform camp = BrooklynServerConfig.getCampPlatform(mgmt).get();
+
+        // TODO should not register new AT each time we instantiate from the same plan; use some kind of cache
+        AssemblyTemplate at;
+        BrooklynClassLoadingContext loader = loadedItem.newClassLoadingContext(mgmt);
+        BrooklynLoaderTracker.setLoader(loader);
+        try {
+            at = camp.pdp().registerDeploymentPlan(plan);
+        } finally {
+            BrooklynLoaderTracker.unsetLoader(loader);
+        }
+
+        try {
+            AssemblyTemplateInstantiator instantiator = at.getInstantiator().newInstance();
+            if (instantiator instanceof AssemblyTemplateSpecInstantiator) {
+                return (SpecT) ((AssemblyTemplateSpecInstantiator)instantiator).createSpec(at, camp);
+            }
+            throw new IllegalStateException("Unable to instantiate YAML; incompatible instantiator "+instantiator+" for "+at);
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+
+    @SuppressWarnings("unchecked")
+    private <T, SpecT> SpecT createPolicySpec(CatalogItemDo<T, SpecT> loadedItem, DeploymentPlan plan) {
+        //Would ideally re-use io.brooklyn.camp.brooklyn.spi.creation.BrooklynEntityDecorationResolver.PolicySpecResolver
+        //but it is CAMP specific and there is no easy way to get hold of it.
+        Object policies = checkNotNull(plan.getCustomAttributes().get(POLICIES_KEY), "policy config");
+        if (!(policies instanceof Iterable<?>)) {
+            throw new IllegalStateException("The value of " + POLICIES_KEY + " must be an Iterable.");
+        }
+
+        Object policy = Iterables.getOnlyElement((Iterable<?>)policies);
+
+        Map<String, Object> policyConfig;
+        if (policy instanceof String) {
+            policyConfig = ImmutableMap.<String, Object>of("type", policy);
+        } else if (policy instanceof Map) {
+            policyConfig = (Map<String, Object>) policy;
+        } else {
+            throw new IllegalStateException("Policy exepcted to be string or map. Unsupported object type " + policy.getClass().getName() + " (" + policy.toString() + ")");
+        }
+
+        String policyType = (String) checkNotNull(Yamls.getMultinameAttribute(policyConfig, "policy_type", "policyType", "type"), "policy type");
+        Map<String, Object> brooklynConfig = (Map<String, Object>) policyConfig.get("brooklyn.config");
+        BrooklynClassLoadingContext loader = loadedItem.newClassLoadingContext(mgmt);
+        PolicySpec<? extends Policy> spec = PolicySpec.create(loader.loadClass(policyType, Policy.class));
+        if (brooklynConfig != null) {
+            spec.configure(brooklynConfig);
+        }
+        return (SpecT) spec;
+    }
+
     @SuppressWarnings("unchecked")
     @Override
     /** @deprecated since 0.7.0 use {@link #createSpec(CatalogItem)} */
@@ -223,7 +270,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     public <T,SpecT> Class<? extends T> loadClass(CatalogItem<T,SpecT> item) {
         if (log.isDebugEnabled())
             log.debug("Loading class for catalog item " + item);
-        Preconditions.checkNotNull(item);
+        checkNotNull(item);
         CatalogItemDo<?,?> loadedItem = getCatalogItemDo(item.getId());
         if (loadedItem==null) throw new NoSuchElementException("Unable to load '"+item.getId()+"' to instantiate it");
         return (Class<? extends T>) loadedItem.getJavaClass();
@@ -253,7 +300,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
 
     private CatalogItemDtoAbstract<?,?> getAbstractCatalogItem(String yaml) {
         DeploymentPlan plan = makePlanFromYaml(yaml);
-        
+
         CatalogLibrariesDto libraries = null;
 
         @SuppressWarnings("rawtypes")
@@ -287,26 +334,38 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
                 }
             }
         }
-        
-        // TODO long-term:  support applications / templates, policies
-        
-        // build the catalog item from the plan (as CatalogItem<Entity> for now)
-        CatalogEntityItemDto dto = CatalogItems.newEntityFromPlan(registeredTypeName, libraries, plan, yaml);
+
+        // TODO long-term:  support applications / templates
+
+        CatalogItemBuilder<?> builder = createItemBuilder(plan, registeredTypeName)
+            .libraries(libraries)
+            .name(plan.getName())
+            .description(plan.getDescription())
+            .plan(yaml);
 
         // and populate other fields
         Maybe<Object> name = catalog.getMaybe("name");
-        if (name.isPresent()) dto.name = (String)name.get();
-        
+        if (name.isPresent()) builder.name((String)name.get());
+
         Maybe<Object> description = catalog.getMaybe("description");
-        if (description.isPresent()) dto.description = (String)description.get();
-        
+        if (description.isPresent()) builder.description((String)description.get());
+
         Maybe<Object> iconUrl = catalog.getMaybe("iconUrl");
         if (iconUrl.isAbsent()) iconUrl = catalog.getMaybe("icon_url");
-        if (iconUrl.isPresent()) dto.iconUrl = (String)iconUrl.get();
+        if (iconUrl.isPresent()) builder.iconUrl((String)iconUrl.get());
 
         // TODO #3 support version info
-        
-        return dto;
+
+        return builder.build();
+    }
+
+    private CatalogItemBuilder<?> createItemBuilder(DeploymentPlan plan, String registeredTypeName) {
+        boolean isPolicy = plan.getCustomAttributes().containsKey(POLICIES_KEY);
+        if (isPolicy) {
+            return CatalogItemBuilder.newPolicy(registeredTypeName);
+        } else {
+            return CatalogItemBuilder.newEntity(registeredTypeName);
+        }
     }
 
     private DeploymentPlan makePlanFromYaml(String yaml) {
@@ -317,7 +376,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     @Override
     public CatalogItem<?,?> addItem(String yaml) {
         log.debug("Adding manual catalog item to "+mgmt+": "+yaml);
-        Preconditions.checkNotNull(yaml, "yaml");
+        checkNotNull(yaml, "yaml");
         if (manualAdditionsCatalog==null) loadManualAdditionsCatalog();
         CatalogItemDtoAbstract<?,?> itemDto = getAbstractCatalogItem(yaml);
         manualAdditionsCatalog.addEntry(itemDto);
@@ -337,7 +396,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     @Override @Deprecated /** @deprecated see super */
     public void addItem(CatalogItem<?,?> item) {
         log.debug("Adding manual catalog item to "+mgmt+": "+item);
-        Preconditions.checkNotNull(item, "item");
+        checkNotNull(item, "item");
         if (manualAdditionsCatalog==null) loadManualAdditionsCatalog();
         manualAdditionsCatalog.addEntry(getAbstractCatalogItem(item));
     }
@@ -345,7 +404,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     @Override @Deprecated /** @deprecated see super */
     public CatalogItem<?,?> addItem(Class<?> type) {
         log.debug("Adding manual catalog item to "+mgmt+": "+type);
-        Preconditions.checkNotNull(type, "type");
+        checkNotNull(type, "type");
         if (manualAdditionsCatalog==null) loadManualAdditionsCatalog();
         manualAdditionsClasses.addClass(type);
         return manualAdditionsCatalog.classpath.addCatalogEntry(type);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb1c9867/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java
new file mode 100644
index 0000000..d0bbe29
--- /dev/null
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.catalog.internal;
+
+import com.google.common.base.Preconditions;
+
+public class CatalogItemBuilder<CatalogItemType extends CatalogItemDtoAbstract<?, ?>> {
+    private CatalogItemType dto;
+
+    public static CatalogItemBuilder<CatalogEntityItemDto> newEntity(String registeredTypeName) {
+        return new CatalogItemBuilder<CatalogEntityItemDto>(new CatalogEntityItemDto())
+                .registeredTypeName(registeredTypeName);
+    }
+
+    public static CatalogItemBuilder<CatalogTemplateItemDto> newTemplate() {
+        return new CatalogItemBuilder<CatalogTemplateItemDto>(new CatalogTemplateItemDto());
+    }
+
+    public static CatalogItemBuilder<CatalogPolicyItemDto> newPolicy(String registeredTypeName) {
+        return new CatalogItemBuilder<CatalogPolicyItemDto>(new CatalogPolicyItemDto())
+                .registeredTypeName(registeredTypeName);
+    }
+
+    public CatalogItemBuilder(CatalogItemType dto) {
+        this.dto = dto;
+        this.dto.libraries = new CatalogLibrariesDto();
+    }
+
+    public CatalogItemBuilder<CatalogItemType> registeredTypeName(String registeredType) {
+        dto.registeredType = registeredType;
+        return this;
+    }
+
+    public CatalogItemBuilder<CatalogItemType> name(String name) {
+        dto.name = name;
+        return this;
+    }
+
+    public CatalogItemBuilder<CatalogItemType> description(String description) {
+        dto.description = description;
+        return this;
+    }
+
+    public CatalogItemBuilder<CatalogItemType> iconUrl(String iconUrl) {
+        dto.iconUrl = iconUrl;
+        return this;
+    }
+
+    public CatalogItemBuilder<CatalogItemType> libraries(CatalogLibrariesDto libraries) {
+        dto.libraries = libraries;
+        return this;
+    }
+
+    public CatalogItemBuilder<CatalogItemType> plan(String yaml) {
+        dto.planYaml = yaml;
+        return this;
+    }
+
+    public CatalogItemType build() {
+        Preconditions.checkNotNull(dto.registeredType);
+
+        if (dto.libraries == null) {
+            dto.libraries = new CatalogLibrariesDto();
+        }
+
+        CatalogItemType ret = dto;
+
+        //prevent mutations through the builder
+        dto = null;
+
+        return ret;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb1c9867/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimplePolicy.java
----------------------------------------------------------------------
diff --git a/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimplePolicy.java b/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimplePolicy.java
new file mode 100644
index 0000000..10a0094
--- /dev/null
+++ b/core/src/test/dependencies/osgi/entities/src/main/java/brooklyn/osgi/tests/SimplePolicy.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.osgi.tests;
+
+
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.config.ConfigKey;
+import brooklyn.policy.basic.AbstractPolicy;
+import brooklyn.util.flags.SetFromFlag;
+
+public class SimplePolicy extends AbstractPolicy {
+    @SetFromFlag("config1")
+    public static final ConfigKey<String> CONFIG1 = ConfigKeys.newStringConfigKey("config1");
+
+    @SetFromFlag("config2")
+    public static final ConfigKey<String> CONFIG2 = ConfigKeys.newStringConfigKey("config2");
+
+    @SetFromFlag("config3")
+    public static final ConfigKey<String> CONFIG3 = ConfigKeys.newStringConfigKey("config3");
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb1c9867/core/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar
----------------------------------------------------------------------
diff --git a/core/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar b/core/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar
index 4093cd3..42d842c 100644
Binary files a/core/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar and b/core/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar differ

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb1c9867/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
index 867c775..0025cf8 100644
--- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
+++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
@@ -23,8 +23,11 @@ import io.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiator.Insta
 import java.util.List;
 import java.util.Map;
 
+import brooklyn.catalog.BrooklynCatalog;
+import brooklyn.catalog.CatalogItem;
 import brooklyn.entity.proxying.EntityInitializer;
 import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.management.ManagementContext;
 import brooklyn.policy.Enricher;
 import brooklyn.policy.EnricherSpec;
 import brooklyn.policy.Policy;
@@ -98,11 +101,24 @@ public abstract class BrooklynEntityDecorationResolver<DT> {
         }
 
         @Override
+        @SuppressWarnings("unchecked")
         protected void addDecorationFromJsonMap(Map<?, ?> decorationJson, List<PolicySpec<?>> decorations) {
             InstantiatorFromKey decoLoader = instantiator.from(decorationJson).prefix("policy");
-            // this pattern of creating a spec could be simplified with a "Configurable" superinterface on *Spec  
-            decorations.add(PolicySpec.create(decoLoader.getType(Policy.class))
-                .configure( decoLoader.getConfigMap() ));
+
+            String policyType = decoLoader.getTypeName().get();
+            ManagementContext mgmt = instantiator.loader.getManagementContext();
+            BrooklynCatalog catalog = mgmt.getCatalog();
+            CatalogItem<?, ?> item = catalog.getCatalogItem(policyType);
+            PolicySpec<? extends Policy> spec;
+            if (item != null) {
+                spec = (PolicySpec<? extends Policy>) catalog.createSpec(item);
+                spec.configure(decoLoader.getConfigMap());
+            } else {
+                // this pattern of creating a spec could be simplified with a "Configurable" superinterface on *Spec  
+                spec = PolicySpec.create(decoLoader.getType(Policy.class))
+                    .configure( decoLoader.getConfigMap() );
+            }
+            decorations.add(spec);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb1c9867/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/CatalogYamlEntityTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/CatalogYamlEntityTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/CatalogYamlEntityTest.java
new file mode 100644
index 0000000..910edd5
--- /dev/null
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/CatalogYamlEntityTest.java
@@ -0,0 +1,254 @@
+/*
+ * 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 io.brooklyn.camp.brooklyn;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.util.Collection;
+
+import org.testng.annotations.Test;
+
+import brooklyn.catalog.CatalogItem;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.BasicEntity;
+import brooklyn.management.osgi.OsgiStandaloneTest;
+
+import com.google.common.collect.Iterables;
+
+
+public class CatalogYamlEntityTest extends AbstractYamlTest {
+    private static final String SIMPLE_ENTITY_TYPE = "brooklyn.osgi.tests.SimpleEntity";
+
+    @Test
+    public void testAddCatalogItem() throws Exception {
+        String registeredTypeName = "my.catalog.app.id.load";
+        addCatalogOSGiEntity(registeredTypeName);
+
+        CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(registeredTypeName);
+        assertEquals(item.getRegisteredTypeName(), registeredTypeName);
+
+        deleteCatalogEntity(registeredTypeName);
+    }
+
+    @Test
+    public void testLaunchApplicationReferencingCatalog() throws Exception {
+        String registeredTypeName = "my.catalog.app.id.launch";
+        registerAndLaunchAndAssertSimpleEntity(registeredTypeName, SIMPLE_ENTITY_TYPE);
+    }
+
+    @Test
+    public void testLaunchApplicationWithCatalogReferencingOtherCatalog() throws Exception {
+        String referencedRegisteredTypeName = "my.catalog.app.id.referenced";
+        String referrerRegisteredTypeName = "my.catalog.app.id.referring";
+        addCatalogOSGiEntity(referencedRegisteredTypeName, SIMPLE_ENTITY_TYPE);
+        addCatalogOSGiEntity(referrerRegisteredTypeName, referencedRegisteredTypeName);
+
+        String yaml = "name: simple-app-yaml\n" +
+                      "location: localhost\n" +
+                      "services: \n" +
+                      "  - serviceType: "+referrerRegisteredTypeName;
+        Entity app = createAndStartApplication(yaml);
+
+        Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
+        assertEquals(simpleEntity.getEntityType().getName(), SIMPLE_ENTITY_TYPE);
+
+        deleteCatalogEntity(referencedRegisteredTypeName);
+        deleteCatalogEntity(referrerRegisteredTypeName);
+    }
+
+    @Test
+    public void testLaunchApplicationChildWithCatalogReferencingOtherCatalog() throws Exception {
+        String referencedRegisteredTypeName = "my.catalog.app.id.child.referenced";
+        String referrerRegisteredTypeName = "my.catalog.app.id.child.referring";
+        addCatalogOSGiEntity(referencedRegisteredTypeName, SIMPLE_ENTITY_TYPE);
+        addCatalogChildOSGiEntity(referrerRegisteredTypeName, referencedRegisteredTypeName);
+
+        Entity app = createAndStartApplication(
+            "name: simple-app-yaml",
+            "location: localhost",
+            "services:",
+            "- serviceType: "+BasicEntity.class.getName(),
+            "  brooklyn.children:",
+            "  - type: " + referrerRegisteredTypeName);
+
+        Collection<Entity> children = app.getChildren();
+        assertEquals(children.size(), 1);
+        Entity child = Iterables.getOnlyElement(children);
+        assertEquals(child.getEntityType().getName(), BasicEntity.class.getName());
+        Collection<Entity> grandChildren = child.getChildren();
+        assertEquals(grandChildren.size(), 1);
+        Entity grandChild = Iterables.getOnlyElement(grandChildren);
+        assertEquals(grandChild.getEntityType().getName(), BasicEntity.class.getName());
+        Collection<Entity> grandGrandChildren = grandChild.getChildren();
+        assertEquals(grandGrandChildren.size(), 1);
+        Entity grandGrandChild = Iterables.getOnlyElement(grandGrandChildren);
+        assertEquals(grandGrandChild.getEntityType().getName(), SIMPLE_ENTITY_TYPE);
+
+        deleteCatalogEntity(referencedRegisteredTypeName);
+        deleteCatalogEntity(referrerRegisteredTypeName);
+    }
+
+    @Test
+    public void testLaunchApplicationWithTypeUsingJavaColonPrefix() throws Exception {
+        String registeredTypeName = SIMPLE_ENTITY_TYPE;
+        String serviceName = "java:"+SIMPLE_ENTITY_TYPE;
+        registerAndLaunchAndAssertSimpleEntity(registeredTypeName, serviceName);
+    }
+
+    @Test
+    public void testLaunchApplicationLoopWithJavaTypeName() throws Exception {
+        String registeredTypeName = SIMPLE_ENTITY_TYPE;
+        String serviceName = SIMPLE_ENTITY_TYPE;
+        registerAndLaunchAndAssertSimpleEntity(registeredTypeName, serviceName);
+    }
+
+    @Test
+    public void testLaunchApplicationLoopCatalogIdFails() throws Exception {
+        String registeredTypeName = "self.referencing.type";
+        registerAndLaunchFailsWithRecursionError(registeredTypeName, registeredTypeName);
+    }
+    
+    @Test
+    public void testLaunchApplicationChildLoopCatalogIdFails() throws Exception {
+        String referrerRegisteredTypeName = "my.catalog.app.id.child.referring";
+        addCatalogChildOSGiEntity(referrerRegisteredTypeName, referrerRegisteredTypeName);
+
+        try {
+            createAndStartApplication(
+                "name: simple-app-yaml",
+                "location: localhost",
+                "services:",
+                "- serviceType: "+BasicEntity.class.getName(),
+                "  brooklyn.children:",
+                "  - type: " + referrerRegisteredTypeName);
+
+                fail("Expected to throw IllegalStateException");
+        } catch (IllegalStateException e) {
+            assertTrue(e.getMessage().contains("Recursive reference to "+referrerRegisteredTypeName));
+        }
+
+        deleteCatalogEntity(referrerRegisteredTypeName);
+    }
+
+    /**
+     * Tests that a catalog item referenced by another
+     * catalog item won't have access to the parent's bundles.
+     */
+    @Test
+    public void testParentCatalogDoesNotLeakBundlesToChildCatalogItems() throws Exception {
+        String childCatalogId = "my.catalog.app.id.no_bundles";
+        String parentCatalogId = "my.catalog.app.id.parent";
+        addCatalogItem(
+                "brooklyn.catalog:",
+                "  id: " + childCatalogId,
+                "",
+                "services:",
+                "- type: " + SIMPLE_ENTITY_TYPE);
+
+        addCatalogItem(
+                "brooklyn.catalog:",
+                "  id: " + parentCatalogId,
+                "  libraries:",
+                "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
+                "",
+                "services:",
+                "- type: " + childCatalogId);
+
+        try {
+            createAndStartApplication(
+                    "services:",
+                    "- type: " + parentCatalogId);
+        } catch (UnsupportedOperationException e) {
+            assertTrue(e.getMessage().endsWith("cannot be matched"));
+            assertTrue(e.getMessage().contains(SIMPLE_ENTITY_TYPE));
+        }
+
+        deleteCatalogEntity(parentCatalogId);
+        deleteCatalogEntity(childCatalogId);
+    }
+
+    private void registerAndLaunchAndAssertSimpleEntity(String registeredTypeName, String serviceType) throws Exception {
+        addCatalogOSGiEntity(registeredTypeName, serviceType);
+        String yaml = "name: simple-app-yaml\n" +
+                      "location: localhost\n" +
+                      "services: \n" +
+                      "  - serviceType: "+registeredTypeName;
+        Entity app = createAndStartApplication(yaml);
+
+        Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
+        assertEquals(simpleEntity.getEntityType().getName(), SIMPLE_ENTITY_TYPE);
+
+        deleteCatalogEntity(registeredTypeName);
+    }
+
+    private void registerAndLaunchFailsWithRecursionError(String registeredTypeName, String serviceType) throws Exception {
+        addCatalogOSGiEntity(registeredTypeName, serviceType);
+        String yaml = "name: simple-app-yaml\n" +
+                      "location: localhost\n" +
+                      "services: \n" +
+                      "  - serviceType: "+registeredTypeName;
+        try {
+            createAndStartApplication(yaml);
+            fail("Expected to throw IllegalStateException");
+        } catch (IllegalStateException e) {
+            assertTrue(e.getMessage().contains("Recursive reference to "+registeredTypeName));
+        }
+
+        deleteCatalogEntity(registeredTypeName);
+    }
+
+    private void addCatalogOSGiEntity(String registeredTypeName) {
+        addCatalogOSGiEntity(registeredTypeName, SIMPLE_ENTITY_TYPE);
+    }
+
+    private void addCatalogOSGiEntity(String registeredTypeName, String serviceType) {
+        addCatalogItem(
+            "brooklyn.catalog:",
+            "  id: " + registeredTypeName,
+            "  name: My Catalog App",
+            "  description: My description",
+            "  icon_url: classpath://path/to/myicon.jpg",
+            "  version: 0.1.2",
+            "  libraries:",
+            "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
+            "",
+            "services:",
+            "- type: " + serviceType);
+    }
+
+    private void addCatalogChildOSGiEntity(String registeredTypeName, String serviceType) {
+        addCatalogItem(
+            "brooklyn.catalog:",
+            "  id: " + registeredTypeName,
+            "  name: My Catalog App",
+            "  description: My description",
+            "  icon_url: classpath://path/to/myicon.jpg",
+            "  version: 0.1.2",
+            "  libraries:",
+            "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
+            "",
+            "services:",
+            "- type: " + BasicEntity.class.getName(),
+            "  brooklyn.children:",
+            "  - type: " + serviceType);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb1c9867/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/CatalogYamlPolicyTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/CatalogYamlPolicyTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/CatalogYamlPolicyTest.java
new file mode 100644
index 0000000..e7e44d6
--- /dev/null
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/CatalogYamlPolicyTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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 io.brooklyn.camp.brooklyn;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import org.testng.annotations.Test;
+
+import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.CatalogPredicates;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.BasicEntity;
+import brooklyn.event.basic.BasicConfigKey;
+import brooklyn.management.osgi.OsgiStandaloneTest;
+import brooklyn.policy.Policy;
+
+import com.google.common.collect.Iterables;
+
+public class CatalogYamlPolicyTest extends AbstractYamlTest {
+    private static final String SIMPLE_POLICY_TYPE = "brooklyn.osgi.tests.SimplePolicy";
+    private static final String SIMPLE_ENTITY_TYPE = "brooklyn.osgi.tests.SimpleEntity";
+
+    @Test
+    public void testAddCatalogItem() throws Exception {
+        assertEquals(countCatalogPolicies(), 0);
+
+        String registeredTypeName = "my.catalog.policy.id.load";
+        addCatalogOSGiPolicy(registeredTypeName, SIMPLE_POLICY_TYPE);
+
+        CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(registeredTypeName);
+        assertEquals(item.getRegisteredTypeName(), registeredTypeName);
+        assertEquals(countCatalogPolicies(), 1);
+
+        deleteCatalogEntity(registeredTypeName);
+    }
+
+    @Test
+    public void testLaunchApplicationReferencingPolicy() throws Exception {
+        String registeredTypeName = "my.catalog.policy.id.launch";
+        addCatalogOSGiPolicy(registeredTypeName, SIMPLE_POLICY_TYPE);
+        Entity app = createAndStartApplication(
+            "name: simple-app-yaml",
+            "location: localhost",
+            "services: ",
+            "  - type: brooklyn.entity.basic.BasicEntity\n" +
+            "    brooklyn.policies:\n" +
+            "    - type: " + registeredTypeName,
+            "      brooklyn.config:",
+            "        config2: config2 override",
+            "        config3: config3");
+
+        Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
+        Policy policy = Iterables.getOnlyElement(simpleEntity.getPolicies());
+        assertEquals(policy.getPolicyType().getName(), SIMPLE_POLICY_TYPE);
+        assertEquals(policy.getConfig(new BasicConfigKey<String>(String.class, "config1")), "config1");
+        assertEquals(policy.getConfig(new BasicConfigKey<String>(String.class, "config2")), "config2 override");
+        assertEquals(policy.getConfig(new BasicConfigKey<String>(String.class, "config3")), "config3");
+
+        deleteCatalogEntity(registeredTypeName);
+    }
+
+    @Test
+    public void testLaunchApplicationWithCatalogReferencingOtherCatalog() throws Exception {
+        String referencedRegisteredTypeName = "my.catalog.policy.id.referenced";
+        String referrerRegisteredTypeName = "my.catalog.policy.id.referring";
+        addCatalogOSGiPolicy(referencedRegisteredTypeName, SIMPLE_POLICY_TYPE);
+
+        addCatalogItem(
+            "brooklyn.catalog:",
+            "  id: " + referrerRegisteredTypeName,
+            "  name: My Catalog App",
+            "  description: My description",
+            "  icon_url: classpath://path/to/myicon.jpg",
+            "  version: 0.1.2",
+            "  libraries:",
+            "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
+            "",
+            "services:",
+            "- type: " + SIMPLE_ENTITY_TYPE,
+            "  brooklyn.policies:",
+            "  - type: " + referencedRegisteredTypeName);
+
+        String yaml = "name: simple-app-yaml\n" +
+                      "location: localhost\n" +
+                      "services: \n" +
+                      "  - serviceType: "+referrerRegisteredTypeName;
+
+        Entity app = createAndStartApplication(yaml);
+
+        Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
+        Policy policy = Iterables.getOnlyElement(simpleEntity.getPolicies());
+        assertEquals(policy.getPolicyType().getName(), SIMPLE_POLICY_TYPE);
+
+        deleteCatalogEntity(referencedRegisteredTypeName);
+    }
+
+    @Test
+    public void testParentCatalogDoesNotLeakBundlesToChildCatalogItems() throws Exception {
+        String childCatalogId = "my.catalog.policy.id.no_bundles";
+        String parentCatalogId = "my.catalog.policy.id.parent";
+        addCatalogItem(
+            "brooklyn.catalog:",
+            "  id: " + childCatalogId,
+            "",
+            "services:",
+            "- type: " + BasicEntity.class.getName(),
+            "  brooklyn.policies:",
+            "  - type: " + SIMPLE_POLICY_TYPE);
+
+        addCatalogItem(
+            "brooklyn.catalog:",
+            "  id: " + parentCatalogId,
+            "  libraries:",
+            "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
+            "",
+            "services:",
+            "- type: " + childCatalogId);
+
+        try {
+            createAndStartApplication(
+                    "services:",
+                    "- type: " + parentCatalogId);
+        } catch (IllegalStateException e) {
+            assertTrue(e.getMessage().startsWith("Unable to load " + SIMPLE_POLICY_TYPE));
+        }
+
+        deleteCatalogEntity(parentCatalogId);
+        deleteCatalogEntity(childCatalogId);
+    }
+
+    private void addCatalogOSGiPolicy(String registeredTypeName, String serviceType) {
+        addCatalogItem(
+            "brooklyn.catalog:",
+            "  id: " + registeredTypeName,
+            "  name: My Catalog Policy",
+            "  description: My description",
+            "  icon_url: classpath://path/to/myicon.jpg",
+            "  version: 0.1.2",
+            "  libraries:",
+            "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
+            "",
+            "brooklyn.policies:",
+            "- type: " + serviceType,
+            "  brooklyn.config:",
+            "    config1: config1",
+            "    config2: config2");
+    }
+
+    private int countCatalogPolicies() {
+        return Iterables.size(mgmt().getCatalog().getCatalogItems(CatalogPredicates.IS_POLICY));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb1c9867/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/CatalogYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/CatalogYamlTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/CatalogYamlTest.java
deleted file mode 100644
index 78f7c0d..0000000
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/CatalogYamlTest.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * 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 io.brooklyn.camp.brooklyn;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
-
-import java.util.Collection;
-
-import org.testng.annotations.Test;
-
-import brooklyn.catalog.CatalogItem;
-import brooklyn.entity.Entity;
-import brooklyn.entity.basic.BasicEntity;
-import brooklyn.management.osgi.OsgiStandaloneTest;
-
-import com.google.common.collect.Iterables;
-
-
-public class CatalogYamlTest extends AbstractYamlTest {
-    private static final String SIMPLE_ENTITY_TYPE = "brooklyn.osgi.tests.SimpleEntity";
-
-    @Test
-    public void testAddCatalogItem() throws Exception {
-        String registeredTypeName = "my.catalog.app.id.load";
-        addCatalogOSGiEntity(registeredTypeName);
-        
-        CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(registeredTypeName);
-        assertEquals(item.getRegisteredTypeName(), registeredTypeName);
-    }
-
-    @Test
-    public void testLaunchApplicationReferencingCatalog() throws Exception {
-        String registeredTypeName = "my.catalog.app.id.launch";
-        registerAndLaunchAndAssertSimpleEntity(registeredTypeName, SIMPLE_ENTITY_TYPE);
-    }
-
-    @Test
-    public void testLaunchApplicationWithCatalogReferencingOtherCatalog() throws Exception {
-        String referencedRegisteredTypeName = "my.catalog.app.id.referenced";
-        String referrerRegisteredTypeName = "my.catalog.app.id.referring";
-        addCatalogOSGiEntity(referencedRegisteredTypeName, SIMPLE_ENTITY_TYPE);
-        addCatalogOSGiEntity(referrerRegisteredTypeName, referencedRegisteredTypeName);
-
-        String yaml = "name: simple-app-yaml\n" +
-                      "location: localhost\n" +
-                      "services: \n" +
-                      "  - serviceType: "+referrerRegisteredTypeName;
-        Entity app = createAndStartApplication(yaml);
-        
-        Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
-        assertEquals(simpleEntity.getEntityType().getName(), SIMPLE_ENTITY_TYPE);
-    }
-
-    @Test
-    public void testLaunchApplicationChildWithCatalogReferencingOtherCatalog() throws Exception {
-        String referencedRegisteredTypeName = "my.catalog.app.id.child.referenced";
-        String referrerRegisteredTypeName = "my.catalog.app.id.child.referring";
-        addCatalogOSGiEntity(referencedRegisteredTypeName, SIMPLE_ENTITY_TYPE);
-        addCatalogChildOSGiEntity(referrerRegisteredTypeName, referencedRegisteredTypeName);
-
-        Entity app = createAndStartApplication(
-            "name: simple-app-yaml",
-            "location: localhost",
-            "services:",
-            "- serviceType: "+BasicEntity.class.getName(),
-            "  brooklyn.children:",
-            "  - type: " + referrerRegisteredTypeName);
-        
-        Collection<Entity> children = app.getChildren();
-        assertEquals(children.size(), 1);
-        Entity child = Iterables.getOnlyElement(children);
-        assertEquals(child.getEntityType().getName(), BasicEntity.class.getName());
-        Collection<Entity> grandChildren = child.getChildren();
-        assertEquals(grandChildren.size(), 1);
-        Entity grandChild = Iterables.getOnlyElement(grandChildren);
-        assertEquals(grandChild.getEntityType().getName(), BasicEntity.class.getName());
-        Collection<Entity> grandGrandChildren = grandChild.getChildren();
-        assertEquals(grandGrandChildren.size(), 1);
-        Entity grandGrandChild = Iterables.getOnlyElement(grandGrandChildren);
-        assertEquals(grandGrandChild.getEntityType().getName(), SIMPLE_ENTITY_TYPE);
-    }
-
-    @Test
-    public void testLaunchApplicationWithTypeUsingJavaColonPrefix() throws Exception {
-        String registeredTypeName = SIMPLE_ENTITY_TYPE;
-        String serviceName = "java:"+SIMPLE_ENTITY_TYPE;
-        registerAndLaunchAndAssertSimpleEntity(registeredTypeName, serviceName);
-    }
-
-    @Test
-    public void testLaunchApplicationLoopWithJavaTypeName() throws Exception {
-        String registeredTypeName = SIMPLE_ENTITY_TYPE;
-        String serviceName = SIMPLE_ENTITY_TYPE;
-        registerAndLaunchAndAssertSimpleEntity(registeredTypeName, serviceName);
-    }
-
-    @Test
-    public void testLaunchApplicationLoopCatalogIdFails() throws Exception {
-        String registeredTypeName = "self.referencing.type";
-        registerAndLaunchFailsWithRecursionError(registeredTypeName, registeredTypeName);
-    }
-    
-    @Test
-    public void testLaunchApplicationChildLoopCatalogIdFails() throws Exception {
-        String referrerRegisteredTypeName = "my.catalog.app.id.child.referring";
-        addCatalogChildOSGiEntity(referrerRegisteredTypeName, referrerRegisteredTypeName);
-
-        try {
-            createAndStartApplication(
-                "name: simple-app-yaml",
-                "location: localhost",
-                "services:",
-                "- serviceType: "+BasicEntity.class.getName(),
-                "  brooklyn.children:",
-                "  - type: " + referrerRegisteredTypeName);
-            
-                fail("Expected to throw IllegalStateException");
-        } catch (IllegalStateException e) {
-            assertTrue(e.getMessage().contains("Recursive reference to "+referrerRegisteredTypeName));
-        } finally {
-            deleteCatalogEntity(referrerRegisteredTypeName);
-        }
-    }
-
-    /**
-     * Tests that a catalog item referenced by another
-     * catalog item won't have access to the parent's bundles.
-     */
-    @Test
-    public void testParentCatalogDoesNotLeakBundlesToChildCatalogItems() throws Exception {
-        String childCatalogId = "my.catalog.app.id.no_bundles";
-        String parentCatalogId = "my.catalog.app.id.parent";
-        addCatalogItem(
-                "brooklyn.catalog:",
-                "  id: " + childCatalogId,
-                "",
-                "services:",
-                "- type: " + SIMPLE_ENTITY_TYPE);
-        
-        addCatalogItem(
-                "brooklyn.catalog:",
-                "  id: " + parentCatalogId,
-                "  libraries:",
-                "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
-                "",
-                "services:",
-                "- type: " + childCatalogId);
-        
-        try {
-            createAndStartApplication(
-                    "services:",
-                    "- type: " + parentCatalogId);
-        } catch (UnsupportedOperationException e) {
-            assertTrue(e.getMessage().endsWith("cannot be matched"));
-            assertTrue(e.getMessage().contains(SIMPLE_ENTITY_TYPE));
-        }
-
-        deleteCatalogEntity(parentCatalogId);
-        deleteCatalogEntity(childCatalogId);
-    }
-
-    private void registerAndLaunchAndAssertSimpleEntity(String registeredTypeName, String serviceType) throws Exception {
-        addCatalogOSGiEntity(registeredTypeName, serviceType);
-        try {
-            String yaml = "name: simple-app-yaml\n" +
-                          "location: localhost\n" +
-                          "services: \n" +
-                          "  - serviceType: "+registeredTypeName;
-            Entity app = createAndStartApplication(yaml);
-    
-            Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
-            assertEquals(simpleEntity.getEntityType().getName(), SIMPLE_ENTITY_TYPE);
-        } finally {
-            deleteCatalogEntity(registeredTypeName);
-        }
-    }
-
-    private void registerAndLaunchFailsWithRecursionError(String registeredTypeName, String serviceType) throws Exception {
-        addCatalogOSGiEntity(registeredTypeName, serviceType);
-        try {
-            String yaml = "name: simple-app-yaml\n" +
-                          "location: localhost\n" +
-                          "services: \n" +
-                          "  - serviceType: "+registeredTypeName;
-            try {
-                createAndStartApplication(yaml);
-                fail("Expected to throw IllegalStateException");
-            } catch (IllegalStateException e) {
-                assertTrue(e.getMessage().contains("Recursive reference to "+registeredTypeName));
-            }
-        } finally {
-            deleteCatalogEntity(registeredTypeName);
-        }
-    }
-
-    private void addCatalogOSGiEntity(String registeredTypeName) {
-        addCatalogOSGiEntity(registeredTypeName, SIMPLE_ENTITY_TYPE);
-    }
-    
-    private void addCatalogOSGiEntity(String registeredTypeName, String serviceType) {
-        addCatalogItem(
-            "brooklyn.catalog:",
-            "  id: " + registeredTypeName,
-            "  name: My Catalog App",
-            "  description: My description",
-            "  icon_url: classpath://path/to/myicon.jpg",
-            "  version: 0.1.2",
-            "  libraries:",
-            "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
-            "",
-            "services:",
-            "- type: " + serviceType);
-    }
-
-    private void addCatalogChildOSGiEntity(String registeredTypeName, String serviceType) {
-        addCatalogItem(
-            "brooklyn.catalog:",
-            "  id: " + registeredTypeName,
-            "  name: My Catalog App",
-            "  description: My description",
-            "  icon_url: classpath://path/to/myicon.jpg",
-            "  version: 0.1.2",
-            "  libraries:",
-            "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
-            "",
-            "services:",
-            "- type: " + BasicEntity.class.getName(),
-            "  brooklyn.children:",
-            "  - type: " + serviceType);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb1c9867/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java
index 6505d8a..98453d4 100644
--- a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java
+++ b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java
@@ -32,12 +32,13 @@ public class CatalogPolicySummary extends CatalogItemSummary {
             @JsonProperty("id") String id,
             @JsonProperty("name") String name,
             @JsonProperty("type") String type,
+            @JsonProperty("planYaml") String planYaml,
             @JsonProperty("description") String description,
             @JsonProperty("iconUrl") String iconUrl,
             @JsonProperty("config") Set<PolicyConfigSummary> config,
             @JsonProperty("links") Map<String, URI> links
         ) {
-        super(id, name, type, type, type, null, description, iconUrl, links);
+        super(id, name, type, type, type, planYaml, description, iconUrl, links);
         // TODO expose config from policies
         this.config = config;
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb1c9867/usage/rest-server/src/main/java/brooklyn/rest/transform/CatalogTransformer.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/transform/CatalogTransformer.java b/usage/rest-server/src/main/java/brooklyn/rest/transform/CatalogTransformer.java
index 33dc4d9..9f232d9 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/transform/CatalogTransformer.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/transform/CatalogTransformer.java
@@ -89,7 +89,8 @@ public class CatalogTransformer {
 
     public static CatalogPolicySummary catalogPolicySummary(BrooklynRestResourceUtils b, CatalogItem<? extends Policy,PolicySpec<?>> item) {
         Set<PolicyConfigSummary> config = ImmutableSet.of();
-        return new CatalogPolicySummary(item.getId(), item.getName(), item.getJavaType(),
+        return new CatalogPolicySummary(item.getId(), item.getName(), item.getRegisteredTypeName(),
+                item.getPlanYaml(),
                 item.getDescription(), tidyIconLink(b, item, item.getIconUrl()), config,
                 makeLinks(item));
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb1c9867/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java b/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java
index a22432f..cad4395 100644
--- a/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java
+++ b/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java
@@ -106,7 +106,22 @@ public class Yamls {
                 if (result==null) result = candidate;
                 else if (!result.equals(candidate)) {
                     log.warn("Different values for attributes "+Arrays.toString(equivalentNames)+"; " +
-                    		"preferring '"+result+"' to '"+candidate+"'");
+                            "preferring '"+result+"' to '"+candidate+"'");
+                }
+            }
+        }
+        return result;
+    }
+
+    public static Object getMultinameAttribute(Map<String,Object> obj, String ...equivalentNames) {
+        Object result = null;
+        for (String name: equivalentNames) {
+            Object candidate = obj.get(name);
+            if (candidate!=null) {
+                if (result==null) result = candidate;
+                else if (!result.equals(candidate)) {
+                    log.warn("Different values for attributes "+Arrays.toString(equivalentNames)+"; " +
+                            "preferring '"+result+"' to '"+candidate+"'");
                 }
             }
         }