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

[09/21] incubator-brooklyn git commit: Parameter aware specs

Parameter aware specs

Let specs be aware of the parameters the underlying brooklyn object is accepting. Catalog items return the same spec parameters derived at addition time. The catalog item can override the underlying entity parameters by including the list in its meta.


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

Branch: refs/heads/master
Commit: 0149bf09972fcfde9819708756cfda176d9865e3
Parents: 5fb4771
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Thu Oct 29 18:44:24 2015 +0200
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 5 15:23:38 2015 +0200

----------------------------------------------------------------------
 .../brooklyn/api/catalog/CatalogItem.java       |  13 +-
 .../apache/brooklyn/api/entity/EntitySpec.java  |  50 +--
 .../internal/AbstractBrooklynObjectSpec.java    |  24 ++
 .../apache/brooklyn/api/objs/SpecParameter.java |  32 ++
 .../catalog/internal/BasicBrooklynCatalog.java  |  44 ++-
 .../catalog/internal/CatalogClasspathDo.java    |   9 +-
 .../core/catalog/internal/CatalogInputDto.java  | 240 --------------
 .../catalog/internal/CatalogItemBuilder.java    |  10 +-
 .../core/catalog/internal/CatalogItemDo.java    |   5 +-
 .../internal/CatalogItemDtoAbstract.java        |  22 +-
 .../internal/JavaCatalogToSpecTransformer.java  |  14 +-
 .../brooklyn/core/entity/AbstractEntity.java    |  19 ++
 .../brooklyn/core/entity/EntityDynamicType.java |  37 +++
 .../core/mgmt/EntityManagementUtils.java        |   6 +-
 .../brooklyn/core/objs/BasicSpecParameter.java  | 322 +++++++++++++++++++
 .../core/objs/proxy/InternalEntityFactory.java  |  12 +
 .../brooklyn/util/core/flags/FlagUtils.java     |  30 +-
 .../internal/CatalogInputDtoClassTest.java      |  65 ----
 .../internal/CatalogInputDtoYamlTest.java       | 185 -----------
 .../core/catalog/internal/CatalogInputTest.java | 139 --------
 .../internal/SpecParameterInMetaTest.java       | 139 ++++++++
 .../entity/DynamicEntityTypeConfigTest.java     | 126 ++++++++
 .../brooklyn/core/entity/EntityTypeTest.java    |  15 +-
 .../objs/BasicSpecParameterFromClassTest.java   |  65 ++++
 .../objs/BasicSpecParameterFromListTest.java    | 186 +++++++++++
 .../camp/brooklyn/BrooklynCampPlatform.java     |   1 +
 .../BrooklynCampPlatformLauncherNoServer.java   |   1 +
 .../camp/brooklyn/BrooklynCampReservedKeys.java |   1 +
 .../BrooklynAssemblyTemplateInstantiator.java   |  22 +-
 .../BrooklynComponentTemplateResolver.java      |   2 +
 .../BrooklynEntityDecorationResolver.java       |  74 ++++-
 .../spi/creation/BrooklynEntityMatcher.java     |   1 +
 .../creation/BrooklynYamlTypeInstantiator.java  |   2 +
 .../brooklyn/spi/creation/CampCatalogUtils.java |  12 +-
 .../camp/brooklyn/spi/creation/CampUtils.java   |  74 ++++-
 .../service/UrlServiceSpecResolver.java         |   7 +-
 .../platform/BrooklynImmutableCampPlatform.java |   1 +
 .../rest/transform/CatalogTransformer.java      |   4 +-
 .../rest/transform/EntityTransformer.java       |   4 +-
 39 files changed, 1243 insertions(+), 772 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/api/src/main/java/org/apache/brooklyn/api/catalog/CatalogItem.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/catalog/CatalogItem.java b/api/src/main/java/org/apache/brooklyn/api/catalog/CatalogItem.java
index e5a2636..7f4e3b3 100644
--- a/api/src/main/java/org/apache/brooklyn/api/catalog/CatalogItem.java
+++ b/api/src/main/java/org/apache/brooklyn/api/catalog/CatalogItem.java
@@ -27,8 +27,8 @@ import org.apache.brooklyn.api.mgmt.rebind.RebindSupport;
 import org.apache.brooklyn.api.mgmt.rebind.Rebindable;
 import org.apache.brooklyn.api.mgmt.rebind.mementos.CatalogItemMemento;
 import org.apache.brooklyn.api.objs.BrooklynObject;
+import org.apache.brooklyn.api.objs.SpecParameter;
 import org.apache.brooklyn.api.typereg.OsgiBundleWithUrl;
-import org.apache.brooklyn.config.ConfigKey;
 
 import com.google.common.annotations.Beta;
 
@@ -47,15 +47,6 @@ public interface CatalogItem<T,SpecT> extends BrooklynObject, Rebindable {
         public boolean isNamed();
     }
 
-    public static interface CatalogInput<T> {
-        /** Short name, to be used in UI */
-        String getLabel();
-        /** Visible by default in UI, not all inputs may be visible at once */
-        boolean isPinned();
-        /** Type information for the input */
-        ConfigKey<T> getType();
-    }
-
     /**
      * @throws UnsupportedOperationException; config not supported for catalog items
      */
