You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ha...@apache.org on 2015/08/05 22:56:20 UTC

[16/20] incubator-brooklyn git commit: Package rename to org.apache.brooklyn: usage/camp/

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e406d1ad/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
new file mode 100644
index 0000000..c413e4d
--- /dev/null
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
@@ -0,0 +1,490 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.camp.brooklyn.spi.creation;
+
+import io.brooklyn.camp.spi.AbstractResource;
+import io.brooklyn.camp.spi.ApplicationComponentTemplate;
+import io.brooklyn.camp.spi.AssemblyTemplate;
+import io.brooklyn.camp.spi.PlatformComponentTemplate;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
+import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys;
+import org.apache.brooklyn.camp.brooklyn.spi.creation.service.BrooklynServiceTypeResolver;
+import org.apache.brooklyn.camp.brooklyn.spi.creation.service.ServiceTypeResolver;
+import org.apache.brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.internal.CatalogUtils;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.Application;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.AbstractEntity;
+import brooklyn.entity.basic.BrooklynTags;
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.proxying.InternalEntityFactory;
+import brooklyn.location.Location;
+import brooklyn.management.ManagementContext;
+import brooklyn.management.ManagementContextInjectable;
+import brooklyn.management.classloading.BrooklynClassLoadingContext;
+import brooklyn.management.classloading.JavaBrooklynClassLoadingContext;
+import brooklyn.management.internal.ManagementContextInternal;
+import brooklyn.util.ResourceUtils;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableSet;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.flags.FlagUtils;
+import brooklyn.util.flags.FlagUtils.FlagConfigKeyAndValueRecord;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.javalang.Reflections;
+import brooklyn.util.task.Tasks;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Function;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+
+/**
+ * This generates instances of a template resolver that use a {@link ServiceTypeResolver}
+ * to parse the {@code serviceType} line in the template.
+ */
+public class BrooklynComponentTemplateResolver {
+
+    private static final Logger log = LoggerFactory.getLogger(BrooklynComponentTemplateResolver.class);
+
+    private final BrooklynClassLoadingContext loader;
+    private final ManagementContext mgmt;
+    private final ConfigBag attrs;
+    private final Maybe<AbstractResource> template;
+    private final BrooklynYamlTypeInstantiator.Factory yamlLoader;
+    private final String type;
+    private final ServiceTypeResolver typeResolver;
+    private final AtomicBoolean alreadyBuilt = new AtomicBoolean(false);
+
+    public BrooklynComponentTemplateResolver(BrooklynClassLoadingContext loader, ConfigBag attrs, AbstractResource optionalTemplate, String type, ServiceTypeResolver typeResolver) {
+        this.loader = loader;
+        this.mgmt = loader.getManagementContext();
+        this.attrs = ConfigBag.newInstanceCopying(attrs);
+        this.template = Maybe.fromNullable(optionalTemplate);
+        this.yamlLoader = new BrooklynYamlTypeInstantiator.Factory(loader, this);
+        this.type = type;
+        this.typeResolver = typeResolver;
+    }
+
+    public BrooklynClassLoadingContext getLoader() { return loader; }
+    public ManagementContext getManagementContext() { return mgmt; }
+    public ConfigBag getAttrs() { return attrs; }
+    public Maybe<AbstractResource> getTemplate() { return template; }
+    public BrooklynYamlTypeInstantiator.Factory getYamlLoader() { return yamlLoader; }
+    public ServiceTypeResolver getServiceTypeResolver() { return typeResolver; }
+    public String getDeclaredType() { return type; }
+    public Boolean isAlreadyBuilt() { return alreadyBuilt.get(); }
+
+    public static class Factory {
+
+        /** returns resolver type based on the service type, inspecting the arguments in order to determine the service type */
+        private static ServiceTypeResolver computeResolverType(BrooklynClassLoadingContext context, String knownServiceType, AbstractResource optionalTemplate, ConfigBag attrs) {
+            String type = getDeclaredType(knownServiceType, optionalTemplate, attrs);
+            return findService(context, type);
+        }
+
+        // TODO This could be extended to support multiple prefixes per resolver and a 'best-match' algorithm
+        protected static ServiceTypeResolver findService(BrooklynClassLoadingContext context, String type) {
+            if (type.indexOf(':') != -1) {
+                String prefix = Splitter.on(":").splitToList(type).get(0);
+                ServiceLoader<ServiceTypeResolver> loader = ServiceLoader.load(ServiceTypeResolver.class,
+                        context.getManagementContext().getCatalogClassLoader());
+                for (ServiceTypeResolver resolver : loader) {
+                   if (prefix.equals(resolver.getTypePrefix())) {
+                       return resolver;
+                   }
+                }
+            }
+            return null;
+        }
+
+        public static BrooklynComponentTemplateResolver newInstance(BrooklynClassLoadingContext context, Map<String, ?> childAttrs) {
+            return newInstance(context, ConfigBag.newInstance(childAttrs), null);
+        }
+
+        public static BrooklynComponentTemplateResolver newInstance(BrooklynClassLoadingContext context, AbstractResource template) {
+            return newInstance(context, ConfigBag.newInstance(template.getCustomAttributes()), template);
+        }
+
+        public static BrooklynComponentTemplateResolver newInstance(BrooklynClassLoadingContext context, String serviceType) {
+            return newInstance(context, ConfigBag.newInstance().configureStringKey("serviceType", serviceType), null);
+        }
+
+        private static BrooklynComponentTemplateResolver newInstance(BrooklynClassLoadingContext context, ConfigBag attrs, AbstractResource optionalTemplate) {
+            ServiceTypeResolver typeResolver = computeResolverType(context, null, optionalTemplate, attrs);
+            String type = getDeclaredType(null, optionalTemplate, attrs);
+            if (typeResolver == null) // use default
+                typeResolver = new BrooklynServiceTypeResolver();
+            return new BrooklynComponentTemplateResolver(context, attrs, optionalTemplate, type, typeResolver);
+        }
+
+        public static String getDeclaredType(String knownServiceType, AbstractResource optionalTemplate, @Nullable ConfigBag attrs) {
+            String type = knownServiceType;
+            if (type==null && optionalTemplate!=null) {
+                type = optionalTemplate.getType();
+                if (type.equals(AssemblyTemplate.CAMP_TYPE) || type.equals(PlatformComponentTemplate.CAMP_TYPE) || type.equals(ApplicationComponentTemplate.CAMP_TYPE))
+                    // ignore these values for the type; only subclasses are interesting
+                    type = null;
+            }
+            if (type==null) type = extractServiceTypeAttribute(attrs);
+            return type;
+        }
+
+        private static String extractServiceTypeAttribute(@Nullable ConfigBag attrs) {
+            return BrooklynYamlTypeInstantiator.InstantiatorFromKey.extractTypeName("service", attrs).orNull();
+        }
+
+        public static boolean supportsType(BrooklynClassLoadingContext context, String serviceType) {
+            ServiceTypeResolver typeResolver = computeResolverType(context, serviceType, null, null);
+            if (typeResolver != null) return true;
+            return newInstance(context, serviceType).canResolve();
+        }
+    }
+
+    protected boolean canResolve() {
+        if (typeResolver.getCatalogItem(this, type)!=null)
+            return true;
+        if (loader.tryLoadClass(getJavaType(), Entity.class).isPresent())
+            return true;
+        return false;
+    }
+
+    /** returns the entity class, if needed in contexts which scan its statics for example */
+    protected Class<? extends Entity> loadEntityClass() {
+        Maybe<Class<? extends Entity>> result = tryLoadEntityClass();
+        if (result.isAbsent())
+            throw new IllegalStateException("Could not find "+typeResolver.getBrooklynType(type), ((Maybe.Absent<?>)result).getException());
+        return result.get();
+    }
+
+    /** tries to load the Java entity class */
+    protected Maybe<Class<? extends Entity>> tryLoadEntityClass() {
+        return loader.tryLoadClass(getJavaType(), Entity.class);
+    }
+
+    // TODO Generalise to have other prefixes (e.g. explicit "catalog:" etc)?
+    protected boolean isJavaTypePrefix() {
+        return type != null && (type.toLowerCase().startsWith("java:") || type.toLowerCase().startsWith("brooklyn:java:"));
+    }
+
+    protected String getJavaType() {
+        CatalogItem<Entity, EntitySpec<?>> item = typeResolver.getCatalogItem(this, type);
+        if (!isJavaTypePrefix() && item != null && item.getJavaType() != null) {
+            return item.getJavaType();
+        } else {
+            return typeResolver.getBrooklynType(type);
+        }
+    }
+
+    /** resolves the spec, updating the loader if a catalog item is loaded */
+    protected <T extends Entity> EntitySpec<T> resolveSpec(Set<String> encounteredCatalogTypes) {
+        if (alreadyBuilt.getAndSet(true))
+            throw new IllegalStateException("Spec can only be used once: "+this);
+
+        EntitySpec<T> spec = createSpec(encounteredCatalogTypes);
+        populateSpec(spec);
+
+        return spec;
+    }
+
+    @SuppressWarnings({ "unchecked" })
+    protected <T extends Entity> EntitySpec<T> createSpec(Set<String> encounteredCatalogTypes) {
+        CatalogItem<Entity, EntitySpec<?>> item = getServiceTypeResolver().getCatalogItem(this, getDeclaredType());
+        if (encounteredCatalogTypes==null) encounteredCatalogTypes = MutableSet.of();
+        
+        //Take the symoblicName part of the catalog item only for recursion detection to prevent
+        //cross referencing of different versions. Not interested in non-catalog item types.
+        //Prevent catalog items self-referencing even if explicitly different version.
+        boolean firstOccurrence = (item == null || encounteredCatalogTypes.add(item.getSymbolicName()));
+        boolean recursiveButTryJava = !firstOccurrence;
+
+        // Load a java class from current loader if explicit java prefix, or if no item, or if item is legacy / 
+        // old-style catalog item (item != null && item.getJavaType() != null).
+        // Old-style catalog items (can be defined in catalog.xml only) don't have structure, only a single type, so
+        // they are loaded as a simple java type, only taking the class name from the catalog item instead of the
+        // type value in the YAML. Classpath entries in the item are also used (through the catalog root classloader).
+        if (isJavaTypePrefix() || item == null || item.getJavaType() != null) {
+            return createSpecFromJavaType();
+
+        // Same as above case, but this time force java type loading (either as plain class or through an old-style
+        // catalog item, since we have already loaded a class item with the same name as the type value.
+        } else if (recursiveButTryJava) {
+            if (tryLoadEntityClass().isAbsent()) {
+                throw new IllegalStateException("Recursive reference to " + item + " (and cannot be resolved as a Java type)");
+            }
+            return createSpecFromJavaType();
+
+        // Only case that's left is a catalog item with YAML content - try to parse it recursively
+        // including it's OSGi bundles in the loader classpath.
+        } else {
+            // TODO perhaps migrate to catalog.createSpec ?
+            EntitySpec<?> spec = BrooklynAssemblyTemplateInstantiator.resolveCatalogYamlReferenceSpec(mgmt, item, encounteredCatalogTypes);
+            spec.catalogItemId(item.getId());
+            
+            return (EntitySpec<T>)spec;
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    protected <T extends Entity> EntitySpec<T> createSpecFromJavaType() {
+        Class<T> type = (Class<T>) loadEntityClass();
+        
+        EntitySpec<T> spec;
+        if (type.isInterface()) {
+            spec = EntitySpec.create(type);
+        } else {
+            // If this is a concrete class, particularly for an Application class, we want the proxy
+            // to expose all interfaces it implements.
+            @SuppressWarnings("rawtypes")
+            Class interfaceclazz = (Application.class.isAssignableFrom(type)) ? Application.class : Entity.class;
+            List<Class<?>> additionalInterfaceClazzes = Reflections.getAllInterfaces(type);
+            spec = EntitySpec.create(interfaceclazz).impl(type).additionalInterfaces(additionalInterfaceClazzes);
+        }
+        spec.catalogItemId(CatalogUtils.getCatalogItemIdFromLoader(loader));
+        if (template.isPresent() && template.get().getSourceCode()!=null)
+            spec.tag(BrooklynTags.newYamlSpecTag(template.get().getSourceCode()));
+
+        return spec;
+    }
+
+    //called from BrooklynAssemblyTemplateInstantiator as well
+    @SuppressWarnings("unchecked")
+    protected <T extends Entity> void populateSpec(EntitySpec<T> spec) {
+        String name, templateId=null, planId=null;
+        if (template.isPresent()) {
+            name = template.get().getName();
+            templateId = template.get().getId();
+        } else {
+            name = (String)attrs.getStringKey("name");
+        }
+        planId = (String)attrs.getStringKey("id");
+        if (planId==null)
+            planId = (String) attrs.getStringKey(BrooklynCampConstants.PLAN_ID_FLAG);
+
+        Object childrenObj = attrs.getStringKey(BrooklynCampReservedKeys.BROOKLYN_CHILDREN);
+        if (childrenObj != null) {
+            // Creating a new set of encounteredCatalogTypes means that this won't check things recursively;
+            // but we are looking at children so we probably *should* be resetting the recursive list we've looked at;
+            // (but see also, a previous comment here which suggested otherwise? - Apr 2015)
+            Set<String> encounteredCatalogTypes = MutableSet.of();
+
+            Iterable<Map<String,?>> children = (Iterable<Map<String,?>>)childrenObj;
+            for (Map<String,?> childAttrs : children) {
+                BrooklynComponentTemplateResolver entityResolver = BrooklynComponentTemplateResolver.Factory.newInstance(loader, childAttrs);
+                EntitySpec<? extends Entity> childSpec = BrooklynAssemblyTemplateInstantiator.resolveSpec(ResourceUtils.create(this), entityResolver, encounteredCatalogTypes);
+                spec.child(childSpec);
+            }
+        }
+        if (!Strings.isBlank(name))
+            spec.displayName(name);
+        if (templateId != null)
+            spec.configure(BrooklynCampConstants.TEMPLATE_ID, templateId);
+        if (planId != null)
+            spec.configure(BrooklynCampConstants.PLAN_ID, planId);
+
+        List<Location> childLocations = new BrooklynYamlLocationResolver(mgmt).resolveLocations(attrs.getAllConfig(), true);
+        if (childLocations != null)
+            spec.locations(childLocations);
+
+        typeResolver.decorateSpec(this, spec);
+        configureEntityConfig(spec);
+    }
+
+    /** returns new *uninitialised* entity, with just a few of the pieces from the spec;
+     * initialisation occurs soon after, in {@link #initEntity(ManagementContext, Entity, EntitySpec)},
+     * inside an execution context and after entity ID's are recognised
+     */
+    protected <T extends Entity> T newEntity(EntitySpec<T> spec) {
+        Class<? extends T> entityImpl = (spec.getImplementation() != null) ? spec.getImplementation() : mgmt.getEntityManager().getEntityTypeRegistry().getImplementedBy(spec.getType());
+        InternalEntityFactory entityFactory = ((ManagementContextInternal)mgmt).getEntityFactory();
+        T entity = entityFactory.constructEntity(entityImpl, spec);
+
+        String planId = (String)spec.getConfig().get(BrooklynCampConstants.PLAN_ID);
+        if (planId != null) {
+            entity.config().set(BrooklynCampConstants.PLAN_ID, planId);
+        }
+
+        if (spec.getLocations().size() > 0) {
+            ((AbstractEntity)entity).addLocations(spec.getLocations());
+        }
+
+        if (spec.getParent() != null) entity.setParent(spec.getParent());
+
+        return entity;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    protected void configureEntityConfig(EntitySpec<?> spec) {
+        // first take *recognised* flags and config keys from the top-level, and put them in the bag (of brooklyn.config)
+        // attrs will contain only brooklyn.xxx properties when coming from BrooklynEntityMatcher.
+        // Any top-level flags will go into "brooklyn.flags". When resolving a spec from $brooklyn:entitySpec
+        // top level flags remain in place. Have to support both cases.
+
+        ConfigBag bag = ConfigBag.newInstance((Map<Object, Object>) attrs.getStringKey(BrooklynCampReservedKeys.BROOKLYN_CONFIG));
+        ConfigBag bagFlags = ConfigBag.newInstanceCopying(attrs);
+        if (attrs.containsKey(BrooklynCampReservedKeys.BROOKLYN_FLAGS)) {
+            bagFlags.putAll((Map<String, Object>) attrs.getStringKey(BrooklynCampReservedKeys.BROOKLYN_FLAGS));
+        }
+
+        Collection<FlagConfigKeyAndValueRecord> topLevelApparentConfig = findAllFlagsAndConfigKeys(spec, bagFlags);
+        for (FlagConfigKeyAndValueRecord r: topLevelApparentConfig) {
+            if (r.getConfigKeyMaybeValue().isPresent())
+                bag.putIfAbsent((ConfigKey)r.getConfigKey(), r.getConfigKeyMaybeValue().get());
+            if (r.getFlagMaybeValue().isPresent())
+                bag.putAsStringKeyIfAbsent(r.getFlagName(), r.getFlagMaybeValue().get());
+        }
+
+        // now set configuration for all the items in the bag
+        Collection<FlagConfigKeyAndValueRecord> records = findAllFlagsAndConfigKeys(spec, bag);
+        Set<String> keyNamesUsed = new LinkedHashSet<String>();
+        for (FlagConfigKeyAndValueRecord r: records) {
+            if (r.getFlagMaybeValue().isPresent()) {
+                Object transformed = new SpecialFlagsTransformer(loader).transformSpecialFlags(r.getFlagMaybeValue().get());
+                spec.configure(r.getFlagName(), transformed);
+                keyNamesUsed.add(r.getFlagName());
+            }
+            if (r.getConfigKeyMaybeValue().isPresent()) {
+                Object transformed = new SpecialFlagsTransformer(loader).transformSpecialFlags(r.getConfigKeyMaybeValue().get());
+                spec.configure((ConfigKey<Object>)r.getConfigKey(), transformed);
+                keyNamesUsed.add(r.getConfigKey().getName());
+            }
+        }
+
+        // set unused keys as anonymous config keys -
+        // they aren't flags or known config keys, so must be passed as config keys in order for
+        // EntitySpec to know what to do with them (as they are passed to the spec as flags)
+        for (String key: MutableSet.copyOf(bag.getUnusedConfig().keySet())) {
+            // we don't let a flag with the same name as a config key override the config key
+            // (that's why we check whether it is used)
+            if (!keyNamesUsed.contains(key)) {
+                Object transformed = new SpecialFlagsTransformer(loader).transformSpecialFlags(bag.getStringKey(key));
+                spec.configure(ConfigKeys.newConfigKey(Object.class, key.toString()), transformed);
+            }
+        }
+    }
+
+    /**
+     * Searches for config keys in the type, additional interfaces and the implementation (if specified)
+     */
+    private Collection<FlagConfigKeyAndValueRecord> findAllFlagsAndConfigKeys(EntitySpec<?> spec, ConfigBag bagFlags) {
+        Set<FlagConfigKeyAndValueRecord> allKeys = MutableSet.of();
+        allKeys.addAll(FlagUtils.findAllFlagsAndConfigKeys(null, spec.getType(), bagFlags));
+        if (spec.getImplementation() != null) {
+            allKeys.addAll(FlagUtils.findAllFlagsAndConfigKeys(null, spec.getImplementation(), bagFlags));
+        }
+        for (Class<?> iface : spec.getAdditionalInterfaces()) {
+            allKeys.addAll(FlagUtils.findAllFlagsAndConfigKeys(null, iface, bagFlags));
+        }
+        return allKeys;
+    }
+
+    protected static class SpecialFlagsTransformer implements Function<Object, Object> {
+        protected final ManagementContext mgmt;
+        /* TODO find a way to make do without loader here?
+         * it is not very nice having to serialize it; but serialization of BLCL is now relatively clean.
+         *
+         * it is only used to instantiate classes, and now most things should be registered with catalog;
+         * the notable exception is when one entity in a bundle is creating another in the same bundle,
+         * it wants to use his bundle CLC to do that.  but we can set up some unique reference to the entity
+         * which can be used to find it from mgmt, rather than pass the loader.
+         */
+        private BrooklynClassLoadingContext loader = null;
+
+        public SpecialFlagsTransformer(BrooklynClassLoadingContext loader) {
+            this.loader = loader;
+            mgmt = loader.getManagementContext();
+        }
+        public Object apply(Object input) {
+            if (input instanceof Map)
+                return transformSpecialFlags((Map<?, ?>)input);
+            else if (input instanceof Set<?>)
+                return MutableSet.of(transformSpecialFlags((Iterable<?>)input));
+            else if (input instanceof List<?>)
+                return MutableList.copyOf(transformSpecialFlags((Iterable<?>)input));
+            else if (input instanceof Iterable<?>)
+                return transformSpecialFlags((Iterable<?>)input);
+            else
+                return transformSpecialFlags((Object)input);
+        }
+
+        protected Map<?, ?> transformSpecialFlags(Map<?, ?> flag) {
+            return Maps.transformValues(flag, this);
+        }
+
+        protected Iterable<?> transformSpecialFlags(Iterable<?> flag) {
+            return Iterables.transform(flag, this);
+        }
+
+        protected BrooklynClassLoadingContext getLoader() {
+            if (loader!=null) return loader;
+            // TODO currently loader will non-null unless someone has messed with the rebind files,
+            // but we'd like to get rid of it; ideally we'd have a reference to the entity.
+            // for now, this is a slightly naff way to do it, if we have to set loader=null as a workaround
+            Entity entity = BrooklynTaskTags.getTargetOrContextEntity(Tasks.current());
+            if (entity!=null) return CatalogUtils.getClassLoadingContext(entity);
+            return JavaBrooklynClassLoadingContext.create(mgmt);
+        }
+
+        /**
+         * Makes additional transformations to the given flag with the extra knowledge of the flag's management context.
+         * @return The modified flag, or the flag unchanged.
+         */
+        protected Object transformSpecialFlags(Object flag) {
+            if (flag instanceof EntitySpecConfiguration) {
+                EntitySpecConfiguration specConfig = (EntitySpecConfiguration) flag;
+                // TODO: This should called from BrooklynAssemblyTemplateInstantiator.configureEntityConfig
+                // And have transformSpecialFlags(Object flag, ManagementContext mgmt) drill into the Object flag if it's a map or iterable?
+                @SuppressWarnings("unchecked")
+                Map<String, Object> resolvedConfig = (Map<String, Object>)transformSpecialFlags(specConfig.getSpecConfiguration());
+                specConfig.setSpecConfiguration(resolvedConfig);
+                return Factory.newInstance(getLoader(), specConfig.getSpecConfiguration()).resolveSpec(null);
+            }
+            if (flag instanceof ManagementContextInjectable) {
+                log.debug("Injecting Brooklyn management context info object: {}", flag);
+                ((ManagementContextInjectable) flag).injectManagementContext(loader.getManagementContext());
+            }
+
+            return flag;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    protected List<Map<String, Object>> getChildren(Map<String, Object> attrs) {
+        if (attrs==null) return null;
+        return (List<Map<String, Object>>) attrs.get(BrooklynCampReservedKeys.BROOKLYN_CHILDREN);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e406d1ad/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
new file mode 100644
index 0000000..8af9ba5
--- /dev/null
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.camp.brooklyn.spi.creation;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys;
+import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiator.InstantiatorFromKey;
+import org.apache.brooklyn.catalog.BrooklynCatalog;
+import org.apache.brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.internal.CatalogUtils;
+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;
+import brooklyn.policy.PolicySpec;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.config.ConfigBag;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Pattern for resolving "decorations" on service specs / entity specs, such as policies, enrichers, etc.
+ * @since 0.7.0
+ */
+@Beta
+public abstract class BrooklynEntityDecorationResolver<DT> {
+
+    public final BrooklynYamlTypeInstantiator.Factory instantiator;
+    
+    protected BrooklynEntityDecorationResolver(BrooklynYamlTypeInstantiator.Factory instantiator) {
+        this.instantiator = instantiator;
+    }
+    
+    public abstract void decorate(EntitySpec<?> entitySpec, ConfigBag attrs);
+
+    protected Iterable<? extends DT> buildListOfTheseDecorationsFromEntityAttributes(ConfigBag attrs) {
+        Object value = getDecorationAttributeJsonValue(attrs); 
+        List<DT> decorations = MutableList.of();
+        if (value==null) return decorations;
+        if (value instanceof Iterable) {
+            for (Object decorationJson: (Iterable<?>)value)
+                addDecorationFromJsonMap(checkIsMap(decorationJson), decorations);
+        } else {
+            // in future may support types other than iterables here, 
+            // e.g. a map short form where the key is the type
+            throw new IllegalArgumentException(getDecorationKind()+" body should be iterable, not " + value.getClass());
+        }
+        return decorations;
+    }
+    
+    protected Map<?,?> checkIsMap(Object decorationJson) {
+        if (!(decorationJson instanceof Map))
+            throw new IllegalArgumentException(getDecorationKind()+" value must be a Map, not " + 
+                (decorationJson==null ? null : decorationJson.getClass()) );
+        return (Map<?,?>) decorationJson;
+    }
+
+    protected abstract String getDecorationKind();
+    protected abstract Object getDecorationAttributeJsonValue(ConfigBag attrs);
+    
+    /** creates and adds decorations from the given json to the given collection; 
+     * default impl requires a map and calls {@link #addDecorationFromJsonMap(Map, List)} */
+    protected void addDecorationFromJson(Object decorationJson, List<DT> decorations) {
+        addDecorationFromJsonMap(checkIsMap(decorationJson), decorations);
+    }
+    protected abstract void addDecorationFromJsonMap(Map<?,?> decorationJson, List<DT> decorations);
+    
+
+    public static class PolicySpecResolver extends BrooklynEntityDecorationResolver<PolicySpec<?>> {
+        
+        public PolicySpecResolver(BrooklynYamlTypeInstantiator.Factory loader) { super(loader); }
+        @Override protected String getDecorationKind() { return "Policy"; }
+
+        @Override
+        public void decorate(EntitySpec<?> entitySpec, ConfigBag attrs) {
+            entitySpec.policySpecs(buildListOfTheseDecorationsFromEntityAttributes(attrs));
+        }
+        
+        @Override
+        protected Object getDecorationAttributeJsonValue(ConfigBag attrs) {
+            return attrs.getStringKey(BrooklynCampReservedKeys.BROOKLYN_POLICIES);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        protected void addDecorationFromJsonMap(Map<?, ?> decorationJson, List<PolicySpec<?>> decorations) {
+            InstantiatorFromKey decoLoader = instantiator.from(decorationJson).prefix("policy");
+
+            String policyType = decoLoader.getTypeName().get();
+            ManagementContext mgmt = instantiator.loader.getManagementContext();
+            BrooklynCatalog catalog = mgmt.getCatalog();
+            CatalogItem<?, ?> item = getPolicyCatalogItem(catalog, 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);
+        }
+        private CatalogItem<?, ?> getPolicyCatalogItem(BrooklynCatalog catalog, String policyType) {
+            if (CatalogUtils.looksLikeVersionedId(policyType)) {
+                String id = CatalogUtils.getIdFromVersionedId(policyType);
+                String version = CatalogUtils.getVersionFromVersionedId(policyType);
+                return catalog.getCatalogItem(id, version);
+            } else {
+                return catalog.getCatalogItem(policyType, BrooklynCatalog.DEFAULT_VERSION);
+            }
+        }
+    }
+
+    public static class EnricherSpecResolver extends BrooklynEntityDecorationResolver<EnricherSpec<?>> {
+        
+        public EnricherSpecResolver(BrooklynYamlTypeInstantiator.Factory loader) { super(loader); }
+        @Override protected String getDecorationKind() { return "Enricher"; }
+
+        @Override
+        public void decorate(EntitySpec<?> entitySpec, ConfigBag attrs) {
+            entitySpec.enricherSpecs(buildListOfTheseDecorationsFromEntityAttributes(attrs));
+        }
+        
+        @Override
+        protected Object getDecorationAttributeJsonValue(ConfigBag attrs) {
+            return attrs.getStringKey(BrooklynCampReservedKeys.BROOKLYN_ENRICHERS);
+        }
+
+        @Override
+        protected void addDecorationFromJsonMap(Map<?, ?> decorationJson, List<EnricherSpec<?>> decorations) {
+            InstantiatorFromKey decoLoader = instantiator.from(decorationJson).prefix("enricher");
+            decorations.add(EnricherSpec.create(decoLoader.getType(Enricher.class))
+                .configure( decoLoader.getConfigMap() ));
+        }
+    }
+    
+    public static class InitializerResolver extends BrooklynEntityDecorationResolver<EntityInitializer> {
+        
+        public InitializerResolver(BrooklynYamlTypeInstantiator.Factory loader) { super(loader); }
+        @Override protected String getDecorationKind() { return "Entity initializer"; }
+
+        @Override
+        public void decorate(EntitySpec<?> entitySpec, ConfigBag attrs) {
+            entitySpec.addInitializers(buildListOfTheseDecorationsFromEntityAttributes(attrs));
+        }
+        
+        @Override
+        protected Object getDecorationAttributeJsonValue(ConfigBag attrs) {
+            return attrs.getStringKey(BrooklynCampReservedKeys.BROOKLYN_INITIALIZERS);
+        }
+
+        @Override
+        protected void addDecorationFromJsonMap(Map<?, ?> decorationJson, List<EntityInitializer> decorations) {
+            decorations.add(instantiator.from(decorationJson).prefix("initializer").newInstance(EntityInitializer.class));
+        }
+    }
+    
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e406d1ad/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java
new file mode 100644
index 0000000..27ccae7
--- /dev/null
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.camp.brooklyn.spi.creation;
+
+import io.brooklyn.camp.spi.PlatformComponentTemplate;
+import io.brooklyn.camp.spi.PlatformComponentTemplate.Builder;
+import io.brooklyn.camp.spi.pdp.AssemblyTemplateConstructor;
+import io.brooklyn.camp.spi.pdp.Service;
+import io.brooklyn.camp.spi.resolve.PdpMatcher;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
+import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.catalog.internal.BasicBrooklynCatalog;
+import brooklyn.management.ManagementContext;
+import brooklyn.management.classloading.BrooklynClassLoadingContext;
+import brooklyn.management.classloading.JavaBrooklynClassLoadingContext;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.net.Urls;
+import brooklyn.util.text.Strings;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+public class BrooklynEntityMatcher implements PdpMatcher {
+
+    private static final Logger log = LoggerFactory.getLogger(BrooklynEntityMatcher.class);
+    
+    protected final ManagementContext mgmt;
+
+    public BrooklynEntityMatcher(ManagementContext bmc) {
+        this.mgmt = bmc;
+    }
+
+    @Override
+    public boolean accepts(Object deploymentPlanItem) {
+        return lookupType(deploymentPlanItem) != null;
+    }
+
+    /** returns the type of the given plan item, 
+     * typically whether a Service can be matched to a Brooklyn entity,
+     * or null if not supported */
+    protected String lookupType(Object deploymentPlanItem) {
+        if (deploymentPlanItem instanceof Service) {
+            Service service = (Service)deploymentPlanItem;
+
+            String serviceType = service.getServiceType();
+            BrooklynClassLoadingContext loader = BasicBrooklynCatalog.BrooklynLoaderTracker.getLoader();
+            if (loader == null) loader = JavaBrooklynClassLoadingContext.create(mgmt);
+            if (BrooklynComponentTemplateResolver.Factory.supportsType(loader, serviceType))
+                return serviceType;
+
+            String protocol = Urls.getProtocol(serviceType);
+            if (protocol != null) {
+                if (BrooklynCampConstants.YAML_URL_PROTOCOL_WHITELIST.contains(protocol)) {
+                    return serviceType;
+                } else {
+                    log.debug("The reference '" + serviceType + "' looks like a URL (running the CAMP Brooklyn entity-matcher) but the protocol '" + 
+                            protocol + "' isn't white listed " + BrooklynCampConstants.YAML_URL_PROTOCOL_WHITELIST + ". " +
+                            "Not recognized as catalog item or java item as well!");
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean apply(Object deploymentPlanItem, AssemblyTemplateConstructor atc) {
+        if (!(deploymentPlanItem instanceof Service)) return false;
+        
+        String type = lookupType(deploymentPlanItem);
+        if (type==null) return false;
+
+        log.debug("Item "+deploymentPlanItem+" being instantiated with "+type);
+
+        Object old = atc.getInstantiator();
+        if (old!=null && !old.equals(BrooklynAssemblyTemplateInstantiator.class)) {
+            log.warn("Can't mix Brooklyn entities with non-Brooklyn entities (at present): "+old);
+            return false;
+        }
+
+        // TODO should we build up a new type, BrooklynEntityComponentTemplate here
+        // complete w EntitySpec -- ie merge w BrooklynComponentTemplateResolver ?
+        
+        Builder<? extends PlatformComponentTemplate> builder = PlatformComponentTemplate.builder();
+        builder.type( type.indexOf(':')==-1 ? "brooklyn:"+type : type );
+        
+        // currently instantiator must be brooklyn at the ATC level
+        // optionally would be nice to support multiple/mixed instantiators, 
+        // ie at the component level, perhaps with the first one responsible for building the app
+        atc.instantiator(BrooklynAssemblyTemplateInstantiator.class);
+
+        String name = ((Service)deploymentPlanItem).getName();
+        if (!Strings.isBlank(name)) builder.name(name);
+        
+        // configuration
+        Map<String, Object> attrs = MutableMap.copyOf( ((Service)deploymentPlanItem).getCustomAttributes() );
+
+        if (attrs.containsKey("id"))
+            builder.customAttribute("planId", attrs.remove("id"));
+
+        Object location = attrs.remove("location");
+        if (location!=null)
+            builder.customAttribute("location", location);
+        Object locations = attrs.remove("locations");
+        if (locations!=null)
+            builder.customAttribute("locations", locations);
+
+        MutableMap<Object, Object> brooklynFlags = MutableMap.of();
+        Object origBrooklynFlags = attrs.remove(BrooklynCampReservedKeys.BROOKLYN_FLAGS);
+        if (origBrooklynFlags!=null) {
+            if (!(origBrooklynFlags instanceof Map))
+                throw new IllegalArgumentException("brooklyn.flags must be a map of brooklyn flags");
+            brooklynFlags.putAll((Map<?,?>)origBrooklynFlags);
+        }
+
+        addCustomMapAttributeIfNonNull(builder, attrs, BrooklynCampReservedKeys.BROOKLYN_CONFIG);
+        addCustomListAttributeIfNonNull(builder, attrs, BrooklynCampReservedKeys.BROOKLYN_POLICIES);
+        addCustomListAttributeIfNonNull(builder, attrs, BrooklynCampReservedKeys.BROOKLYN_ENRICHERS);
+        addCustomListAttributeIfNonNull(builder, attrs, BrooklynCampReservedKeys.BROOKLYN_INITIALIZERS);
+        addCustomListAttributeIfNonNull(builder, attrs, BrooklynCampReservedKeys.BROOKLYN_CHILDREN);
+        addCustomMapAttributeIfNonNull(builder, attrs, BrooklynCampReservedKeys.BROOKLYN_CATALOG);
+
+        brooklynFlags.putAll(attrs);
+        if (!brooklynFlags.isEmpty()) {
+            builder.customAttribute(BrooklynCampReservedKeys.BROOKLYN_FLAGS, brooklynFlags);
+        }
+
+        atc.add(builder.build());
+
+        return true;
+    }
+
+    /**
+     * Looks for the given key in the map of attributes and adds it to the given builder
+     * as a custom attribute with type List.
+     * @throws java.lang.IllegalArgumentException if map[key] is not an instance of List
+     */
+    private void addCustomListAttributeIfNonNull(Builder<? extends PlatformComponentTemplate> builder, Map<?,?> attrs, String key) {
+        Object items = attrs.remove(key);
+        if (items != null) {
+            if (items instanceof List) {
+                List<?> itemList = (List<?>) items;
+                if (!itemList.isEmpty()) {
+                    builder.customAttribute(key, Lists.newArrayList(itemList));
+                }
+            } else {
+                throw new IllegalArgumentException(key + " must be a list, is: " + items.getClass().getName());
+            }
+        }
+    }
+
+    /**
+     * Looks for the given key in the map of attributes and adds it to the given builder
+     * as a custom attribute with type Map.
+     * @throws java.lang.IllegalArgumentException if map[key] is not an instance of Map
+     */
+    private void addCustomMapAttributeIfNonNull(Builder<? extends PlatformComponentTemplate> builder, Map<?,?> attrs, String key) {
+        Object items = attrs.remove(key);
+        if (items != null) {
+            if (items instanceof Map) {
+                Map<?, ?> itemMap = (Map<?, ?>) items;
+                if (!itemMap.isEmpty()) {
+                    builder.customAttribute(key, Maps.newHashMap(itemMap));
+                }
+            } else {
+                throw new IllegalArgumentException(key + " must be a map, is: " + items.getClass().getName());
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e406d1ad/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlLocationResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlLocationResolver.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlLocationResolver.java
new file mode 100644
index 0000000..7c4f734
--- /dev/null
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlLocationResolver.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.camp.brooklyn.spi.creation;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import brooklyn.location.Location;
+import brooklyn.location.LocationDefinition;
+import brooklyn.management.ManagementContext;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.guava.Maybe.Absent;
+import brooklyn.util.text.Strings;
+
+import com.google.common.collect.Iterables;
+
+public class BrooklynYamlLocationResolver {
+
+    protected final ManagementContext mgmt;
+
+    public BrooklynYamlLocationResolver(ManagementContext bmc) {
+        this.mgmt = bmc;
+    }
+
+    /** returns list of locations, if any were supplied, or null if none indicated */
+    @SuppressWarnings("unchecked")
+    public List<Location> resolveLocations(Map<? super String,?> attrs, boolean removeUsedAttributes) {
+        Object location = attrs.get("location");
+        Object locations = attrs.get("locations");
+
+        if (location==null && locations==null)
+            return null;
+        
+        Location locationFromString = null;
+        List<Location> locationsFromList = null;
+        
+        if (location!=null) {
+            if (location instanceof String) {
+                locationFromString = resolveLocationFromString((String)location);
+            } else if (location instanceof Map) {
+                locationFromString = resolveLocationFromMap((Map<?,?>)location);
+            } else {
+                throw new IllegalStateException("Illegal parameter for 'location'; must be a string or map (but got "+location+")");
+            }
+        }
+        
+        if (locations!=null) {
+            if (!(locations instanceof Iterable))
+                throw new IllegalStateException("Illegal parameter for 'locations'; must be an iterable (but got "+locations+")");
+            locationsFromList = resolveLocations( (Iterable<Object>)locations );
+        }
+        
+        if (locationFromString!=null && locationsFromList!=null) {
+            if (locationsFromList.size() != 1)
+                throw new IllegalStateException("Conflicting 'location' and 'locations' ("+location+" and "+locations+"); "
+                    + "if both are supplied the list must have exactly one element being the same");
+            if (!locationFromString.equals( Iterables.getOnlyElement(locationsFromList) ))
+                throw new IllegalStateException("Conflicting 'location' and 'locations' ("+location+" and "+locations+"); "
+                    + "different location specified in each");
+        } else if (locationFromString!=null) {
+            locationsFromList = Arrays.asList(locationFromString);
+        }
+        
+        return locationsFromList;
+    }
+
+    public List<Location> resolveLocations(Iterable<Object> locations) {
+        List<Location> result = MutableList.of();
+        for (Object l: locations) {
+            Location ll = resolveLocation(l);
+            if (ll!=null) result.add(ll);
+        }
+        return result;
+    }
+
+    public Location resolveLocation(Object location) {
+        if (location instanceof String) {
+            return resolveLocationFromString((String)location);
+        } else if (location instanceof Map) {
+            return resolveLocationFromMap((Map<?,?>)location);
+        }
+        // could support e.g. location definition
+        throw new IllegalStateException("Illegal parameter for 'location' ("+location+"); must be a string or map");
+    }
+    
+    /** resolves the location from the given spec string, either "Named Location", or "named:Named Location" format;
+     * returns null if input is blank (or null); otherwise guaranteed to resolve or throw error */
+    public Location resolveLocationFromString(String location) {
+        if (Strings.isBlank(location)) return null;
+        return resolveLocation(location, MutableMap.of());
+    }
+
+    public Location resolveLocationFromMap(Map<?,?> location) {
+        if (location.size() > 1) {
+            throw new IllegalStateException("Illegal parameter for 'location'; expected a single entry in map ("+location+")");
+        }
+        Object key = Iterables.getOnlyElement(location.keySet());
+        Object value = location.get(key);
+        
+        if (!(key instanceof String)) {
+            throw new IllegalStateException("Illegal parameter for 'location'; expected String key ("+location+")");
+        }
+        if (!(value instanceof Map)) {
+            throw new IllegalStateException("Illegal parameter for 'location'; expected config map ("+location+")");
+        }
+        return resolveLocation((String)key, (Map<?,?>)value);
+    }
+    
+    protected Location resolveLocation(String spec, Map<?,?> flags) {
+        LocationDefinition ldef = mgmt.getLocationRegistry().getDefinedLocationByName((String)spec);
+        if (ldef!=null)
+            // found it as a named location
+            return mgmt.getLocationRegistry().resolve(ldef, null, flags).get();
+        
+        Maybe<Location> l = mgmt.getLocationRegistry().resolve(spec, null, flags);
+        if (l.isPresent()) return l.get();
+        
+        RuntimeException exception = ((Absent<?>)l).getException();
+        throw new IllegalStateException("Illegal parameter for 'location' ("+spec+"); not resolvable: "+
+            Exceptions.collapseText( exception ), exception);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e406d1ad/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlTypeInstantiator.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlTypeInstantiator.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlTypeInstantiator.java
new file mode 100644
index 0000000..8c2cb72
--- /dev/null
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynYamlTypeInstantiator.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.camp.brooklyn.spi.creation;
+
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.management.classloading.BrooklynClassLoadingContext;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.javalang.Reflections;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+/** Assists in loading types referenced from YAML;
+ * mainly as a way to share logic used in very different contexts. */
+public abstract class BrooklynYamlTypeInstantiator {
+
+    private static final Logger log = LoggerFactory.getLogger(BrooklynYamlTypeInstantiator.class);
+    
+    protected final Factory factory;
+
+    @Beta
+    public static class Factory {
+        final BrooklynClassLoadingContext loader;
+        final Object contextForLogging;
+        
+        public Factory(BrooklynClassLoadingContext loader, Object contextForLogging) {
+            this.loader = loader;
+            this.contextForLogging = contextForLogging;
+        }
+        
+        public InstantiatorFromKey from(Map<?,?> data) {
+            return new InstantiatorFromKey(this, ConfigBag.newInstance(data));
+        }
+        
+        public InstantiatorFromKey from(ConfigBag data) {
+            return new InstantiatorFromKey(this, data);
+        }
+        
+        public InstantiatorFromName type(String typeName) {
+            return new InstantiatorFromName(this, typeName);
+        }
+
+    }
+        
+    public static class InstantiatorFromKey extends BrooklynYamlTypeInstantiator {
+        protected final ConfigBag data;
+        protected String typeKeyPrefix = null;
+        
+        /** Nullable only permitted for instances which do not do loading, e.g. LoaderFromKey#lookup */
+        protected InstantiatorFromKey(@Nullable Factory factory, ConfigBag data) {
+            super(factory);
+            this.data = data;
+        }
+        
+        public static Maybe<String> extractTypeName(String prefix, ConfigBag data) {
+            if (data==null) return Maybe.absent();
+            return new InstantiatorFromKey(null, data).prefix(prefix).getTypeName();
+        }
+        
+        public InstantiatorFromKey prefix(String prefix) {
+            typeKeyPrefix = prefix;
+            return this;
+        }
+
+        public Maybe<String> getTypeName() {
+            Maybe<Object> result = data.getStringKeyMaybe(getPreferredKeyName());
+            if (result.isAbsent() && typeKeyPrefix!=null) {
+                // try alternatives if a prefix was specified
+                result = data.getStringKeyMaybe(typeKeyPrefix+"Type");
+                if (result.isAbsent()) result = data.getStringKeyMaybe("type");
+            }
+            
+            if (result.isAbsent() || result.get()==null) 
+                return Maybe.absent("Missing key '"+getPreferredKeyName()+"'");
+            
+            if (result.get() instanceof String) return Maybe.of((String)result.get());
+            
+            throw new IllegalArgumentException("Invalid value "+result.get().getClass()+" for "+getPreferredKeyName()+"; "
+                + "expected String, got "+result.get());
+        }
+        
+        protected String getPreferredKeyName() {
+            if (typeKeyPrefix!=null) return typeKeyPrefix+"_type";
+            return "type";
+        }
+        
+        /** as {@link #newInstance(Class)} but inferring the type */
+        public Object newInstance() {
+            return newInstance(null);
+        }
+        
+        /** creates a new instance of the type referred to by this description,
+         * as a subtype of the type supplied here, 
+         * inferring a Map from <code>brooklyn.config</code> key.
+         * TODO in future also picking up recognized flags and config keys (those declared on the type).  
+         * <p>
+         * constructs the object using:
+         * <li> a constructor on the class taking a Map
+         * <li> a no-arg constructor, only if the inferred map is empty  
+         **/
+        public <T> T newInstance(@Nullable Class<T> supertype) {
+            Class<? extends T> type = getType(supertype);
+            Map<String, ?> cfg = getConfigMap();
+            Optional<? extends T> result = Reflections.invokeConstructorWithArgs(type, cfg);
+            if (result.isPresent()) 
+                return result.get();
+            
+            ConfigBag cfgBag = ConfigBag.newInstance(cfg);
+            result = Reflections.invokeConstructorWithArgs(type, cfgBag);
+            if (result.isPresent()) 
+                return result.get();
+            
+            if (cfg.isEmpty()) {
+                result = Reflections.invokeConstructorWithArgs(type);
+                if (result.isPresent()) 
+                    return result.get();
+            }
+            
+            throw new IllegalStateException("No known mechanism for constructing type "+type+" in "+factory.contextForLogging);
+        }
+
+        /** finds the map of config for the type specified;
+         * currently only gets <code>brooklyn.config</code>, returning empty map if none,
+         * but TODO in future should support recognized flags and config keys (those declared on the type),
+         * incorporating code in {@link BrooklynEntityMatcher}.
+         */
+        @SuppressWarnings("unchecked")
+        @Nonnull
+        public Map<String,?> getConfigMap() {
+            MutableMap<String,Object> result = MutableMap.of();
+            Object bc = data.getStringKey(BrooklynCampReservedKeys.BROOKLYN_CONFIG);
+            if (bc!=null) {
+                if (bc instanceof Map)
+                    result.putAll((Map<? extends String, ?>) bc);
+                else
+                    throw new IllegalArgumentException("brooklyn.config key in "+factory.contextForLogging+" should be a map, not "+bc.getClass()+" ("+bc+")");
+            }
+            return result; 
+        }
+
+    }
+    
+    public static class InstantiatorFromName extends BrooklynYamlTypeInstantiator {
+        protected final String typeName;
+        protected InstantiatorFromName(Factory factory, String typeName) {
+            super(factory);
+            this.typeName = typeName;
+        }
+        
+        public Maybe<String> getTypeName() {
+            return Maybe.fromNullable(typeName);
+        }
+    }
+    
+    protected BrooklynYamlTypeInstantiator(Factory factory) {
+        this.factory = factory;
+    }
+        
+    public abstract Maybe<String> getTypeName();
+    
+    public BrooklynClassLoadingContext getClassLoadingContext() {
+        Preconditions.checkNotNull(factory, "No factory set; cannot use this instance for type loading");
+        return factory.loader;
+    }
+    
+    public Class<?> getType() {
+        return getType(Object.class);
+    }
+    
+    public <T> Class<? extends T> getType(@Nonnull Class<T> type) {
+        try {
+            return getClassLoadingContext().loadClass(getTypeName().get(), type);
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            log.debug("Unable to resolve " + type + " " + getTypeName().get() + " (rethrowing) in spec " + factory.contextForLogging);
+            throw Exceptions.propagate(e);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e406d1ad/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/EntitySpecConfiguration.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/EntitySpecConfiguration.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/EntitySpecConfiguration.java
new file mode 100644
index 0000000..626529f
--- /dev/null
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/EntitySpecConfiguration.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.camp.brooklyn.spi.creation;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.proxying.EntitySpec;
+
+import com.google.common.collect.Maps;
+
+/**
+ * Captures the {@link EntitySpec} configuration defined in YAML. 
+ * 
+ * This class does not parse that output; it just stores it.
+ */
+public class EntitySpecConfiguration {
+
+    @SuppressWarnings("unused")
+    private static final Logger LOG = LoggerFactory.getLogger(EntitySpecConfiguration.class);
+
+    private Map<String, Object> specConfiguration;
+
+    public EntitySpecConfiguration(Map<String, ?> specConfiguration) {
+        this.specConfiguration = Maps.newHashMap(checkNotNull(specConfiguration, "specConfiguration"));
+    }
+
+    public Map<String, Object> getSpecConfiguration() {
+        return specConfiguration;
+    }
+    
+    /**
+     * Allows BrooklynComponentTemplateResolver to traverse the configuration and resolve any entity specs
+     */
+    public void setSpecConfiguration(Map<String, Object> specConfiguration) {
+       this.specConfiguration =  specConfiguration;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e406d1ad/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/BrooklynServiceTypeResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/BrooklynServiceTypeResolver.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/BrooklynServiceTypeResolver.java
new file mode 100644
index 0000000..de9f36f
--- /dev/null
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/BrooklynServiceTypeResolver.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.camp.brooklyn.spi.creation.service;
+
+import io.brooklyn.camp.spi.PlatformComponentTemplate;
+
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynComponentTemplateResolver;
+import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynEntityDecorationResolver;
+import org.apache.brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.internal.CatalogUtils;
+import brooklyn.entity.Entity;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.util.text.Strings;
+
+/**
+ * This converts {@link PlatformComponentTemplate} instances whose type is prefixed {@code brooklyn:}
+ * to Brooklyn {@link EntitySpec} instances.
+ */
+public class BrooklynServiceTypeResolver implements ServiceTypeResolver {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ServiceTypeResolver.class);
+
+    @Override
+    public String getTypePrefix() { return DEFAULT_TYPE_PREFIX; }
+
+    @Override
+    public String getBrooklynType(String serviceType) {
+        String type = Strings.removeFromStart(serviceType, getTypePrefix() + ":").trim();
+        if (type == null) return null;
+        return type;
+    }
+
+    @Nullable
+    @Override
+    public CatalogItem<Entity,EntitySpec<?>> getCatalogItem(BrooklynComponentTemplateResolver resolver, String serviceType) {
+        String type = getBrooklynType(serviceType);
+        if (type != null) {
+            return CatalogUtils.getCatalogItemOptionalVersion(resolver.getManagementContext(), Entity.class,  type);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public <T extends Entity> void decorateSpec(BrooklynComponentTemplateResolver resolver, EntitySpec<T> spec) {
+        new BrooklynEntityDecorationResolver.PolicySpecResolver(resolver.getYamlLoader()).decorate(spec, resolver.getAttrs());
+        new BrooklynEntityDecorationResolver.EnricherSpecResolver(resolver.getYamlLoader()).decorate(spec, resolver.getAttrs());
+        new BrooklynEntityDecorationResolver.InitializerResolver(resolver.getYamlLoader()).decorate(spec, resolver.getAttrs());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e406d1ad/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/CatalogServiceTypeResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/CatalogServiceTypeResolver.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/CatalogServiceTypeResolver.java
new file mode 100644
index 0000000..e569183
--- /dev/null
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/CatalogServiceTypeResolver.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.camp.brooklyn.spi.creation.service;
+
+import io.brooklyn.camp.spi.PlatformComponentTemplate;
+
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.VanillaSoftwareProcess;
+import brooklyn.entity.brooklynnode.BrooklynNode;
+import brooklyn.entity.group.DynamicCluster;
+import brooklyn.entity.group.DynamicRegionsFabric;
+import brooklyn.entity.java.VanillaJavaApp;
+import brooklyn.entity.proxying.EntitySpec;
+
+import com.google.common.base.CaseFormat;
+import com.google.common.base.Converter;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * This converts {@link PlatformComponentTemplate} instances whose type is prefixed {@code catalog:}
+ * to Brooklyn {@link EntitySpec} instances.
+ */
+public class CatalogServiceTypeResolver extends BrooklynServiceTypeResolver {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ServiceTypeResolver.class);
+
+    // TODO currently a hardcoded list of aliases; would like that to come from mgmt somehow
+    private static final Map<String, String> CATALOG_TYPES = ImmutableMap.<String, String>builder()
+            .put("cluster", DynamicCluster.class.getName())
+            .put("fabric", DynamicRegionsFabric.class.getName())
+            .put("vanilla", VanillaSoftwareProcess.class.getName())
+            .put("software-process", VanillaSoftwareProcess.class.getName())
+            .put("java-app", VanillaJavaApp.class.getName())
+            .put("brooklyn-node", BrooklynNode.class.getName())
+            .put("web-app-cluster","brooklyn.entity.webapp.ControlledDynamicWebAppCluster")
+            .build();
+
+    // Allow catalog-type or CatalogType as service type string
+    private static final Converter<String, String> FMT = CaseFormat.LOWER_HYPHEN.converterTo(CaseFormat.UPPER_CAMEL);
+
+    @Override
+    public String getTypePrefix() { return "catalog"; }
+
+    @Override
+    public String getBrooklynType(String serviceType) {
+        String type = super.getBrooklynType(serviceType);
+        if (type == null) return null;
+
+        for (String check : CATALOG_TYPES.keySet()) {
+            if (type.equals(check) || type.equals(FMT.convert(check))) {
+                return CATALOG_TYPES.get(check);
+            }
+        }
+
+        return type;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e406d1ad/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/ChefServiceTypeResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/ChefServiceTypeResolver.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/ChefServiceTypeResolver.java
new file mode 100644
index 0000000..f5d135f
--- /dev/null
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/ChefServiceTypeResolver.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.camp.brooklyn.spi.creation.service;
+
+import io.brooklyn.camp.spi.PlatformComponentTemplate;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynComponentTemplateResolver;
+import org.apache.brooklyn.catalog.CatalogItem;
+import brooklyn.entity.Entity;
+import brooklyn.entity.chef.ChefConfig;
+import brooklyn.entity.chef.ChefEntity;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.util.text.Strings;
+
+/**
+ * This converts {@link PlatformComponentTemplate} instances whose type is prefixed {@code chef:}
+ * to Brooklyn {@link EntitySpec} instances.
+ */
+public class ChefServiceTypeResolver extends BrooklynServiceTypeResolver {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ServiceTypeResolver.class);
+
+    @Override
+    public String getTypePrefix() { return "chef"; }
+
+    @Override
+    public String getBrooklynType(String serviceType) {
+        return ChefEntity.class.getName();
+    }
+
+    /** Chef items are not in the catalog. */
+    @Override
+    public CatalogItem<Entity, EntitySpec<?>> getCatalogItem(BrooklynComponentTemplateResolver resolver, String serviceType) {
+        return null;
+    }
+
+    @Override
+    public <T extends Entity> void decorateSpec(BrooklynComponentTemplateResolver resolver, EntitySpec<T> spec) {
+        spec.configure(ChefConfig.CHEF_COOKBOOK_PRIMARY_NAME, Strings.removeFromStart(resolver.getDeclaredType(), "chef:"));
+        super.decorateSpec(resolver, spec);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e406d1ad/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/JavaServiceTypeResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/JavaServiceTypeResolver.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/JavaServiceTypeResolver.java
new file mode 100644
index 0000000..8670723
--- /dev/null
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/JavaServiceTypeResolver.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.camp.brooklyn.spi.creation.service;
+
+import io.brooklyn.camp.spi.PlatformComponentTemplate;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.proxying.EntitySpec;
+
+/**
+ * This converts {@link PlatformComponentTemplate} instances whose type is prefixed {@code java:}
+ * to Brooklyn {@link EntitySpec} instances.
+ */
+public class JavaServiceTypeResolver extends BrooklynServiceTypeResolver {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ServiceTypeResolver.class);
+
+    @Override
+    public String getTypePrefix() { return "java"; }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e406d1ad/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/ServiceTypeResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/ServiceTypeResolver.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/ServiceTypeResolver.java
new file mode 100644
index 0000000..734352b
--- /dev/null
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/ServiceTypeResolver.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.camp.brooklyn.spi.creation.service;
+
+import java.util.ServiceLoader;
+
+import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynComponentTemplateResolver;
+import org.apache.brooklyn.catalog.CatalogItem;
+import brooklyn.entity.Entity;
+import brooklyn.entity.proxying.EntitySpec;
+
+/**
+ * Resolves and decorates {@link EntitySpec entity specifications} based on the {@code serviceType} in a template.
+ * <p>
+ * The {@link #getTypePrefix()} method returns a string that should match the beginning of the
+ * service type. The resolver implementation will use the rest of the service type information
+ * to create and decorate an approprate {@link EntitySpec entity}.
+ * <p>
+ * The resolvers are loaded using the {@link ServiceLoader} mechanism, allowing external libraries
+ * to add extra service type implementations that will be picked up at runtime.
+ *
+ * @see BrooklynServiceTypeResolver
+ * @see ChefServiceTypeResolver
+ */
+public interface ServiceTypeResolver {
+
+    String DEFAULT_TYPE_PREFIX = "brooklyn";
+
+    /**
+     * The service type prefix the resolver is responsible for.
+     */
+    String getTypePrefix();
+
+    /**
+     * The name of the Java type that Brooklyn will instantiate to create the
+     * service. This can be generated from parts of the service type information
+     * or may be a fixed value.
+     */
+    String getBrooklynType(String serviceType);
+
+    /**
+     * Returns the {@link CatalogItem} if there is one for the given type.
+     * <p>
+     * If no type, callers should fall back to default classloading.
+     */
+    CatalogItem<Entity, EntitySpec<?>> getCatalogItem(BrooklynComponentTemplateResolver resolver, String serviceType);
+
+    /**
+     * Takes the provided {@link EntitySpec} and decorates it appropriately for the service type.
+     * <p>
+     * This includes setting configuration and adding policies, enrichers and initializers.
+     *
+     * @see BrooklynServiceTypeResolver#decorateSpec(BrooklynComponentTemplateResolver, EntitySpec)
+     */
+    <T extends Entity> void decorateSpec(BrooklynComponentTemplateResolver resolver, EntitySpec<T> spec);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e406d1ad/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
new file mode 100644
index 0000000..9b27607
--- /dev/null
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.camp.brooklyn.spi.dsl;
+
+import java.io.Serializable;
+
+import io.brooklyn.camp.spi.Assembly;
+import io.brooklyn.camp.spi.AssemblyTemplate;
+import io.brooklyn.camp.spi.resolve.interpret.PlanInterpretationNode;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.effector.EffectorTasks;
+import brooklyn.management.Task;
+import brooklyn.management.TaskFactory;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.task.DeferredSupplier;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/** provide an object suitable to resolve chained invocations in a parsed YAML / Deployment Plan DSL,
+ * which also implements {@link DeferredSupplier} so that they can be resolved when needed
+ * (e.g. when entity-lookup and execution contexts are available).
+ * <p>
+ * implementations of this abstract class are expected to be immutable,
+ * as instances must support usage in multiple {@link Assembly} instances 
+ * created from a single {@link AssemblyTemplate}  
+ * <p>
+ * subclasses which return a deferred value are typically only
+ * resolvable in the context of a {@link Task} on an {@link Entity}; 
+ * these should be only used as the value of a {@link ConfigKey} set in the YAML,
+ * and should not accessed until after the components / entities are created 
+ * and are being started.
+ * (TODO the precise semantics of this are under development.)
+ * <p>
+ **/
+public abstract class BrooklynDslDeferredSupplier<T> implements DeferredSupplier<T>, TaskFactory<Task<T>>, Serializable {
+
+    private static final long serialVersionUID = -8789624905412198233L;
+
+    private static final Logger log = LoggerFactory.getLogger(BrooklynDslDeferredSupplier.class);
+    
+    // TODO json of this object should *be* this, not wrapped this ($brooklyn:literal is a bit of a hack, though it might work!)
+    @JsonInclude
+    @JsonProperty(value="$brooklyn:literal")
+    // currently marked transient because it's only needed for logging
+    private transient Object dsl = "(gone)";
+    
+    public BrooklynDslDeferredSupplier() {
+        PlanInterpretationNode sourceNode = BrooklynDslInterpreter.currentNode();
+        dsl = sourceNode!=null ? sourceNode.getOriginalValue() : null;
+    }
+    
+    /** returns the current entity; for use in implementations of {@link #get()} */
+    protected final static EntityInternal entity() {
+        // rely on implicit ThreadLocal for now
+        return (EntityInternal) EffectorTasks.findEntity();
+    }
+
+    @Override
+    public final synchronized T get() {
+        try {
+            if (log.isDebugEnabled())
+                log.debug("Queuing task to resolve "+dsl);
+            T result = Entities.submit(entity(), newTask()).get();
+            if (log.isDebugEnabled())
+                log.debug("Resolved "+result+" from "+dsl);
+            return result;
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+    
+    @Override
+    public abstract Task<T> newTask();
+
+}