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

Reply via email to