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