@@ -106,7 +97,7 @@ public interface CatalogItem<T,SpecT> extends BrooklynObject, Rebindable {
 
     public String getVersion();
 
-    public List<CatalogInput<?>> getInputs();
+    public List<SpecParameter<?>> getParameters();
 
     public Collection<CatalogBundle> getLibraries();
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/api/src/main/java/org/apache/brooklyn/api/entity/EntitySpec.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/entity/EntitySpec.java b/api/src/main/java/org/apache/brooklyn/api/entity/EntitySpec.java
index a1f7dec..a38e52e 100644
--- a/api/src/main/java/org/apache/brooklyn/api/entity/EntitySpec.java
+++ b/api/src/main/java/org/apache/brooklyn/api/entity/EntitySpec.java
@@ -103,27 +103,7 @@ public class EntitySpec<T extends Entity> extends AbstractBrooklynObjectSpec<T,E
      * original entity spec.
      */
     public static <T extends Entity> EntitySpec<T> create(EntitySpec<T> spec) {
-        EntitySpec<T> result = create(spec.getType())
-                .displayName(spec.getDisplayName())
-                .tags(spec.getTags())
-                .additionalInterfaces(spec.getAdditionalInterfaces())
-                .configure(spec.getConfig())
-                .configure(spec.getFlags())
-                .policySpecs(spec.getPolicySpecs())
-                .policies(spec.getPolicies())
-                .enricherSpecs(spec.getEnricherSpecs())
-                .enrichers(spec.getEnrichers())
-                .addInitializers(spec.getInitializers())
-                .children(spec.getChildren())
-                .members(spec.getMembers())
-                .groups(spec.getGroups())
-                .catalogItemId(spec.getCatalogItemId())
-                .locations(spec.getLocations());
-        
-        if (spec.getParent() != null) result.parent(spec.getParent());
-        if (spec.getImplementation() != null) result.impl(spec.getImplementation());
-        
-        return result;
+        return create(spec.getType()).copyFrom(spec);
     }
     
     public static <T extends Entity> EntitySpec<T> newInstance(Class<T> type) {
@@ -149,7 +129,33 @@ public class EntitySpec<T extends Entity> extends AbstractBrooklynObjectSpec<T,E
     public EntitySpec(Class<T> type) {
         super(type);
     }
-    
+
+    @Override
+    protected EntitySpec<T> copyFrom(EntitySpec<T> otherSpec) {
+        super.copyFrom(otherSpec)
+                .displayName(otherSpec.getDisplayName())
+                .tags(otherSpec.getTags())
+                .additionalInterfaces(otherSpec.getAdditionalInterfaces())
+                .configure(otherSpec.getConfig())
+                .configure(otherSpec.getFlags())
+                .policySpecs(otherSpec.getPolicySpecs())
+                .policies(otherSpec.getPolicies())
+                .enricherSpecs(otherSpec.getEnricherSpecs())
+                .enrichers(otherSpec.getEnrichers())
+                .addInitializers(otherSpec.getInitializers())
+                .children(otherSpec.getChildren())
+                .members(otherSpec.getMembers())
+                .groups(otherSpec.getGroups())
+                .catalogItemId(otherSpec.getCatalogItemId())
+                .locations(otherSpec.getLocations());
+        
+        if (otherSpec.getParent() != null) parent(otherSpec.getParent());
+        if (otherSpec.getImplementation() != null) impl(otherSpec.getImplementation());
+        
+        return this;
+    }
+
+    @Override
     @SuppressWarnings("unchecked")
     public Class<T> getType() {
         return (Class<T>)super.getType();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java b/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
index 7e8a022..6ba5a3c 100644
--- a/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
+++ b/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
@@ -18,14 +18,19 @@
  */
 package org.apache.brooklyn.api.internal;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import java.io.Serializable;
 import java.lang.reflect.Modifier;
+import java.util.List;
 import java.util.Set;
 
+import org.apache.brooklyn.api.objs.SpecParameter;
 import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 
 import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 
@@ -37,6 +42,7 @@ public abstract class AbstractBrooklynObjectSpec<T,SpecT extends AbstractBrookly
     private String displayName;
     private String catalogItemId;
     private Set<Object> tags = MutableSet.of();
+    private List<SpecParameter<?>> parameters = ImmutableList.of();
 
     protected AbstractBrooklynObjectSpec(Class<? extends T> type) {
         checkValidType(type);
@@ -76,6 +82,11 @@ public abstract class AbstractBrooklynObjectSpec<T,SpecT extends AbstractBrookly
         Iterables.addAll(this.tags, tagsToAdd);
         return self();
     }
+    
+    public SpecT parameters(List<? extends SpecParameter<?>> parameters) {
+        this.parameters = ImmutableList.copyOf(checkNotNull(parameters, "parameters"));
+        return self();
+    }
 
     /**
      * @return The type of the object (or significant interface)
@@ -99,6 +110,11 @@ public abstract class AbstractBrooklynObjectSpec<T,SpecT extends AbstractBrookly
         return ImmutableSet.copyOf(tags);
     }
 
+    /** A list of configuration options that the entity supports. */
+    public final List<SpecParameter<?>> getParameters() {
+        return ImmutableList.copyOf(parameters);
+    }
+
     // TODO Duplicates method in BasicEntityTypeRegistry and InternalEntityFactory.isNewStyleEntity
     protected final void checkIsNewStyleImplementation(Class<?> implClazz) {
         try {
@@ -119,6 +135,13 @@ public abstract class AbstractBrooklynObjectSpec<T,SpecT extends AbstractBrookly
         if (val.isInterface()) throw new IllegalStateException("Implementation "+val+" is an interface, but must be a non-abstract class");
         if (Modifier.isAbstract(val.getModifiers())) throw new IllegalStateException("Implementation "+val+" is abstract, but must be a non-abstract class");
     }
+    
+    protected SpecT copyFrom(SpecT otherSpec) {
+        return displayName(otherSpec.getDisplayName())
+            .tags(otherSpec.getTags())
+            .catalogItemId(otherSpec.getCatalogItemId())
+            .parameters(otherSpec.getParameters());
+    }
 
     @Override
     public boolean equals(Object obj) {
@@ -129,6 +152,7 @@ public abstract class AbstractBrooklynObjectSpec<T,SpecT extends AbstractBrookly
         if (!Objects.equal(getCatalogItemId(), other.getCatalogItemId())) return false;
         if (!Objects.equal(getType(), other.getType())) return false;
         if (!Objects.equal(getTags(), other.getTags())) return false;
+        if (!Objects.equal(getParameters(), other.getParameters())) return false;
         return true;
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java b/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java
new file mode 100644
index 0000000..4e9dc62
--- /dev/null
+++ b/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java
@@ -0,0 +1,32 @@
+/*
+ * 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.api.objs;
+
+import java.io.Serializable;
+
+import org.apache.brooklyn.config.ConfigKey;
+
+public interface SpecParameter<T> extends Serializable {
+    /** Short name, to be used in UI */
+    String getLabel();
+    /** Visible by default in UI, not all inputs may be visible at once */
+    boolean isPinned();
+    /** Type information for the input */
+    ConfigKey<T> getType();
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
index e9f8d34..262e3b4 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
@@ -32,17 +32,18 @@ import javax.annotation.Nullable;
 import org.apache.brooklyn.api.catalog.BrooklynCatalog;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogInput;
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
 import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.objs.SpecParameter;
 import org.apache.brooklyn.core.catalog.CatalogPredicates;
 import org.apache.brooklyn.core.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
 import org.apache.brooklyn.core.location.BasicLocationRegistry;
 import org.apache.brooklyn.core.mgmt.classloading.BrooklynClassLoadingContext;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.objs.BasicSpecParameter;
 import org.apache.brooklyn.core.plan.PlanToSpecFactory;
 import org.apache.brooklyn.core.plan.PlanToSpecTransformer;
 import org.apache.brooklyn.util.collections.MutableList;
@@ -318,12 +319,16 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         if (encounteredTypes.contains(item.getSymbolicName())) {
             throw new IllegalStateException("Type being resolved '"+item.getSymbolicName()+"' has already been encountered in " + encounteredTypes + "; recursive cycle detected");
         }
-        return PlanToSpecFactory.attemptWithLoaders(mgmt, new Function<PlanToSpecTransformer, SpecT>() {
+        Maybe<SpecT> specMaybe = PlanToSpecFactory.attemptWithLoaders(mgmt, new Function<PlanToSpecTransformer, SpecT>() {
             @Override
             public SpecT apply(PlanToSpecTransformer input) {
                 return input.createCatalogSpec(item, encounteredTypes);
             }
-        }).get();
+        });
+        if (specMaybe.isPresent() && !item.getParameters().isEmpty()) {
+            specMaybe.get().parameters(item.getParameters());
+        }
+        return specMaybe.get();
     }
 
     /**
@@ -421,8 +426,8 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         // (this load is required for the scan below and I think also for yaml resolution)
         CatalogUtils.installLibraries(mgmt, libraryBundlesNew);
 
-        List<?> inputsRaw = MutableList.copyOf(getFirstAs(itemMetadata, List.class, "brooklyn.inputs", "inputs").orNull());
-        List<CatalogInput<?>> inputs = CatalogInputDto.ParseYamlInputs.parseInputs(inputsRaw, CatalogUtils.newClassLoadingContext(mgmt, "<catalog_input_parser>", libraryBundles));
+        List<?> parametersRaw = MutableList.copyOf(getFirstAs(itemMetadata, List.class, "brooklyn.parameters", "parameters").orNull());
+        List<SpecParameter<?>> metaParameters = BasicSpecParameter.fromConfigList(parametersRaw, CatalogUtils.newClassLoadingContext(mgmt, "<catalog_input_parser>", libraryBundles));
 
         Boolean scanJavaAnnotations = getFirstAs(itemMetadata, Boolean.class, "scanJavaAnnotations", "scan_java_annotations").orNull();
         if (scanJavaAnnotations==null || !scanJavaAnnotations) {
@@ -571,8 +576,8 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         String sourcePlanYaml = planInterpreter.getPlanYaml();
         
         CatalogItemDtoAbstract<?, ?> dto = createItemBuilder(itemType, symbolicName, version)
-            .inputs(inputs)
             .libraries(libraryBundles)
+            .parameters(getParameters(metaParameters, planInterpreter))
             .displayName(displayName)
             .description(description)
             .deprecated(catalogDeprecated)
@@ -584,6 +589,14 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         result.add(dto);
     }
 
+    private List<SpecParameter<?>> getParameters(List<SpecParameter<?>> metaParameters, PlanInterpreterGuessingType planInterpreter) {
+        if (!metaParameters.isEmpty()) {
+            return metaParameters;
+        } else {
+            return planInterpreter.parameters;
+        }
+    }
+
     private String setFromItemIfUnset(String oldValue, Map<?,?> item, String fieldAttr) {
         if (Strings.isNonBlank(oldValue)) return oldValue;
         if (item!=null) {
@@ -647,6 +660,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         CatalogItemType catalogItemType;
         String planYaml;
         boolean resolved = false;
+        List<SpecParameter<?>> parameters = ImmutableList.of();
         List<Exception> errors = MutableList.of();
         List<Exception> entityErrors = MutableList.of();
         
@@ -684,11 +698,16 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
             if (!resolved && catalogItemType==CatalogItemType.TEMPLATE) {
                 // anything goes, for an explicit template, because we can't easily recurse into the types
                 planYaml = itemYaml;
+                parameters = getItemParameters();
                 resolved = true;
             }
             
             return this;
         }
+
+        private List<SpecParameter<?>> getItemParameters() {
+            return BasicSpecParameter.fromConfigList((List<?>)item.get("brooklyn.parameters"), CatalogUtils.newClassLoadingContext(mgmt, "<template_parameters_parser>", libraryBundles));
+        }
         
         public boolean isResolved() { return resolved; }
         
@@ -734,6 +753,10 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
                             // matched - exit
                             catalogItemType = candidateCiType;
                             planYaml = candidateYaml;
+                            parameters = getItemParameters();
+                            if (parameters.isEmpty()) {
+                                parameters = candidate.getParameters();
+                            }
                             resolved = true;
                             return true;
                         }
@@ -749,10 +772,14 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
                     .libraries(libraryBundles)
                     .build();
                 @SuppressWarnings("unchecked")
-                Object spec = internalCreateSpecAttempting(mgmt, itemToAttempt, MutableSet.<String>of());
+                AbstractBrooklynObjectSpec<?, ?> spec = internalCreateSpecAttempting(mgmt, itemToAttempt, MutableSet.<String>of());
                 if (spec!=null) {
                     catalogItemType = candidateCiType;
                     planYaml = candidateYaml;
+                    parameters = getItemParameters();
+                    if (parameters.isEmpty()) {
+                        parameters = spec.getParameters();
+                    }
                     resolved = true;
                 }
                 return true;
@@ -787,10 +814,11 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
                             .libraries(libraryBundles)
                             .build();
                     @SuppressWarnings("unchecked")
-                    Object cutdownSpec = internalCreateSpecAttempting(mgmt, itemToAttempt, MutableSet.<String>of());
+                    AbstractBrooklynObjectSpec<?, ?> cutdownSpec = internalCreateSpecAttempting(mgmt, itemToAttempt, MutableSet.<String>of());
                     if (cutdownSpec!=null) {
                         catalogItemType = candidateCiType;
                         planYaml = candidateYaml;
+                        parameters = cutdownSpec.getParameters();
                         resolved = true;
                     }
                     return true;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDo.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDo.java
index 7b2fb53..69ee940 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDo.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogClasspathDo.java
@@ -31,14 +31,15 @@ import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.catalog.CatalogItem;
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogInput;
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.ImplementedBy;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.objs.SpecParameter;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.core.entity.factory.ApplicationBuilder;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.objs.BasicSpecParameter;
 import org.apache.brooklyn.util.core.ResourceUtils;
 import org.apache.brooklyn.util.core.javalang.ReflectionScanner;
 import org.apache.brooklyn.util.core.javalang.UrlClassLoader;
@@ -312,7 +313,7 @@ public class CatalogClasspathDo {
         }
         if (log.isTraceEnabled())
             log.trace("adding to catalog: "+c+" (from catalog "+catalog+")");
-        item.setInputs(getJavaTypeInputs(c));
+        item.setParameters(getJavaTypeParameters(c));
         catalog.addEntry(item);
         return item;
     }
@@ -352,8 +353,8 @@ public class CatalogClasspathDo {
         classloader.addFirst(loader);
     }
 
-    private List<CatalogInput<?>> getJavaTypeInputs(Class<?> c) {
-        return CatalogInputDto.ParseClassInputs.parseInputs(c);
+    private List<SpecParameter<?>> getJavaTypeParameters(Class<?> c) {
+        return BasicSpecParameter.fromClass(catalog.mgmt, c);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogInputDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogInputDto.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogInputDto.java
deleted file mode 100644
index c549137..0000000
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogInputDto.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.core.catalog.internal;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.brooklyn.api.catalog.CatalogConfig;
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogInput;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.EntityType;
-import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.core.config.BasicConfigKey;
-import org.apache.brooklyn.core.entity.EntityDynamicType;
-import org.apache.brooklyn.core.mgmt.classloading.BrooklynClassLoadingContext;
-import org.apache.brooklyn.core.objs.BrooklynTypes;
-import org.apache.brooklyn.util.collections.MutableList;
-import org.apache.brooklyn.util.guava.Maybe;
-import org.apache.brooklyn.util.text.StringPredicates;
-
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.reflect.TypeToken;
-
-public class CatalogInputDto<T> implements CatalogInput<T> {
-    private static final String DEFAULT_TYPE = "string";
-    private static final Map<String, Class<?>> BUILT_IN_TYPES = ImmutableMap.<String, Class<?>>builder()
-            .put(DEFAULT_TYPE, String.class)
-            .put("integer", Integer.class)
-            .put("long", Long.class)
-            .put("float", Float.class)
-            .put("double", Double.class)
-            .put("timestamp", Date.class)
-            .build();
-
-    private static final Map<String, Predicate<?>> BUILT_IN_CONSTRAINTS = ImmutableMap.<String, Predicate<?>>of(
-            "required", StringPredicates.isNonBlank());
-
-    private String label;
-    private boolean pinned;
-    private ConfigKey<T> type;
-
-    public CatalogInputDto(String label, boolean pinned, ConfigKey<T> type) {
-        this.label = label;
-        this.pinned = pinned;
-        this.type = type;
-    }
-
-    @Override
-    public String getLabel() {
-        return label;
-    }
-
-    @Override
-    public boolean isPinned() {
-        return pinned;
-    }
-
-    @Override
-    public ConfigKey<T> getType() {
-        return type;
-    }
-
-    public static final class ParseYamlInputs {
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        public static List<CatalogInput<?>> parseInputs(List<?> inputsRaw, BrooklynClassLoadingContext loader) {
-            List<CatalogInput<?>> inputs = new ArrayList<>(inputsRaw.size());
-            for (Object obj : inputsRaw) {
-                Map inputDef;
-                if (obj instanceof String) {
-                    inputDef = ImmutableMap.of("name", obj);
-                } else if (obj instanceof Map) {
-                    inputDef = (Map) obj;
-                } else {
-                    throw new IllegalArgumentException("Catalog input definition expected to be a map, but is " + obj.getClass() + " instead: " + obj);
-                }
-                String name = (String)inputDef.get("name");
-                String label = (String)inputDef.get("label");
-                String description = (String)inputDef.get("description");
-                String type = (String)inputDef.get("type");
-                Object defaultValue = inputDef.get("default");
-                Predicate<?> constraints = parseConstraints(inputDef.get("constraints"), loader);
-
-                if (name == null) {
-                    throw new IllegalArgumentException("'name' value missing from input definition " + obj + " but is required. Check for typos.");
-                }
-
-                ConfigKey inputType = BasicConfigKey.builder(inferType(type, loader))
-                        .name(name)
-                        .description(description)
-                        .defaultValue(defaultValue)
-                        .constraint(constraints)
-                        .build();
-                inputs.add(new CatalogInputDto(Maybe.fromNullable(label).or(name), true, inputType));
-            }
-            return inputs;
-        }
-
-        @SuppressWarnings({ "rawtypes" })
-        private static TypeToken inferType(String typeRaw, BrooklynClassLoadingContext loader) {
-            if (typeRaw == null) return TypeToken.of(String.class);
-            String type = typeRaw.trim();
-            if (BUILT_IN_TYPES.containsKey(type)) {
-                return TypeToken.of(BUILT_IN_TYPES.get(type));
-            } else {
-                // Assume it's a Java type
-                Maybe<Class<?>> inputType = loader.tryLoadClass(type);
-                if (inputType.isPresent()) {
-                    return TypeToken.of(inputType.get());
-                } else {
-                    throw new IllegalArgumentException("The type '" + type + "' for a catalog input not recognised as a built-in (" + BUILT_IN_TYPES.keySet() + ") or a java type");
-                }
-            }
-        }
-    
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        private static Predicate parseConstraints(Object obj, BrooklynClassLoadingContext loader) {
-            List constraintsRaw;
-            if (obj == null) {
-                constraintsRaw = ImmutableList.of();
-            } else if (obj instanceof String) {
-                constraintsRaw = ImmutableList.of(obj);
-            } else if (obj instanceof List) {
-                constraintsRaw = (List) obj;
-            } else {
-                throw new IllegalArgumentException ("The constraint '" + obj + "' for a catalog input is invalid format - string or list supported");
-            }
-            List<Predicate> constraints = new ArrayList(constraintsRaw.size());
-            for (Object untypedConstraint : constraintsRaw) {
-                String constraint = (String)untypedConstraint;
-                if (BUILT_IN_CONSTRAINTS.containsKey(constraint)) {
-                    constraints.add(BUILT_IN_CONSTRAINTS.get(constraint));
-                } else {
-                    throw new IllegalArgumentException("The constraint '" + constraint + "' for a catalog input is not recognized as a built-in (" + BUILT_IN_CONSTRAINTS.keySet() + ")");
-                }
-            }
-            if (!constraints.isEmpty()) {
-                if (constraints.size() == 1) {
-                    return constraints.get(0);
-                } else {
-                    return Predicates.and((List<Predicate<Object>>)(List) constraints);
-                }
-            } else {
-                return Predicates.alwaysTrue();
-            }
-        }
-    }
-
-    public static final class ParseClassInputs {
-        private static final class WeightedCatalogInput {
-            private Double weight;
-            private CatalogInput<?> input;
-            public WeightedCatalogInput(Double weight, CatalogInput<?> input) {
-                this.weight = weight;
-                this.input = input;
-            }
-            public Double getWeight() {return weight; }
-            public CatalogInput<?> getInput() { return input; }
-        }
-        private static final class InputsComparator implements Comparator<WeightedCatalogInput> {
-            @Override
-            public int compare(WeightedCatalogInput o1, WeightedCatalogInput o2) {
-                if (o1.getWeight() == o2.getWeight()) {
-                    return 0;
-                } else if (o1.getWeight() == null) {
-                    return 1;
-                } else if (o2.getWeight() == null) {
-                    return -1;
-                } else {
-                    return Double.compare(o1.getWeight(),  o2.getWeight());
-                }
-            }
-        }
-        private static final class InputsTransformer implements Function<WeightedCatalogInput, CatalogInput<?>> {
-            @Override
-            public CatalogInput<?> apply(WeightedCatalogInput input) {
-                return input.getInput();
-            }
-        }
-
-        public static List<CatalogInput<?>> parseInputs(Class<?> c) {
-            MutableList<WeightedCatalogInput> inputs = MutableList.<WeightedCatalogInput>of();
-            if (Entity.class.isAssignableFrom(c)) {
-                @SuppressWarnings("unchecked")
-                Class<? extends Entity> entityClass = (Class<? extends Entity>) c;
-                EntityDynamicType dynamicType = BrooklynTypes.getDefinedEntityType(entityClass);
-                EntityType type = dynamicType.getSnapshot();
-                for (ConfigKey<?> x: type.getConfigKeys()) {
-                    WeightedCatalogInput fieldConfig = getFieldConfig(x, dynamicType.getConfigKeyField(x.getName()));
-                    inputs.appendIfNotNull(fieldConfig);
-                }
-                Collections.sort(inputs, new InputsComparator());
-                return FluentIterable.from(inputs)
-                        .transform(new InputsTransformer()).toList();
-            } else {
-                return ImmutableList.<CatalogInput<?>>of();
-            }
-        }
-
-        public static WeightedCatalogInput getFieldConfig(ConfigKey<?> config, Field configKeyField) {
-            if (configKeyField == null) return null;
-            CatalogConfig catalogConfig = configKeyField.getAnnotation(CatalogConfig.class);
-            String label = config.getName();
-            Double priority = null;
-            if (catalogConfig != null) {
-                label = Maybe.fromNullable(catalogConfig.label()).or(config.getName());
-                priority = catalogConfig.priority();
-            }
-            @SuppressWarnings({ "unchecked", "rawtypes" })
-            CatalogInput<?> input = new CatalogInputDto(label, priority != null, config);
-            return new WeightedCatalogInput(priority, input);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemBuilder.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemBuilder.java
index f1e4711..1f9b9a2 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemBuilder.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemBuilder.java
@@ -23,7 +23,7 @@ import java.util.Collections;
 import java.util.List;
 
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogInput;
+import org.apache.brooklyn.api.objs.SpecParameter;
 
 import com.google.common.base.Preconditions;
 
@@ -106,8 +106,8 @@ public class CatalogItemBuilder<CatalogItemType extends CatalogItemDtoAbstract<?
         return this;
     }
 
-    public CatalogItemBuilder<CatalogItemType> inputs(List<CatalogInput<?>> inputs) {
-        dto.setInputs(inputs);
+    public CatalogItemBuilder<CatalogItemType> parameters(List<SpecParameter<?>> inputs) {
+        dto.setParameters(inputs);
         return this;
     }
 
@@ -125,8 +125,8 @@ public class CatalogItemBuilder<CatalogItemType extends CatalogItemDtoAbstract<?
         Preconditions.checkNotNull(dto.getSymbolicName());
         Preconditions.checkNotNull(dto.getVersion());
 
-        if (dto.getInputs() == null) {
-            dto.setInputs(Collections.<CatalogInput<?>>emptyList());
+        if (dto.getParameters() == null) {
+            dto.setParameters(Collections.<SpecParameter<?>>emptyList());
         }
         if (dto.getLibraries() == null) {
             dto.setLibraries(Collections.<CatalogBundle>emptyList());

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java
index e053374..4224cb9 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDo.java
@@ -28,6 +28,7 @@ import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.mgmt.rebind.RebindSupport;
 import org.apache.brooklyn.api.mgmt.rebind.mementos.CatalogItemMemento;
+import org.apache.brooklyn.api.objs.SpecParameter;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
 import org.apache.brooklyn.core.relations.EmptyRelationSupport;
@@ -179,8 +180,8 @@ public class CatalogItemDo<T,SpecT> implements CatalogItem<T,SpecT>, BrooklynObj
     }
 
     @Override
-    public List<CatalogInput<?>> getInputs() {
-        return itemDto.getInputs();
+    public List<SpecParameter<?>> getParameters() {
+        return itemDto.getParameters();
     }
 
     @Nonnull  // but it is still null sometimes, see in CatalogDo.loadJavaClass

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java
index e95b0dd..d891ae1 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogItemDtoAbstract.java
@@ -18,6 +18,8 @@
  */
 package org.apache.brooklyn.core.catalog.internal;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -30,6 +32,7 @@ import javax.annotation.Nullable;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.mgmt.rebind.RebindSupport;
 import org.apache.brooklyn.api.mgmt.rebind.mementos.CatalogItemMemento;
+import org.apache.brooklyn.api.objs.SpecParameter;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.mgmt.rebind.BasicCatalogItemRebindSupport;
 import org.apache.brooklyn.core.objs.AbstractBrooklynObject;
@@ -62,7 +65,7 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
     private @Deprecated @SetFromFlag String type;
     private @SetFromFlag String planYaml;
 
-    private @SetFromFlag List<CatalogInput<?>> inputs;
+    private @SetFromFlag List<SpecParameter<?>> parameters = ImmutableList.of();
     private @SetFromFlag Collection<CatalogBundle> libraries;
     private @SetFromFlag Set<Object> tags = Sets.newLinkedHashSet();
     private @SetFromFlag boolean deprecated;
@@ -176,8 +179,13 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
     }
     
     @Override
-    public List<CatalogInput<?>> getInputs() {
-        return inputs;
+    public List<SpecParameter<?>> getParameters() {
+        if (parameters != null) {
+            return parameters;
+        } else {
+            // Needed for the case when created by xstream
+            return ImmutableList.of();
+        }
     }
 
     @Nonnull
@@ -197,7 +205,7 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(symbolicName, planYaml, javaType, nullIfEmpty(inputs), nullIfEmpty(libraries), version, getCatalogItemId());
+        return Objects.hashCode(symbolicName, planYaml, javaType, nullIfEmpty(parameters), nullIfEmpty(libraries), version, getCatalogItemId());
     }
 
     @Override
@@ -209,7 +217,7 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
         if (!Objects.equal(symbolicName, other.symbolicName)) return false;
         if (!Objects.equal(planYaml, other.planYaml)) return false;
         if (!Objects.equal(javaType, other.javaType)) return false;
-        if (!Objects.equal(nullIfEmpty(inputs), nullIfEmpty(other.inputs))) return false;
+        if (!Objects.equal(nullIfEmpty(parameters), nullIfEmpty(other.parameters))) return false;
         if (!Objects.equal(nullIfEmpty(libraries), nullIfEmpty(other.libraries))) return false;
         if (!Objects.equal(getCatalogItemId(), other.getCatalogItemId())) return false;
         if (!Objects.equal(version, other.version)) return false;
@@ -382,8 +390,8 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
         this.planYaml = planYaml;
     }
 
-    protected void setInputs(List<CatalogInput<?>> inputs) {
-        this.inputs = inputs;
+    protected void setParameters(List<SpecParameter<?>> parameters) {
+        this.parameters = checkNotNull(parameters, "parameters");
     }
 
     protected void setLibraries(Collection<CatalogBundle> libraries) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/core/src/main/java/org/apache/brooklyn/core/catalog/internal/JavaCatalogToSpecTransformer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/JavaCatalogToSpecTransformer.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/JavaCatalogToSpecTransformer.java
index 7df9adf..519bb23 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/JavaCatalogToSpecTransformer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/JavaCatalogToSpecTransformer.java
@@ -29,6 +29,7 @@ import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.api.typereg.RegisteredType;
+import org.apache.brooklyn.core.objs.BasicSpecParameter;
 import org.apache.brooklyn.core.plan.PlanNotRecognizedException;
 import org.apache.brooklyn.core.plan.PlanToSpecTransformer;
 import org.apache.brooklyn.util.exceptions.Exceptions;
@@ -71,29 +72,32 @@ public class JavaCatalogToSpecTransformer implements PlanToSpecTransformer {
     @Override
     public <T, SpecT extends AbstractBrooklynObjectSpec<? extends T, SpecT>> SpecT createCatalogSpec(
             CatalogItem<T, SpecT> item, Set<String> encounteredTypes) throws PlanNotRecognizedException {
-        if (item.getJavaType() != null) {
+        @SuppressWarnings("deprecation")
+        String javaType = item.getJavaType();
+        if (javaType != null) {
             log.warn("Deprecated functionality (since 0.9.0). Using old-style xml catalog items with java type attribute for " + item);
             Class<?> type;
             try {
                 // java types were deprecated before we added osgi support so this isn't necessary,
                 // but it doesn't hurt (and if we re-instate a class+bundle approach for RegisteredType 
                 // we will want to do this)
-                type = CatalogUtils.newClassLoadingContext(mgmt, item).loadClass(item.getJavaType());
+                type = CatalogUtils.newClassLoadingContext(mgmt, item).loadClass(javaType);
             } catch (Exception e) {
                 Exceptions.propagateIfFatal(e);
-                throw new IllegalStateException("Unable to load old-style java catalog item type " + item.getJavaType() + " for item " + item, e);
+                throw new IllegalStateException("Unable to load old-style java catalog item type " + javaType + " for item " + item, e);
             }
             AbstractBrooklynObjectSpec<?,?> spec;
             if (Entity.class.isAssignableFrom(type)) {
                 @SuppressWarnings("unchecked")
                 Class<Entity> entityType = (Class<Entity>)type;
-                spec = EntitySpec.create(entityType);
+                spec = EntitySpec.create(entityType)
+                        .parameters(BasicSpecParameter.fromClass(mgmt, entityType));
             } else if (Policy.class.isAssignableFrom(type)) {
                 @SuppressWarnings("unchecked")
                 Class<Policy> policyType = (Class<Policy>)type;
                 spec = PolicySpec.create(policyType);
             } else {
-                throw new IllegalStateException("Catalog item " + item + " java type " + item.getJavaType() + " is not a Brooklyn supported object.");
+                throw new IllegalStateException("Catalog item " + item + " java type " + javaType + " is not a Brooklyn supported object.");
             }
             spec.catalogItemId(item.getCatalogItemId());
             @SuppressWarnings("unchecked")

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
index 09b362d..36b425f 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
@@ -44,6 +44,7 @@ import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.api.mgmt.rebind.RebindSupport;
 import org.apache.brooklyn.api.mgmt.rebind.mementos.EntityMemento;
 import org.apache.brooklyn.api.objs.EntityAdjunct;
+import org.apache.brooklyn.api.objs.SpecParameter;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
@@ -95,6 +96,7 @@ import org.apache.brooklyn.util.core.flags.FlagUtils;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.core.task.DeferredSupplier;
 import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.guava.TypeTokens;
 import org.apache.brooklyn.util.javalang.Equals;
 import org.apache.brooklyn.util.text.Strings;
 import org.apache.commons.lang3.builder.EqualsBuilder;
@@ -152,8 +154,10 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
     public static final BasicNotificationSensor<Location> LOCATION_REMOVED = new BasicNotificationSensor<Location>(
             Location.class, "entity.location.removed", "Location dynamically removed from entity");
 
+    @SuppressWarnings("rawtypes")
     public static final BasicNotificationSensor<Sensor> SENSOR_ADDED = new BasicNotificationSensor<Sensor>(Sensor.class,
             "entity.sensor.added", "Sensor dynamically added to entity");
+    @SuppressWarnings("rawtypes")
     public static final BasicNotificationSensor<Sensor> SENSOR_REMOVED = new BasicNotificationSensor<Sensor>(Sensor.class,
             "entity.sensor.removed", "Sensor dynamically removed from entity");
 
@@ -164,6 +168,13 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
     public static final BasicNotificationSensor<String> EFFECTOR_CHANGED = new BasicNotificationSensor<String>(String.class,
             "entity.effector.changed", "Effector dynamically changed on entity");
 
+    @SuppressWarnings("rawtypes")
+    public static final BasicNotificationSensor<ConfigKey> CONFIG_KEY_ADDED = new BasicNotificationSensor<ConfigKey>(ConfigKey.class,
+            "entity.config_key.added", "ConfigKey dynamically added to entity");
+    @SuppressWarnings("rawtypes")
+    public static final BasicNotificationSensor<ConfigKey> CONFIG_KEY_REMOVED = new BasicNotificationSensor<ConfigKey>(ConfigKey.class,
+            "entity.config_key.removed", "ConfigKey dynamically removed from entity");
+
     public static final BasicNotificationSensor<PolicyDescriptor> POLICY_ADDED = new BasicNotificationSensor<PolicyDescriptor>(PolicyDescriptor.class,
             "entity.policy.added", "Policy dynamically added to entity");
     public static final BasicNotificationSensor<PolicyDescriptor> POLICY_REMOVED = new BasicNotificationSensor<PolicyDescriptor>(PolicyDescriptor.class,
@@ -387,6 +398,14 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
         return this;
     }
 
+    /**
+     * Adds the config keys to the entity dynamic type
+     * @since 0.9.0
+     */
+    public void configure(Iterable<ConfigKey<?>> configKeys) {
+        entityType.addConfigKeys(configKeys);
+    }
+
     @Override
     public int hashCode() {
         return getId().hashCode();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/core/src/main/java/org/apache/brooklyn/core/entity/EntityDynamicType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/EntityDynamicType.java b/core/src/main/java/org/apache/brooklyn/core/entity/EntityDynamicType.java
index 3634f73..69e8caa 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/EntityDynamicType.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/EntityDynamicType.java
@@ -30,6 +30,7 @@ import org.apache.brooklyn.api.effector.Effector;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntityType;
 import org.apache.brooklyn.api.sensor.Sensor;
+import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
 import org.apache.brooklyn.core.effector.EffectorAndBody;
 import org.apache.brooklyn.core.effector.EffectorBody;
@@ -97,6 +98,7 @@ public class EntityDynamicType extends BrooklynDynamicType<Entity, AbstractEntit
         return super.getBrooklynClass();
     }
     
+    @Override
     public EntityType getSnapshot() {
         return (EntityType) super.getSnapshot();
     }
@@ -232,6 +234,41 @@ public class EntityDynamicType extends BrooklynDynamicType<Entity, AbstractEntit
     
     // --------------------------------------------------
     
+    /**
+     * Adds the given {@link ConfigKey} to this entity.
+     */
+    public void addConfigKey(ConfigKey<?> newKey) {
+        configKeys.put(newKey.getName(), new FieldAndValue<ConfigKey<?>>(null, newKey));
+        invalidateSnapshot();
+        instance.sensors().emit(AbstractEntity.CONFIG_KEY_ADDED, newKey);
+    }
+    
+    /**
+     * Adds the given {@link ConfigKey} to this entity.
+     */
+    public void addConfigKeys(Iterable<ConfigKey<?>> newKeys) {
+        for (ConfigKey<?> newKey : newKeys) {
+            addConfigKey(newKey);
+        }
+    }
+
+    /**
+     * Removes the named {@link ConfigKey} from this entity.
+     */
+    public boolean removeConfigKey(ConfigKey<?> key) {
+        FieldAndValue<ConfigKey<?>> result = configKeys.remove(key.getName());
+        if (result != null) {
+            invalidateSnapshot();
+            ConfigKey<?> removedKey = result.value;
+            instance.sensors().emit(AbstractEntity.CONFIG_KEY_REMOVED, removedKey);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    // --------------------------------------------------
+    
     @Override
     protected EntityTypeSnapshot newSnapshot() {
         return new EntityTypeSnapshot(name, value(configKeys), sensors, effectors.values());

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
index 9c4ebc1..4081177 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
@@ -59,6 +59,7 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.Runnables;
 
 /** Utility methods for working with entities and applications */
 public class EntityManagementUtils {
@@ -215,7 +216,7 @@ public class EntityManagementUtils {
             } else {
                 // include a task, just to give feedback in the GUI
                 taskS.add(Tasks.builder().displayName("create").description("Skipping start (not a Startable Entity)")
-                    .body(new Runnable() { public void run() {} })
+                    .body(Runnables.doNothing())
                     .tag(BrooklynTaskTags.tagForTargetEntity(child))
                     .build());
             }
@@ -250,6 +251,9 @@ public class EntityManagementUtils {
             wrappedChild.displayName(wrapperParent.getDisplayName());
         if (!wrapperParent.getLocations().isEmpty())
             wrappedChild.locations(wrapperParent.getLocations());
+        if (!wrapperParent.getParameters().isEmpty()) {
+            wrappedChild.parameters(wrapperParent.getParameters());
+        }
 
         // NB: this clobbers child config; might prefer to deeply merge maps etc
         // (but this should not be surprising, as unwrapping is often parameterising the nested blueprint, so outer config should dominate) 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java b/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
new file mode 100644
index 0000000..d2703ff
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
@@ -0,0 +1,322 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.objs;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.api.catalog.CatalogConfig;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.objs.BrooklynObject;
+import org.apache.brooklyn.api.objs.BrooklynType;
+import org.apache.brooklyn.api.objs.SpecParameter;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigKey;
+import org.apache.brooklyn.core.mgmt.classloading.BrooklynClassLoadingContext;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.text.StringPredicates;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+
+public class BasicSpecParameter<T> implements SpecParameter<T>{
+    private static final long serialVersionUID = -4728186276307619778L;
+
+    private String label;
+    private boolean pinned;
+    private ConfigKey<T> type;
+
+    public BasicSpecParameter(String label, boolean pinned, ConfigKey<T> type) {
+        this.label = label;
+        this.pinned = pinned;
+        this.type = type;
+    }
+
+    @Override
+    public String getLabel() {
+        return label;
+    }
+
+    @Override
+    public boolean isPinned() {
+        return pinned;
+    }
+
+    @Override
+    public ConfigKey<T> getType() {
+        return type;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(label, pinned, type);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        BasicSpecParameter<?> other = (BasicSpecParameter<?>) obj;
+        return Objects.equal(label,  other.label) &&
+                pinned == other.pinned &&
+                Objects.equal(type, other.type);
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("label", label)
+                .add("pinned", pinned)
+                .add("type", type)
+                .toString();
+    }
+
+    // Not CAMP specific since it's used to get the parameters from catalog items meta which are syntax independent.
+    public static List<SpecParameter<?>> fromConfigList(List<?> obj, BrooklynClassLoadingContext loader) {
+        return ParseYamlInputs.parseParameters(obj, loader);
+    }
+
+    public static List<SpecParameter<?>> fromClass(ManagementContext mgmt, Class<?> type) {
+        return ParseClassParameters.parseParameters(getImplementedBy(mgmt, type));
+    }
+
+    public static List<SpecParameter<?>> fromSpec(ManagementContext mgmt, AbstractBrooklynObjectSpec<?, ?> spec) {
+        if (!spec.getParameters().isEmpty()) {
+            return spec.getParameters();
+        }
+        Class<?> type = null;
+        if (spec instanceof EntitySpec) {
+            EntitySpec<?> entitySpec = (EntitySpec<?>)spec;
+            if (entitySpec.getImplementation() != null) {
+                type = entitySpec.getImplementation();
+            }
+        }
+        if (type == null) {
+            type = getImplementedBy(mgmt, spec.getType());
+        }
+        return ParseClassParameters.parseParameters(getImplementedBy(mgmt, type));
+    }
+
+    private static Class<?> getImplementedBy(ManagementContext mgmt, Class<?> type) {
+        if (Entity.class.isAssignableFrom(type) && type.isInterface()) {
+            try {
+                @SuppressWarnings("unchecked")
+                Class<? extends Entity> uncheckedType = ParseClassParameters.getEntityImplementedBy(mgmt, (Class<Entity>)type);
+                return uncheckedType;
+            } catch(IllegalArgumentException e) {
+                // NOP, no implementation for type, use whatever we have
+            }
+        }
+        return type;
+    }
+
+    private static final class ParseYamlInputs {
+        private static final String DEFAULT_TYPE = "string";
+        private static final Map<String, Class<?>> BUILT_IN_TYPES = ImmutableMap.<String, Class<?>>builder()
+                .put(DEFAULT_TYPE, String.class)
+                .put("integer", Integer.class)
+                .put("long", Long.class)
+                .put("float", Float.class)
+                .put("double", Double.class)
+                .put("timestamp", Date.class)
+                .build();
+
+        private static final Map<String, Predicate<?>> BUILT_IN_CONSTRAINTS = ImmutableMap.<String, Predicate<?>>of(
+                "required", StringPredicates.isNonBlank());
+
+        public static List<SpecParameter<?>> parseParameters(List<?> inputsRaw, BrooklynClassLoadingContext loader) {
+            if (inputsRaw == null) return ImmutableList.of();
+            List<SpecParameter<?>> inputs = new ArrayList<>(inputsRaw.size());
+            for (Object obj : inputsRaw) {
+                inputs.add(parseParameter(obj, loader));
+            }
+            return inputs;
+        }
+
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        private static SpecParameter<?> parseParameter(Object obj, BrooklynClassLoadingContext loader) {
+            Map inputDef;
+            if (obj instanceof String) {
+                inputDef = ImmutableMap.of("name", obj);
+            } else if (obj instanceof Map) {
+                inputDef = (Map) obj;
+            } else {
+                throw new IllegalArgumentException("Catalog input definition expected to be a map, but is " + obj.getClass() + " instead: " + obj);
+            }
+            String name = (String)inputDef.get("name");
+            String label = (String)inputDef.get("label");
+            String description = (String)inputDef.get("description");
+            String type = (String)inputDef.get("type");
+            Object defaultValue = inputDef.get("default");
+            Predicate<?> constraints = parseConstraints(inputDef.get("constraints"), loader);
+
+            if (name == null) {
+                throw new IllegalArgumentException("'name' value missing from input definition " + obj + " but is required. Check for typos.");
+            }
+
+            ConfigKey inputType = BasicConfigKey.builder(inferType(type, loader))
+                    .name(name)
+                    .description(description)
+                    .defaultValue(defaultValue)
+                    .constraint(constraints)
+                    .build();
+            return new BasicSpecParameter(Objects.firstNonNull(label, name), true, inputType);
+        }
+
+        @SuppressWarnings({ "rawtypes" })
+        private static TypeToken inferType(String typeRaw, BrooklynClassLoadingContext loader) {
+            if (typeRaw == null) return TypeToken.of(String.class);
+            String type = typeRaw.trim();
+            if (BUILT_IN_TYPES.containsKey(type)) {
+                return TypeToken.of(BUILT_IN_TYPES.get(type));
+            } else {
+                // Assume it's a Java type
+                Maybe<Class<?>> inputType = loader.tryLoadClass(type);
+                if (inputType.isPresent()) {
+                    return TypeToken.of(inputType.get());
+                } else {
+                    throw new IllegalArgumentException("The type '" + type + "' for a catalog input not recognised as a built-in (" + BUILT_IN_TYPES.keySet() + ") or a java type");
+                }
+            }
+        }
+    
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        private static Predicate parseConstraints(Object obj, BrooklynClassLoadingContext loader) {
+            List constraintsRaw;
+            if (obj == null) {
+                constraintsRaw = ImmutableList.of();
+            } else if (obj instanceof String) {
+                constraintsRaw = ImmutableList.of(obj);
+            } else if (obj instanceof List) {
+                constraintsRaw = (List) obj;
+            } else {
+                throw new IllegalArgumentException ("The constraint '" + obj + "' for a catalog input is invalid format - string or list supported");
+            }
+            List<Predicate> constraints = new ArrayList(constraintsRaw.size());
+            for (Object untypedConstraint : constraintsRaw) {
+                String constraint = (String)untypedConstraint;
+                if (BUILT_IN_CONSTRAINTS.containsKey(constraint)) {
+                    constraints.add(BUILT_IN_CONSTRAINTS.get(constraint));
+                } else {
+                    throw new IllegalArgumentException("The constraint '" + constraint + "' for a catalog input is not recognized as a built-in (" + BUILT_IN_CONSTRAINTS.keySet() + ")");
+                }
+            }
+            if (!constraints.isEmpty()) {
+                if (constraints.size() == 1) {
+                    return constraints.get(0);
+                } else {
+                    return Predicates.and((List<Predicate<Object>>)(List) constraints);
+                }
+            } else {
+                return Predicates.alwaysTrue();
+            }
+        }
+    }
+
+    private static final class ParseClassParameters {
+        private static final class WeightedParameter {
+            private Double weight;
+            private SpecParameter<?> input;
+            public WeightedParameter(Double weight, SpecParameter<?> input) {
+                this.weight = weight;
+                this.input = input;
+            }
+            public Double getWeight() {return weight; }
+            public SpecParameter<?> getInput() { return input; }
+        }
+        private static final class WeightedParameterComparator implements Comparator<WeightedParameter> {
+            @Override
+            public int compare(WeightedParameter o1, WeightedParameter o2) {
+                if (o1.getWeight() == o2.getWeight()) {
+                    return 0;
+                } else if (o1.getWeight() == null) {
+                    return 1;
+                } else if (o2.getWeight() == null) {
+                    return -1;
+                } else {
+                    return Double.compare(o1.getWeight(),  o2.getWeight());
+                }
+            }
+        }
+        private static final class SpecParameterTransformer implements Function<WeightedParameter, SpecParameter<?>> {
+            @Override
+            public SpecParameter<?> apply(WeightedParameter input) {
+                return input.getInput();
+            }
+        }
+
+        public static List<SpecParameter<?>> parseParameters(Class<?> c) {
+            MutableList<WeightedParameter> parameters = MutableList.<WeightedParameter>of();
+            if (BrooklynObject.class.isAssignableFrom(c)) {
+                @SuppressWarnings("unchecked")
+                Class<? extends BrooklynObject> brooklynClass = (Class<? extends BrooklynObject>) c;
+                BrooklynDynamicType<?, ?> dynamicType = BrooklynTypes.getDefinedBrooklynType(brooklynClass);
+                BrooklynType type = dynamicType.getSnapshot();
+                for (ConfigKey<?> x: type.getConfigKeys()) {
+                    WeightedParameter fieldConfig = getFieldConfig(x, dynamicType.getConfigKeyField(x.getName()));
+                    parameters.appendIfNotNull(fieldConfig);
+                }
+                Collections.sort(parameters, new WeightedParameterComparator());
+                return FluentIterable.from(parameters)
+                        .transform(new SpecParameterTransformer()).toList();
+            } else {
+                return ImmutableList.of();
+            }
+        }
+
+        public static WeightedParameter getFieldConfig(ConfigKey<?> config, Field configKeyField) {
+            if (configKeyField == null) return null;
+            CatalogConfig catalogConfig = configKeyField.getAnnotation(CatalogConfig.class);
+            String label = config.getName();
+            Double priority = null;
+            if (catalogConfig != null) {
+                label = Maybe.fromNullable(catalogConfig.label()).or(config.getName());
+                priority = catalogConfig.priority();
+            }
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            SpecParameter<?> param = new BasicSpecParameter(label, priority != null, config);
+            return new WeightedParameter(priority, param);
+        }
+
+        private static <T extends Entity> Class<? extends T> getEntityImplementedBy(ManagementContext mgmt, Class<T> type) {
+            return mgmt.getEntityManager().getEntityTypeRegistry().getImplementedBy(type);
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
index 9552583..3b0795b 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
@@ -23,6 +23,7 @@ import static com.google.common.base.Preconditions.checkState;
 
 import java.lang.reflect.InvocationTargetException;
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 import java.util.Queue;
 import java.util.Set;
@@ -33,6 +34,7 @@ import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.entity.EntityTypeRegistry;
 import org.apache.brooklyn.api.entity.Group;
+import org.apache.brooklyn.api.objs.SpecParameter;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.api.sensor.Enricher;
@@ -46,6 +48,7 @@ import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.policy.AbstractPolicy;
+import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
@@ -243,6 +246,7 @@ public class InternalEntityFactory extends InternalFactory {
             }
             
             entity.tags().addTags(spec.getTags());
+            ((AbstractEntity)entity).configure(getConfigKeysFromSpecParameters(spec));
             ((AbstractEntity)entity).configure(MutableMap.copyOf(spec.getFlags()));
             
             for (Map.Entry<ConfigKey<?>, Object> entry : spec.getConfig().entrySet()) {
@@ -262,6 +266,14 @@ public class InternalEntityFactory extends InternalFactory {
         }
     }
 
+    private <T extends Entity> List<ConfigKey<?>> getConfigKeysFromSpecParameters(EntitySpec<T> spec) {
+        List<ConfigKey<?>> configKeys = MutableList.of();
+        for (SpecParameter<?> param : spec.getParameters()) {
+            configKeys.add(param.getType());
+        }
+        return configKeys;
+    }
+
     /**
      * Calls {@link ConfigConstraints#assertValid(Entity)} on the given entity and all of
      * its descendants.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java b/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
index f59814e..85c9537 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
@@ -22,9 +22,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
 import static org.apache.brooklyn.util.groovy.GroovyJavaMethods.elvis;
 import static org.apache.brooklyn.util.groovy.GroovyJavaMethods.truth;
 
-import groovy.lang.Closure;
-import groovy.lang.GroovyObject;
-
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
@@ -36,6 +33,7 @@ import java.util.NoSuchElementException;
 import java.util.Set;
 
 import org.apache.brooklyn.api.objs.Configurable;
+import org.apache.brooklyn.api.objs.SpecParameter;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
 import org.apache.brooklyn.util.core.config.ConfigBag;
@@ -53,6 +51,9 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 
+import groovy.lang.Closure;
+import groovy.lang.GroovyObject;
+
 
 /** class to help transfer values passed as named arguments to other well-known variables/fields/objects;
  * see the test case for example usage */
@@ -229,17 +230,30 @@ public class FlagUtils {
         return output;
     }
 
+    /** gets all the keys in the given config bag which are applicable to the given list of parameters */
+    public static List<FlagConfigKeyAndValueRecord> findAllParameterConfigKeys(List<SpecParameter<?>> parameters, ConfigBag input) {
+        List<FlagConfigKeyAndValueRecord> output = new ArrayList<FlagUtils.FlagConfigKeyAndValueRecord>();
+        for (SpecParameter<?> param : parameters) {
+            FlagConfigKeyAndValueRecord record = getFlagConfigKeyRecord(null, param.getType(), input);
+            if (record.isValuePresent())
+                output.add(record);
+        }
+        return output;
+    }
+
     /** returns the flag/config-key record for the given input */
     private static FlagConfigKeyAndValueRecord getFlagConfigKeyRecord(Field f, ConfigKey<?> key, ConfigBag input) {
         FlagConfigKeyAndValueRecord result = new FlagConfigKeyAndValueRecord(); 
         result.configKey = key;
         if (key!=null && input.containsKey(key))
             result.configKeyValue = Maybe.<Object>of(input.getStringKey(key.getName()));
-        SetFromFlag flag = f.getAnnotation(SetFromFlag.class);
-        if (flag!=null) {
-            result.flagName = flag.value();
-            if (input.containsKey(flag.value()))
-                result.flagValue = Maybe.of(input.getStringKey(flag.value()));
+        if (f != null) {
+            SetFromFlag flag = f.getAnnotation(SetFromFlag.class);
+            if (flag!=null) {
+                result.flagName = flag.value();
+                if (input.containsKey(flag.value()))
+                    result.flagValue = Maybe.of(input.getStringKey(flag.value()));
+            }
         }
         return result;
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogInputDtoClassTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogInputDtoClassTest.java b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogInputDtoClassTest.java
deleted file mode 100644
index 7602c2a..0000000
--- a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogInputDtoClassTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.core.catalog.internal;
-
-import static org.testng.Assert.assertEquals;
-
-import java.util.List;
-
-import org.apache.brooklyn.api.catalog.CatalogConfig;
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogInput;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.core.config.ConfigKeys;
-import org.testng.annotations.Test;
-
-import com.google.common.base.Predicate;
-import com.google.common.reflect.TypeToken;
-
-public class CatalogInputDtoClassTest {
-    public interface CatalogInputTestEntity extends Entity {
-        @CatalogConfig(label="String Key", priority=3)
-        ConfigKey<String> STRING_KEY = ConfigKeys.newStringConfigKey("string_key");
-
-        @CatalogConfig(label="Integer Key", priority=2)
-        ConfigKey<Integer> INTEGER_KEY = ConfigKeys.newIntegerConfigKey("integer_key");
-
-        @SuppressWarnings("serial")
-        @CatalogConfig(label="Predicate Key", priority=1)
-        ConfigKey<Predicate<String>> PREDICATE_KEY = ConfigKeys.newConfigKey(new TypeToken<Predicate<String>>() {}, "predicate_key");
-
-        ConfigKey<String> UNPINNNED_KEY = ConfigKeys.newStringConfigKey("unpinned_key");
-    }
-
-    @Test
-    public void testFullDefinition() {
-        List<CatalogInput<?>> inputs = CatalogInputDto.ParseClassInputs.parseInputs(CatalogInputTestEntity.class);
-        assertInput(inputs.get(0), "Predicate Key", true, CatalogInputTestEntity.PREDICATE_KEY);
-        assertInput(inputs.get(1), "Integer Key", true, CatalogInputTestEntity.INTEGER_KEY);
-        assertInput(inputs.get(2), "String Key", true, CatalogInputTestEntity.STRING_KEY);
-        assertInput(inputs.get(3), "unpinned_key", false, CatalogInputTestEntity.UNPINNNED_KEY);
-    }
-
-    private void assertInput(CatalogInput<?> input, String label, boolean pinned, ConfigKey<?> type) {
-        assertEquals(input.getLabel(), label);
-        assertEquals(input.isPinned(), pinned);
-        assertEquals(input.getType(), type);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0149bf09/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogInputDtoYamlTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogInputDtoYamlTest.java b/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogInputDtoYamlTest.java
deleted file mode 100644
index 6e65904..0000000
--- a/core/src/test/java/org/apache/brooklyn/core/catalog/internal/CatalogInputDtoYamlTest.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.core.catalog.internal;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-
-import java.util.List;
-
-import org.apache.brooklyn.api.catalog.CatalogItem.CatalogInput;
-import org.apache.brooklyn.api.mgmt.ManagementContext;
-import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.core.mgmt.classloading.BrooklynClassLoadingContext;
-import org.apache.brooklyn.core.mgmt.classloading.JavaBrooklynClassLoadingContext;
-import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
-import org.apache.brooklyn.util.text.StringPredicates;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-import com.google.common.reflect.TypeToken;
-
-public class CatalogInputDtoYamlTest {
-    private ManagementContext mgmt;
-
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() {
-        mgmt = LocalManagementContextForTests.newInstance();
-    }
-
-    @Test
-    public void testInlineName() {
-        String name = "minRam";
-        CatalogInput<?> input = parse(name);
-        assertEquals(input.getLabel(), name);
-        assertTrue(input.isPinned());
-        ConfigKey<?> type = input.getType();
-        assertEquals(type.getName(), name);
-        assertEquals(type.getTypeToken(), TypeToken.of(String.class));
-        assertNull(type.getDefaultValue());
-        assertNull(type.getDescription());
-        assertNull(type.getInheritance());
-        assertConstraint(type.getConstraint(), Predicates.alwaysTrue());
-    }
-
-    @Test
-    public void testOnlyName() {
-        String name = "minRam";
-        CatalogInput<?> input = parse(ImmutableMap.of("name", name));
-        assertEquals(input.getLabel(), name);
-        assertEquals(input.getType().getName(), name);
-        assertEquals(input.getType().getTypeToken(), TypeToken.of(String.class));
-    }
-
-    @Test
-    public void testUnusualName() {
-        parse(ImmutableMap.of("name", "name with spaces"));
-    }
-
-    @Test
-    public void testFullDefinition() {
-        String name = "minRam";
-        String label = "Minimum Ram";
-        String description = "Some description";
-        String inputType = "string";
-        String defaultValue = "VALUE";
-        String constraint = "required";
-        CatalogInput<?> input = parse(ImmutableMap.builder()
-                .put("name", name)
-                .put("label", label)
-                .put("description", description)
-                .put("type", inputType)
-                .put("default", defaultValue)
-                .put("constraints", constraint)
-                .build());
-
-        assertEquals(input.getLabel(), label);
-        assertTrue(input.isPinned());
-
-        ConfigKey<?> type = input.getType();
-        assertEquals(type.getName(), name);
-        assertEquals(type.getTypeToken(), TypeToken.of(String.class));
-        assertEquals(type.getDefaultValue(), defaultValue);
-        assertEquals(type.getDescription(), description);
-        assertNull(type.getInheritance());
-        assertConstraint(type.getConstraint(), StringPredicates.isNonBlank());
-    }
-
-    @Test
-    public void testUnexpectedType() {
-        String name = "1234";
-        String label = "1234";
-        String description = "5678.56";
-        String defaultValue = "444.12";
-        CatalogInput<?> input = parse(ImmutableMap.of(
-                "name", name,
-                "label", label,
-                "description", description,
-                "default", defaultValue));
-
-        assertEquals(input.getLabel(), name);
-        assertTrue(input.isPinned());
-
-        ConfigKey<?> type = input.getType();
-        assertEquals(type.getName(), name);
-        assertEquals(type.getDefaultValue(), defaultValue);
-        assertEquals(type.getDescription(), description);
-        assertNull(type.getInheritance());
-    }
-
-    @Test
-    public void testConstraintAsArray() {
-        String name = "minRam";
-        String constraint = "required";
-        CatalogInput<?> input = parse(ImmutableMap.of(
-                "name", name,
-                "constraints", ImmutableList.of(constraint)));
-        ConfigKey<?> type = input.getType();
-        assertConstraint(type.getConstraint(), StringPredicates.isNonBlank());
-    }
-
-    @Test(expectedExceptions = IllegalArgumentException.class)
-    public void testMissingName() {
-        parse(ImmutableMap.of(
-                "type", "string"));
-    }
-
-    @Test
-    public void testJavaType() {
-        String name = "minRam";
-        CatalogInput<?> input = parse(ImmutableMap.of(
-                "name", name,
-                "type", CatalogInputDtoYamlTest.class.getName()));
-        assertEquals(input.getType().getTypeToken(), TypeToken.of(CatalogInputDtoYamlTest.class));
-    }
-
-    @Test(expectedExceptions = IllegalArgumentException.class)
-    public void testInvalidType() {
-        String name = "minRam";
-        parse(ImmutableMap.of(
-                "name", name,
-                "type", "missing_type"));
-    }
-
-    @Test(expectedExceptions = IllegalArgumentException.class)
-    public void testInvalidConstraint() {
-        String name = "minRam";
-        parse(ImmutableMap.of(
-                "name", name,
-                "type", "missing_type"));
-    }
-
-    private CatalogInput<?> parse(Object def) {
-        BrooklynClassLoadingContext loader = JavaBrooklynClassLoadingContext.create(mgmt);
-        List<CatalogInput<?>> inputs = CatalogInputDto.ParseYamlInputs.parseInputs(ImmutableList.of(def), loader);
-        return Iterables.getOnlyElement(inputs);
-    }
-
-    private void assertConstraint(Predicate<?> actual, Predicate<?> expected) {
-        //How to compare predicates correctly, re-creating the same predicate doesn't work
-        assertEquals(actual.toString(), expected.toString());
-    }
-
-}