brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [19/71] [abbrv] incubator-brooklyn git commit: Merge commit 'e430723' into reorg2
Date Wed, 23 Dec 2015 11:06:42 GMT
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/tomcat/TomcatServerWebAppFixtureIntegrationTest.java
----------------------------------------------------------------------
diff --cc brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/tomcat/TomcatServerWebAppFixtureIntegrationTest.java
index 0000000,0d21d03..e824e26
mode 000000,100644..100644
--- a/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/tomcat/TomcatServerWebAppFixtureIntegrationTest.java
+++ b/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/tomcat/TomcatServerWebAppFixtureIntegrationTest.java
@@@ -1,0 -1,174 +1,154 @@@
+ /*
+  * 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.entity.webapp.tomcat;
+ 
+ import java.io.File;
+ import java.net.InetAddress;
+ import java.net.Socket;
+ import java.net.SocketException;
+ import java.util.List;
+ import java.util.concurrent.Callable;
+ import java.util.concurrent.TimeUnit;
+ import java.util.concurrent.atomic.AtomicReference;
+ 
+ import org.apache.brooklyn.api.entity.EntitySpec;
+ import org.apache.brooklyn.core.location.PortRanges;
+ import org.apache.brooklyn.core.test.entity.TestApplication;
+ import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+ import org.apache.brooklyn.entity.webapp.AbstractWebAppFixtureIntegrationTest;
+ import org.apache.brooklyn.entity.webapp.HttpsSslConfig;
+ import org.apache.brooklyn.entity.webapp.JavaWebAppSoftwareProcess;
+ import org.apache.brooklyn.test.support.TestResourceUnavailableException;
+ import org.apache.brooklyn.util.exceptions.Exceptions;
+ import org.apache.brooklyn.util.repeat.Repeater;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+ import org.testng.Assert;
+ import org.testng.annotations.AfterMethod;
+ import org.testng.annotations.DataProvider;
+ import org.testng.annotations.Test;
+ 
+ import com.google.common.collect.ImmutableSet;
+ import com.google.common.collect.Lists;
+ 
+ public class TomcatServerWebAppFixtureIntegrationTest extends AbstractWebAppFixtureIntegrationTest {
+ 
+     @SuppressWarnings("unused")
+     private static final Logger log = LoggerFactory.getLogger(TomcatServerWebAppFixtureIntegrationTest.class);
+     
+     @DataProvider(name = "basicEntities")
+     public Object[][] basicEntities() {
+         TestApplication tomcatApp = newTestApplication();
+         TomcatServer tomcat = tomcatApp.createAndManageChild(EntitySpec.create(TomcatServer.class)
+                 .configure(TomcatServer.HTTP_PORT, PortRanges.fromString(DEFAULT_HTTP_PORT)));
+ 
+ 
+         File keystoreFile;
+         try {
+             keystoreFile = createTemporaryKeyStore("myname", "mypass");
+             keystoreFile.deleteOnExit();
+         } catch (Exception e) {
+             throw Exceptions.propagate(e);
+         }
+ 
+         TestApplication tomcatHttpsApp = newTestApplication();
+         TomcatServer httpsTomcat = tomcatHttpsApp.createAndManageChild(EntitySpec.create(TomcatServer.class)
+                 .configure(TomcatServer.ENABLED_PROTOCOLS, ImmutableSet.of("https"))
+                 .configure(TomcatServer.HTTPS_SSL_CONFIG,
+                         new HttpsSslConfig().keyAlias("myname").keystorePassword("mypass").keystoreUrl(keystoreFile.getAbsolutePath())));
+ 
+         return new JavaWebAppSoftwareProcess[][] {
+                 new JavaWebAppSoftwareProcess[] { tomcat },
+                 new JavaWebAppSoftwareProcess[] { httpsTomcat }
+         };
+     }
+ 
+     // exists to be able to test on this class from GUI in Eclipse IDE
+     @Test(groups = "Integration", dataProvider = "basicEntities")
+     public void canStartAndStop(final SoftwareProcess entity) {
+         super.canStartAndStop(entity);
+     }
++
+     @Test(groups = "Integration", dataProvider = "basicEntities")
+     public void testReportsServiceDownWhenKilled(final SoftwareProcess entity) throws Exception {
+         super.testReportsServiceDownWhenKilled(entity);
+     }
+ 
+     @Override
 -    // as parent, but with spring travel
+     @DataProvider(name = "entitiesWithWarAndURL")
+     public Object[][] entitiesWithWar() {
+         TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), "/hello-world.war");
+         List<Object[]> result = Lists.newArrayList();
+         
+         for (Object[] entity : basicEntities()) {
+             result.add(new Object[] {
+                     entity[0],
+                     "hello-world.war",
+                     "hello-world/",
+                     "" // no sub-page path
+                     });
+         }
 -
 -        // TODO would be nice to test against spring web framework stock booking example
 -        // but we'd need an external URL for that (we removed the binary from here for apache compliance reasons)
 -//        TestApplication tomcatApp = newTestApplication();
 -//        TomcatServer tomcat = tomcatApp.createAndManageChild(EntitySpec.create(TomcatServer.class)
 -//                .configure(TomcatServer.HTTP_PORT, PortRanges.fromString(DEFAULT_HTTP_PORT)));
 -//        result.add(new Object[] {
 -//                tomcat,
 -//                "swf-booking-mvc.war",
 -//                "swf-booking-mvc/",
 -//                "spring/intro",
 -//               });
 -        
+         return result.toArray(new Object[][] {});
+     }
+ 
+     @AfterMethod(alwaysRun=true, dependsOnMethods="shutdownApp")
+     public void ensureIsShutDown() throws Exception {
+         final AtomicReference<Socket> shutdownSocket = new AtomicReference<Socket>();
+         final AtomicReference<SocketException> gotException = new AtomicReference<SocketException>();
+         final Integer shutdownPort = (entity != null) ? entity.getAttribute(TomcatServer.SHUTDOWN_PORT) : null;
+         
+         if (shutdownPort != null) {
+             boolean socketClosed = Repeater.create("Checking WebApp has shut down")
+                     .repeat(new Callable<Void>() {
+                             public Void call() throws Exception {
+                                 if (shutdownSocket.get() != null) shutdownSocket.get().close();
+                                 try {
+                                     shutdownSocket.set(new Socket(InetAddress.getLocalHost(), shutdownPort));
+                                     gotException.set(null);
+                                 } catch (SocketException e) {
+                                     gotException.set(e);
+                                 }
+                                 return null;
+                             }})
+                     .every(100, TimeUnit.MILLISECONDS)
+                     .until(new Callable<Boolean>() {
+                             public Boolean call() {
+                                 return (gotException.get() != null);
+                             }})
+                     .limitIterationsTo(25)
+                     .run();
+             
 -            if (socketClosed == false) {
 -//                log.error("WebApp did not shut down - this is a failure of the last test run");
 -//                log.warn("I'm sending a message to the shutdown port {}", shutdownPort);
 -//                OutputStreamWriter writer = new OutputStreamWriter(shutdownSocket.getOutputStream());
 -//                writer.write("SHUTDOWN\r\n");
 -//                writer.flush();
 -//                writer.close();
 -//                shutdownSocket.close();
++            if (!socketClosed) {
+                 throw new Exception("Last test run did not shut down WebApp entity "+entity+" (port "+shutdownPort+")");
+             }
+         } else {
+             Assert.fail("Cannot shutdown, because shutdown-port not set for "+entity);
+         }
+     }
+ 
+     public static void main(String ...args) throws Exception {
+         TomcatServerWebAppFixtureIntegrationTest t = new TomcatServerWebAppFixtureIntegrationTest();
+         t.setUp();
+         t.testReportsServiceDownWhenKilled((SoftwareProcess) t.basicEntities()[0][0]);
+         t.shutdownApp();
+         t.ensureIsShutDown();
+         t.shutdownMgmt();
+     }
+ 
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
----------------------------------------------------------------------
diff --cc brooklyn-server/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
index 0000000,aa3a198..ab046d5
mode 000000,100644..100644
--- a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
+++ b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
@@@ -1,0 -1,262 +1,267 @@@
+ /*
+  * 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.internal;
+ 
+ import static com.google.common.base.Preconditions.checkNotNull;
+ 
+ import java.io.Serializable;
+ import java.lang.reflect.Modifier;
+ import java.util.Collections;
+ import java.util.List;
+ import java.util.Map;
+ import java.util.Set;
+ 
+ import org.apache.brooklyn.api.mgmt.EntityManager;
+ import org.apache.brooklyn.api.mgmt.Task;
+ import org.apache.brooklyn.api.objs.BrooklynObject;
+ 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.collections.MutableSet;
+ import org.apache.brooklyn.util.exceptions.Exceptions;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+ 
+ import com.google.common.base.Objects;
+ import com.google.common.collect.ImmutableList;
+ import com.google.common.collect.ImmutableSet;
+ import com.google.common.collect.Iterables;
+ import com.google.common.collect.Maps;
+ 
+ /** Defines a spec for creating a {@link BrooklynObject}.
+  * <p>
+  * In addition to the contract defined by the code,
+  * subclasses should provide a public static <code>create(Class)</code>
+  * method to create an instance of the spec for the target type indicated by the argument. 
+  * <p>
+  * The spec is then passed to type-specific methods,
+  * e.g. {@link EntityManager#createEntity(org.apache.brooklyn.api.entity.EntitySpec)}
+  * to create a managed instance of the target type. */
+ public abstract class AbstractBrooklynObjectSpec<T,SpecT extends AbstractBrooklynObjectSpec<T,SpecT>> implements Serializable {
+ 
+     private static final long serialVersionUID = 3010955277740333030L;
+ 
+     private static final Logger log = LoggerFactory.getLogger(AbstractBrooklynObjectSpec.class);
+     
+     private final Class<? extends T> type;
+     private String displayName;
+     private String catalogItemId;
+     private Set<Object> tags = MutableSet.of();
+     private List<SpecParameter<?>> parameters = ImmutableList.of();
+ 
+     protected final Map<String, Object> flags = Maps.newLinkedHashMap();
+     protected final Map<ConfigKey<?>, Object> config = Maps.newLinkedHashMap();
+ 
+     protected AbstractBrooklynObjectSpec(Class<? extends T> type) {
+         checkValidType(type);
+         this.type = type;
+         this.catalogItemId = ApiObjectsFactory.get().getCatalogItemIdFromContext();
+     }
+     
+     @SuppressWarnings("unchecked")
+     protected SpecT self() {
+         return (SpecT) this;
+     }
+ 
+     @Override
+     public String toString() {
+         return Objects.toStringHelper(this).add("type", getType()).toString();
+     }
+ 
+     protected abstract void checkValidType(Class<? extends T> type);
+     
+     public SpecT displayName(String val) {
+         displayName = val;
+         return self();
+     }
+     
+     public SpecT catalogItemId(String val) {
+         catalogItemId = val;
+         return self();
+     }
+     
+     public SpecT tag(Object tag) {
+         tags.add(tag);
+         return self();
+     }
+ 
+     /** adds the given tags */
+     public SpecT tags(Iterable<Object> tagsToAdd) {
+         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 (often an interface) this spec represents and which will be instantiated from it 
+      */
+     public Class<? extends T> getType() {
+         return type;
+     }
+     
+     /**
+      * @return The display name of the object
+      */
+     public final String getDisplayName() {
+         return displayName;
+     }
+     
+     public final String getCatalogItemId() {
+         return catalogItemId;
+     }
+ 
+     public final Set<Object> getTags() {
+         return ImmutableSet.copyOf(tags);
+     }
+ 
+     /** A list of configuration options that the entity supports. */
+     public final List<SpecParameter<?>> getParameters() {
 -        return ImmutableList.copyOf(parameters);
++        //Could be null after rebind
++        if (parameters != null) {
++            return ImmutableList.copyOf(parameters);
++        } else {
++            return ImmutableList.of();
++        }
+     }
+ 
+     // TODO Duplicates method in BasicEntityTypeRegistry and InternalEntityFactory.isNewStyleEntity
+     protected final void checkIsNewStyleImplementation(Class<?> implClazz) {
+         try {
+             implClazz.getConstructor(new Class[0]);
+         } catch (NoSuchMethodException e) {
+             throw new IllegalStateException("Implementation "+implClazz+" must have a no-argument constructor");
+         } catch (SecurityException e) {
+             throw Exceptions.propagate(e);
+         }
+         
+         if (implClazz.isInterface()) throw new IllegalStateException("Implementation "+implClazz+" is an interface, but must be a non-abstract class");
+         if (Modifier.isAbstract(implClazz.getModifiers())) throw new IllegalStateException("Implementation "+implClazz+" is abstract, but must be a non-abstract class");
+     }
+     
+     // TODO Duplicates method in BasicEntityTypeRegistry
+     protected final void checkIsImplementation(Class<?> val, Class<? super T> requiredInterface) {
+         if (!requiredInterface.isAssignableFrom(val)) throw new IllegalStateException("Implementation "+val+" does not implement "+requiredInterface.getName());
+         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())
+             .configure(otherSpec.getConfig())
+             .configure(otherSpec.getFlags())
+             .tags(otherSpec.getTags())
+             .catalogItemId(otherSpec.getCatalogItemId())
+             .parameters(otherSpec.getParameters());
+     }
+ 
+     @Override
+     public boolean equals(Object obj) {
+         if (obj==null) return false;
+         if (!obj.getClass().equals(getClass())) return false;
+         AbstractBrooklynObjectSpec<?,?> other = (AbstractBrooklynObjectSpec<?,?>)obj;
+         if (!Objects.equal(getDisplayName(), other.getDisplayName())) return false;
+         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;
+     }
+     
+     @Override
+     public int hashCode() {
+         return Objects.hashCode(getCatalogItemId(), getDisplayName(), getType(), getTags());
+     }
+ 
+     /** strings inserted as flags, config keys inserted as config keys; 
+      * if you want to force one or the other, create a ConfigBag and convert to the appropriate map type */
+     public SpecT configure(Map<?,?> val) {
+         for (Map.Entry<?, ?> entry: val.entrySet()) {
+             if (entry.getKey()==null) throw new NullPointerException("Null key not permitted");
+             if (entry.getKey() instanceof CharSequence)
+                 flags.put(entry.getKey().toString(), entry.getValue());
+             else if (entry.getKey() instanceof ConfigKey<?>)
+                 config.put((ConfigKey<?>)entry.getKey(), entry.getValue());
+             else if (entry.getKey() instanceof HasConfigKey<?>)
+                 config.put(((HasConfigKey<?>)entry.getKey()).getConfigKey(), entry.getValue());
+             else {
+                 log.warn("Spec "+this+" ignoring unknown config key "+entry.getKey());
+             }
+         }
+         return self();
+     }
+ 
+     public SpecT configure(CharSequence key, Object val) {
+         flags.put(checkNotNull(key, "key").toString(), val);
+         return self();
+     }
+     
+     public <V> SpecT configure(ConfigKey<V> key, V val) {
+         config.put(checkNotNull(key, "key"), val);
+         return self();
+     }
+ 
+     public <V> SpecT configureIfNotNull(ConfigKey<V> key, V val) {
+         return (val != null) ? configure(key, val) : self();
+     }
+ 
+     public <V> SpecT configure(ConfigKey<V> key, Task<? extends V> val) {
+         config.put(checkNotNull(key, "key"), val);
+         return self();
+     }
+ 
+     public <V> SpecT configure(HasConfigKey<V> key, V val) {
+         config.put(checkNotNull(key, "key").getConfigKey(), val);
+         return self();
+     }
+ 
+     public <V> SpecT configure(HasConfigKey<V> key, Task<? extends V> val) {
+         config.put(checkNotNull(key, "key").getConfigKey(), val);
+         return self();
+     }
+ 
+     public <V> SpecT removeConfig(ConfigKey<V> key) {
+         config.remove( checkNotNull(key, "key") );
+         return self();
+     }
+ 
+     /** Clears the config map, removing any config previously set. */
+     public void clearConfig() {
+         config.clear();
+     }
+         
+     /**
+      * @return Read-only construction flags
+      * @see SetFromFlag declarations on the policy type
+      */
+     public Map<String, ?> getFlags() {
+         return Collections.unmodifiableMap(flags);
+     }
+     
+     /**
+      * @return Read-only configuration values
+      */
+     public Map<ConfigKey<?>, Object> getConfig() {
+         return Collections.unmodifiableMap(config);
+     }
+ 
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/typereg/BrooklynTypeRegistry.java
----------------------------------------------------------------------
diff --cc brooklyn-server/api/src/main/java/org/apache/brooklyn/api/typereg/BrooklynTypeRegistry.java
index 0000000,78c49d8..17a7fb3
mode 000000,100644..100644
--- a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/typereg/BrooklynTypeRegistry.java
+++ b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/typereg/BrooklynTypeRegistry.java
@@@ -1,0 -1,70 +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.api.typereg;
+ 
+ import javax.annotation.Nullable;
+ 
+ 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.util.guava.Maybe;
+ 
++import com.google.common.annotations.Beta;
+ import com.google.common.base.Predicate;
+ 
+ 
+ public interface BrooklynTypeRegistry {
+ 
+     public enum RegisteredTypeKind {
+         /** a registered type which will create an {@link AbstractBrooklynObjectSpec} (e.g. {@link EntitySpec}) 
+          * for the type registered (e.g. the {@link Entity} instance) */
+         SPEC,
+         /** a registered type which will create the java type described */
+         BEAN 
+         // note: additional kinds should have the visitor in core/RegisteredTypeKindVisitor updated
+         // to flush out all places which want to implement support for all kinds 
+     }
+     
+     Iterable<RegisteredType> getAll();
 -    Iterable<RegisteredType> getAll(Predicate<? super RegisteredType> filter);
++    Iterable<RegisteredType> getMatching(Predicate<? super RegisteredType> filter);
+ 
+     /** @return The item matching the given given 
+      * {@link RegisteredType#getSymbolicName() symbolicName} 
+      * and optionally {@link RegisteredType#getVersion()},
+      * taking the best version if the version is null or a default marker,
+      * returning null if no matches are found. */
+     RegisteredType get(String symbolicName, String version);
 -    /** as {@link #get(String, String)} but allows <code>"name:version"</code> 
 -     * (the {@link RegisteredType#getId()}) in addition to the unversioned name,
 -     * using a default marker if no version can be inferred */
++    /** as {@link #get(String, String)} but the given string here 
++     * is allowed to match any of:
++     * <li>the given string as an ID including version (<code>"name:version"</code>) 
++     * <li>the symbolic name unversioned, or
++     * <li>an alias */
+     RegisteredType get(String symbolicNameWithOptionalVersion);
 -    
 -    // TODO remove
 -//    /** as {@link #get(String, String)}, but applying the optionally supplied {@link RegisteredTypeLoadingContext} */ 
 -//    RegisteredType get(String symbolicName, String version, @Nullable RegisteredTypeLoadingContext context);
 -//    /** as {@link #get(String)}, but applying the optionally supplied {@link RegisteredTypeLoadingContext} */ 
 -//    RegisteredType get(String symbolicNameWithOptionalVersion, @Nullable RegisteredTypeLoadingContext context);
++
++    /** as {@link #get(String)} but further filtering for the additional context */
++    public RegisteredType get(String symbolicNameOrAliasWithOptionalVersion, RegisteredTypeLoadingContext context);
++    /** returns a wrapper of the result of {@link #get(String, RegisteredTypeLoadingContext)} 
++     * including a detailed message if absent */
++    public Maybe<RegisteredType> getMaybe(String symbolicNameOrAliasWithOptionalVersion, RegisteredTypeLoadingContext context);
+ 
+     // NB the seemingly more correct generics <T,SpecT extends AbstractBrooklynObjectSpec<T,SpecT>> 
+     // cause compile errors, not in Eclipse, but in maven (?) 
 -    // TODO do these belong here, or in a separate master TypePlanTransformer ?  see also BrooklynTypePlanTransformer 
++    // TODO do these belong here, or in a separate master TypePlanTransformer ?  see also BrooklynTypePlanTransformer
++    @Beta
+     <SpecT extends AbstractBrooklynObjectSpec<?,?>> SpecT createSpec(RegisteredType type, @Nullable RegisteredTypeLoadingContext optionalContext, @Nullable Class<SpecT> optionalSpecSuperType);
++    @Beta
+     <SpecT extends AbstractBrooklynObjectSpec<?,?>> SpecT createSpecFromPlan(@Nullable String planFormat, Object planData, @Nullable RegisteredTypeLoadingContext optionalContext, @Nullable Class<SpecT> optionalSpecSuperType);
++    @Beta
+     <T> T createBean(RegisteredType type, @Nullable RegisteredTypeLoadingContext optionalContext, @Nullable Class<T> optionalResultSuperType);
++    @Beta
+     <T> T createBeanFromPlan(String planFormat, Object planData, @Nullable RegisteredTypeLoadingContext optionalConstraint, @Nullable Class<T> optionalBeanSuperType);
+     
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredType.java
----------------------------------------------------------------------
diff --cc brooklyn-server/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredType.java
index 0000000,2674736..29b64d3
mode 000000,100644..100644
--- a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredType.java
+++ b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredType.java
@@@ -1,0 -1,92 +1,96 @@@
+ /*
+  * 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.typereg;
+ 
+ import java.util.Collection;
+ import java.util.Set;
+ 
 -import javax.annotation.Nullable;
 -
+ import org.apache.brooklyn.api.entity.Entity;
+ import org.apache.brooklyn.api.entity.EntitySpec;
+ import org.apache.brooklyn.api.objs.BrooklynObject;
+ import org.apache.brooklyn.api.objs.Identifiable;
+ import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry.RegisteredTypeKind;
+ 
+ import com.google.common.annotations.Beta;
+ 
+ public interface RegisteredType extends Identifiable {
+     
+     @Override String getId();
+     
+     RegisteredTypeKind getKind();
+     
+     String getSymbolicName();
+     String getVersion();
+ 
+     Collection<OsgiBundleWithUrl> getLibraries();
+ 
+     String getDisplayName();
+     String getDescription();
+     String getIconUrl();
+ 
+     /** @return all declared supertypes or super-interfaces of this registered type,
+      * consisting of a collection of {@link Class} or {@link RegisteredType}
+      * <p>
+      * This should normally include at least one {@link Class} object:
+      * For beans, this should include the java type that the {@link BrooklynTypeRegistry} will create. 
+      * For specs, this should refer to the {@link BrooklynObject} type that the created spec will point at 
+      * (e.g. the concrete {@link Entity}, not the {@link EntitySpec}).
+      * <p>
+      * This may not necessarily return the most specific java class or classes;
+      * such as if the concrete type is private and callers should know only about a particular public interface,
+      * or if precise type details are unavailable and all that is known at creation is some higher level interface/supertype
+      * (e.g. this may return {@link Entity} even though the spec points at a specific subclass,
+      * for instance because the YAML has not yet been parsed or OSGi bundles downloaded).
+      * <p>
+      * This may include other registered types such as marker interfaces.
+      */
+     @Beta
+     Set<Object> getSuperTypes();
+ 
+     /**
+      * @return True if the item has been deprecated (i.e. its use is discouraged)
+      */
+     boolean isDeprecated();
+     
+     /**
+      * @return True if the item has been disabled (i.e. its use is forbidden, except for pre-existing apps)
+      */
+     boolean isDisabled();
+ 
++    /** Alias words defined for this type */
++    Set<String> getAliases();
++
++    /** Tags attached to this item */
++    Set<Object> getTags();
++    
+     /** @return implementation details, so that the framework can find a suitable {@link BrooklynTypePlanTransformer} 
+      * which can then use this object to instantiate this type */
+     TypeImplementationPlan getPlan();
+     
+     public interface TypeImplementationPlan {
+         /** hint which {@link BrooklynTypePlanTransformer} instance(s) can be used, if known;
+          * this may be null if the relevant transformer was not declared when created,
+          * but in general we should look to determine the kind as early as possible 
+          * and use that to retrieve the appropriate such transformer */
+         String getPlanFormat();
+         /** data for the implementation; may be more specific */
+         Object getPlanData();
+     }
+ 
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/PdpProcessor.java
----------------------------------------------------------------------
diff --cc brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/PdpProcessor.java
index 0000000,8716aa5..ae42ee7
mode 000000,100644..100644
--- a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/PdpProcessor.java
+++ b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/PdpProcessor.java
@@@ -1,0 -1,186 +1,186 @@@
+ /*
+  * 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.spi.resolve;
+ 
+ import java.io.InputStream;
+ import java.io.Reader;
+ import java.util.ArrayList;
+ import java.util.List;
+ import java.util.Map;
+ 
+ import org.apache.brooklyn.camp.CampPlatform;
+ import org.apache.brooklyn.camp.spi.AssemblyTemplate;
+ import org.apache.brooklyn.camp.spi.instantiate.BasicAssemblyTemplateInstantiator;
+ import org.apache.brooklyn.camp.spi.pdp.Artifact;
+ import org.apache.brooklyn.camp.spi.pdp.AssemblyTemplateConstructor;
+ import org.apache.brooklyn.camp.spi.pdp.DeploymentPlan;
+ import org.apache.brooklyn.camp.spi.pdp.Service;
+ import org.apache.brooklyn.camp.spi.resolve.interpret.PlanInterpretationContext;
+ import org.apache.brooklyn.camp.spi.resolve.interpret.PlanInterpretationNode;
+ import org.apache.brooklyn.util.collections.MutableMap;
+ import org.apache.brooklyn.util.exceptions.Exceptions;
+ import org.apache.brooklyn.util.stream.Streams;
+ import org.apache.brooklyn.util.yaml.Yamls;
+ import org.apache.commons.compress.archivers.ArchiveEntry;
+ import org.apache.commons.compress.archivers.ArchiveInputStream;
+ import org.apache.commons.compress.archivers.ArchiveStreamFactory;
+ import org.yaml.snakeyaml.error.YAMLException;
+ 
+ import com.google.common.annotations.VisibleForTesting;
+ 
+ public class PdpProcessor {
+ 
+     final CampPlatform campPlatform;
+     
+     final List<PdpMatcher> matchers = new ArrayList<PdpMatcher>();
+     final List<PlanInterpreter> interpreters = new ArrayList<PlanInterpreter>();
+     
+     public PdpProcessor(CampPlatform campPlatform) {
+         this.campPlatform = campPlatform;
+     }
+ 
+     public DeploymentPlan parseDeploymentPlan(Reader yaml) {
+         return parseDeploymentPlan(Streams.readFully(yaml));
+     }
+     
+     @SuppressWarnings("unchecked")
+     public DeploymentPlan parseDeploymentPlan(String yaml) {
+         Iterable<Object> template = Yamls.parseAll(yaml);
+         
+         Map<String, Object> dpRootUninterpreted = null;
+         try {
+             dpRootUninterpreted = Yamls.getAs(template, Map.class);
+         } catch (Exception e) {
+             Exceptions.propagateIfFatal(e);
+             throw new YAMLException("Plan not in acceptable format: "+(e.getMessage()!=null ? e.getMessage() : ""+e), e);
+         }
+         Map<String, Object> dpRootInterpreted = applyInterpreters(dpRootUninterpreted);
+         
+         return DeploymentPlan.of(dpRootInterpreted, yaml);
+     }
+     
+     /** create and return an AssemblyTemplate based on the given DP (yaml) */
+     public AssemblyTemplate registerDeploymentPlan(Reader yaml) {
+         DeploymentPlan plan = parseDeploymentPlan(yaml);
+         return registerDeploymentPlan(plan);
+     }
+ 
+     /** applies matchers to the given deployment plan to create an assembly template */
+     public AssemblyTemplate registerDeploymentPlan(DeploymentPlan plan) {
+         AssemblyTemplateConstructor atc = new AssemblyTemplateConstructor(campPlatform);
+         
+         if (plan.getName()!=null) atc.name(plan.getName());
+         if (plan.getDescription()!=null) atc.description(plan.getDescription());
+         if (plan.getSourceCode()!=null) atc.sourceCode(plan.getSourceCode());
+         // nothing done with origin just now...
+         
+         if (plan.getServices()!=null) {
+             for (Service svc: plan.getServices()) {
+                 applyMatchers(svc, atc);
+             }
+         }
+ 
+         if (plan.getArtifacts()!=null) {
+             for (Artifact art: plan.getArtifacts()) {
+                 applyMatchers(art, atc);
+             }
+         }
+ 
+         Map<String, Object> attrs = plan.getCustomAttributes();
+         if (attrs!=null && !attrs.isEmpty()) {
+             Map<String, Object> customAttrs = attrs;
+             if (customAttrs.containsKey("id")) {
+                 // id shouldn't be leaking to entities, see InternalEntityFactory.createEntityAndDescendantsUninitialized.
+                 // If set it will go through to the spec because AbstractBrooklynObject has @SetFromFlag("id") on the id property.
+                 // Follows logic in BrooklynEntityMatcher.apply(...).
+                 customAttrs = MutableMap.copyOf(attrs);
+                 customAttrs.put("planId", customAttrs.remove("id"));
+             }
+             atc.addCustomAttributes(customAttrs);
+         }
+         
+         if (atc.getInstantiator()==null)
+             // set a default instantiator which just invokes the component's instantiators
+             // (or throws unsupported exceptions, currently!)
+             atc.instantiator(BasicAssemblyTemplateInstantiator.class);
+ 
+         return atc.commit();
+     }
+     
+     public AssemblyTemplate registerPdpFromArchive(InputStream archiveInput) {
+         try {
+             ArchiveInputStream input = new ArchiveStreamFactory()
+                 .createArchiveInputStream(archiveInput);
+             
+             while (true) {
+                 ArchiveEntry entry = input.getNextEntry();
+                 if (entry==null) break;
+                 // TODO unpack entry, create a space on disk holding the archive ?
+             }
+ 
+             // use yaml...
+             throw new UnsupportedOperationException("in progress");
+             
+         } catch (Exception e) {
+             throw Exceptions.propagate(e);
+         }
+     }
+ 
+ 
+     // ----------------------------
+     
+     public void addMatcher(PdpMatcher m) {
+         // TODO a list is a crude way to do matching ... but good enough to start
+         matchers.add(m);
+     }
+ 
+     public List<PdpMatcher> getMatchers() {
+         return matchers;
+     }
+ 
+ 
+     protected void applyMatchers(Object deploymentPlanItem, AssemblyTemplateConstructor atc) {
+         for (PdpMatcher matcher: getMatchers()) {
+             if (matcher.accepts(deploymentPlanItem)) {
+                 // TODO first accepting is a crude way to do matching ... but good enough to start
+                 if (matcher.apply(deploymentPlanItem, atc))
+                     return;
+             }
+         }
+         throw new IllegalArgumentException("Deployment plan item cannot be matched. Please check your YAML. Item: "+deploymentPlanItem);
+     }
+ 
+     // ----------------------------
+ 
+     public void addInterpreter(PlanInterpreter interpreter) {
+         interpreters.add(interpreter);
+     }
+     
+     /** returns a DeploymentPlan object which is the result of running the interpretation
+      * (with all interpreters) against the supplied deployment plan YAML object,
+      * essentially a post-parse processing step before matching */
+     @SuppressWarnings("unchecked")
+     @VisibleForTesting
 -    public Map<String, Object> applyInterpreters(Map<String, Object> originalDeploymentPlan) {
++    public Map<String, Object> applyInterpreters(Map<String, ?> originalDeploymentPlan) {
+         PlanInterpretationNode interpretation = new PlanInterpretationNode(
+                 new PlanInterpretationContext(originalDeploymentPlan, interpreters));
+         return (Map<String, Object>) interpretation.getNewValue();
+     }
+     
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationContext.java
----------------------------------------------------------------------
diff --cc brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationContext.java
index 0000000,08053cb..26822aa
mode 000000,100644..100644
--- a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationContext.java
+++ b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationContext.java
@@@ -1,0 -1,152 +1,152 @@@
+ /*
+  * 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.spi.resolve.interpret;
+ 
+ import java.util.List;
+ import java.util.Map;
+ 
+ import org.apache.brooklyn.camp.spi.resolve.PlanInterpreter;
+ 
+ import com.google.common.collect.ImmutableList;
+ import com.google.common.collect.ImmutableMap;
+ 
+ public class PlanInterpretationContext {
+ 
+     private final Map<String,Object> originalDeploymentPlan;
+     private final List<PlanInterpreter> interpreters;
+     private final PlanInterpreter allInterpreter;
+ 
 -    public PlanInterpretationContext(Map<String,Object> originalDeploymentPlan, List<PlanInterpreter> interpreters) {
++    public PlanInterpretationContext(Map<String,?> originalDeploymentPlan, List<PlanInterpreter> interpreters) {
+         super();
+         this.originalDeploymentPlan = ImmutableMap.copyOf(originalDeploymentPlan);
+         this.interpreters = ImmutableList.copyOf(interpreters);
+         this.allInterpreter = new PlanInterpreter() {
+             @Override
+             public boolean isInterestedIn(PlanInterpretationNode node) {
+                 return true;
+             }
+             
+             @Override
+             public void applyYamlPrimitive(PlanInterpretationNode node) {
+                 for (PlanInterpreter i: PlanInterpretationContext.this.interpreters) {
+                     if (node.isExcluded())
+                         break;
+                     if (i.isInterestedIn(node)) {
+                         i.applyYamlPrimitive(node);
+                     }
+                 }
+             }
+ 
+             @Override
+             public boolean applyMapBefore(PlanInterpretationNode node, Map<Object, Object> mapIn) {
+                 boolean result = true;
+                 for (PlanInterpreter i: PlanInterpretationContext.this.interpreters) {
+                     if (node.isExcluded())
+                         break;
+                     if (i.isInterestedIn(node)) {
+                         boolean ri= i.applyMapBefore(node, mapIn);
+                         result &= ri;
+                     }
+                 }
+                 return result;
+             }
+ 
+             @Override
+             public boolean applyMapEntry(PlanInterpretationNode node, Map<Object, Object> mapIn, Map<Object, Object> mapOut, 
+                                     PlanInterpretationNode key, PlanInterpretationNode value) {
+                 boolean result = true;
+                 for (PlanInterpreter i: PlanInterpretationContext.this.interpreters) { 
+                     if (node.isExcluded())
+                         break;
+                     if (i.isInterestedIn(key)) {
+                         boolean ri = i.applyMapEntry(node, mapIn, mapOut, key, value);
+                         result &= ri;
+                     }
+                 }
+                 return result;
+             }
+ 
+             @Override
+             public void applyMapAfter(PlanInterpretationNode node, Map<Object, Object> mapIn, Map<Object, Object> mapOut) {
+                 for (PlanInterpreter i: PlanInterpretationContext.this.interpreters) { 
+                     if (node.isExcluded())
+                         break;
+                     if (i.isInterestedIn(node)) {
+                         i.applyMapAfter(node, mapIn, mapOut);
+                     }
+                 }
+             }
+ 
+             @Override
+             public boolean applyListBefore(PlanInterpretationNode node, List<Object> listIn) {
+                 boolean result = true;
+                 for (PlanInterpreter i: PlanInterpretationContext.this.interpreters) { 
+                     if (node.isExcluded())
+                         break;
+                     if (i.isInterestedIn(node)) {
+                         boolean ri = i.applyListBefore(node, listIn);
+                         result &= ri;
+                     }
+                 }
+                 return result;
+             }
+ 
+             @Override
+             public boolean applyListEntry(PlanInterpretationNode node, List<Object> listIn, List<Object> listOut, 
+                                     PlanInterpretationNode value) {
+                 boolean result = true;
+                 for (PlanInterpreter i: PlanInterpretationContext.this.interpreters) { 
+                     if (node.isExcluded())
+                         break;
+                     if (i.isInterestedIn(value)) {
+                         boolean ri = i.applyListEntry(node, listIn, listOut, value);
+                         result &= ri;
+                     }
+                 }
+                 return result;
+             }
+ 
+             @Override
+             public void applyListAfter(PlanInterpretationNode node, List<Object> listIn, List<Object> listOut) {
+                 for (PlanInterpreter i: PlanInterpretationContext.this.interpreters) { 
+                     if (node.isExcluded())
+                         break;
+                     if (i.isInterestedIn(node)) {
+                         i.applyListAfter(node, listIn, listOut);
+                     }
+                 }
+             }
+ 
+         };
+     }
+ 
+     /** returns an interpreter which recurses through all interpreters */
+     PlanInterpreter getAllInterpreter() {
+         return allInterpreter;
+     }
+ 
+     public Map<String,Object> getOriginalDeploymentPlan() {
+         return originalDeploymentPlan;
+     }
+ 
+     public List<PlanInterpreter> getInterpreters() {
+         return interpreters;
+     }
+ 
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/BrooklynCampPlatform.java
----------------------------------------------------------------------
diff --cc brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/BrooklynCampPlatform.java
index 0000000,14a7c59..7290c24
mode 000000,100644..100644
--- a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/BrooklynCampPlatform.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/BrooklynCampPlatform.java
@@@ -1,0 -1,77 +1,103 @@@
+ /*
+  * 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;
+ 
++import static com.google.common.base.Preconditions.checkState;
++
++import java.util.Map;
++
+ import org.apache.brooklyn.api.mgmt.ManagementContext;
+ import org.apache.brooklyn.api.mgmt.ManagementContext.PropertiesReloadListener;
+ import org.apache.brooklyn.camp.AggregatingCampPlatform;
+ import org.apache.brooklyn.camp.CampPlatform;
+ import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynEntityMatcher;
+ import org.apache.brooklyn.camp.brooklyn.spi.dsl.BrooklynDslInterpreter;
+ import org.apache.brooklyn.camp.brooklyn.spi.platform.BrooklynImmutableCampPlatform;
+ import org.apache.brooklyn.camp.spi.PlatformRootSummary;
 -import org.apache.brooklyn.core.internal.BrooklynProperties;
+ import org.apache.brooklyn.core.mgmt.HasBrooklynManagementContext;
++import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
++import org.apache.brooklyn.core.mgmt.internal.CampYamlParser;
++
++import com.google.common.collect.ImmutableMap;
++import com.google.common.collect.ImmutableSet;
+ 
+ /** {@link CampPlatform} implementation which includes Brooklyn entities 
+  * (via {@link BrooklynImmutableCampPlatform})
+  * and allows customisation / additions */
+ public class BrooklynCampPlatform extends AggregatingCampPlatform implements HasBrooklynManagementContext {
+ 
+     private final ManagementContext bmc;
+ 
+     public BrooklynCampPlatform(PlatformRootSummary root, ManagementContext managementContext) {
+         super(root);
+         addPlatform(new BrooklynImmutableCampPlatform(root, managementContext));
+         
+         this.bmc = managementContext;
+         
+         addMatchers();
+         addInterpreters();
+         
+         managementContext.addPropertiesReloadListener(new PropertiesReloadListener() {
+             private static final long serialVersionUID = -3739276553334749184L;
+             @Override public void reloaded() {
+                 setConfigKeyAtManagmentContext();
+             }
+         });
+     }
+ 
+     // --- brooklyn setup
+     
+     @Override
+     public ManagementContext getBrooklynManagementContext() {
+         return bmc;
+     }
+     
+     protected void addMatchers() {
+         // TODO artifacts
+         pdp().addMatcher(new BrooklynEntityMatcher(bmc));
+     }
+     
+     protected void addInterpreters() {
+         pdp().addInterpreter(new BrooklynDslInterpreter());
+     }
+ 
+     public BrooklynCampPlatform setConfigKeyAtManagmentContext() {
 -        ((BrooklynProperties)bmc.getConfig()).put(BrooklynCampConstants.CAMP_PLATFORM, this);
++        ((ManagementContextInternal)bmc).getBrooklynProperties().put(BrooklynCampConstants.CAMP_PLATFORM, this);
++        ((ManagementContextInternal)bmc).getBrooklynProperties().put(CampYamlParser.YAML_PARSER_KEY, new YamlParserImpl(this));
+         return this;
+     }
 -
++    
++    public static class YamlParserImpl implements CampYamlParser {
++        private final BrooklynCampPlatform platform;
++        
++        YamlParserImpl(BrooklynCampPlatform platform) {
++            this.platform = platform;
++        }
++        
++        public Map<String, Object> parse(Map<String, Object> map) {
++            return platform.pdp().applyInterpreters(map);
++        }
++        
++        public Object parse(String val) {
++            Map<String, Object> result = platform.pdp().applyInterpreters(ImmutableMap.of("dummyKey", val));
++            checkState(result.keySet().equals(ImmutableSet.of("dummyKey")), "expected single result, but got %s", result);
++            return result.get("dummyKey");
++        }
++    }
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
----------------------------------------------------------------------
diff --cc brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
index 0000000,0147b9d..6734875
mode 000000,100644..100644
--- a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
@@@ -1,0 -1,211 +1,213 @@@
+ /*
+  * 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.api.entity.EntityInitializer;
+ import org.apache.brooklyn.api.entity.EntitySpec;
+ import org.apache.brooklyn.api.mgmt.ManagementContext;
+ 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;
+ import org.apache.brooklyn.api.sensor.EnricherSpec;
+ import org.apache.brooklyn.api.typereg.RegisteredType;
+ import org.apache.brooklyn.camp.brooklyn.BrooklynCampReservedKeys;
+ import org.apache.brooklyn.camp.brooklyn.spi.creation.BrooklynYamlTypeInstantiator.InstantiatorFromKey;
+ import org.apache.brooklyn.core.objs.BasicSpecParameter;
+ import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts;
+ import org.apache.brooklyn.core.typereg.RegisteredTypes;
+ import org.apache.brooklyn.util.collections.MutableList;
+ import org.apache.brooklyn.util.core.config.ConfigBag;
++import org.apache.brooklyn.util.guava.Maybe;
+ 
+ import com.google.common.annotations.Beta;
+ import com.google.common.collect.ImmutableList;
+ 
+ /**
+  * 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 List<? extends DT> buildListOfTheseDecorationsFromEntityAttributes(ConfigBag attrs) {
+         Object value = getDecorationAttributeJsonValue(attrs); 
+         if (value==null) return MutableList.of();
+         if (value instanceof Iterable) {
+             return buildListOfTheseDecorationsFromIterable((Iterable<?>)value);
+         } 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());
+         }
+     }
+ 
+     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 List<DT> buildListOfTheseDecorationsFromIterable(Iterable<?> value) {
+         List<DT> decorations = MutableList.of();
+         for (Object decorationJson: value)
+             addDecorationFromJsonMap(checkIsMap(decorationJson), decorations);
+         return decorations;
+     }
+ 
+     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
+         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();
+             
 -            RegisteredType item = RegisteredTypes.validate(mgmt.getTypeRegistry().get(policyType), RegisteredTypeLoadingContexts.spec(Policy.class));
++            Maybe<RegisteredType> item = RegisteredTypes.tryValidate(mgmt.getTypeRegistry().get(policyType), RegisteredTypeLoadingContexts.spec(Policy.class));
+             PolicySpec<?> spec;
 -            if (item!=null) {
 -                spec = mgmt.getTypeRegistry().createSpec(item, null, PolicySpec.class);
++            if (!item.isNull()) {
++                // throw error if absent for any reason other than null
++                spec = mgmt.getTypeRegistry().createSpec(item.get(), null, PolicySpec.class);
+             } else {
+                 Class<? extends Policy> type = decoLoader.getType(Policy.class);
+                 spec = PolicySpec.create(type)
+                         .parameters(BasicSpecParameter.fromClass(mgmt, type));
+             }
+             spec.configure( decoLoader.getConfigMap() );
+             decorations.add(spec);
+         }
+     }
+ 
+     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");
+             Class<? extends Enricher> type = decoLoader.getType(Enricher.class);
+             decorations.add(EnricherSpec.create(type)
+                 .configure(decoLoader.getConfigMap())
+                 .parameters(BasicSpecParameter.fromClass(instantiator.loader.getManagementContext(), type)));
+         }
+     }
+     
+     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));
+         }
+     }
+ 
+     // Not much value from extending from BrooklynEntityDecorationResolver, but let's not break the convention
+     public static class SpecParameterResolver extends BrooklynEntityDecorationResolver<SpecParameter<?>> {
+ 
+         protected SpecParameterResolver(BrooklynYamlTypeInstantiator.Factory instantiator) { super(instantiator); }
+         @Override protected String getDecorationKind() { return "Spec Parameter initializer"; }
+ 
+         @Override
+         public void decorate(EntitySpec<?> entitySpec, ConfigBag attrs) {
+             List<? extends SpecParameter<?>> explicitParams = buildListOfTheseDecorationsFromEntityAttributes(attrs);
+             if (!explicitParams.isEmpty()) {
+                 entitySpec.parameters(explicitParams);
+             }
+             if (entitySpec.getParameters().isEmpty()) {
+                 entitySpec.parameters(BasicSpecParameter.fromSpec(instantiator.loader.getManagementContext(), entitySpec));
+             }
+         }
+ 
+         @Override
+         protected List<SpecParameter<?>> buildListOfTheseDecorationsFromIterable(Iterable<?> value) {
+             return BasicSpecParameter.fromConfigList(ImmutableList.copyOf(value), instantiator.loader);
+         }
+ 
+         @Override
+         protected Object getDecorationAttributeJsonValue(ConfigBag attrs) {
+             return attrs.getStringKey(BrooklynCampReservedKeys.BROOKLYN_PARAMETERS);
+         }
+ 
+         @Override
+         protected void addDecorationFromJsonMap(Map<?, ?> decorationJson, List<SpecParameter<?>> decorations) {
+             throw new IllegalStateException("Not called");
+         }
+     }
+ 
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
----------------------------------------------------------------------
diff --cc brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
index 0000000,06dfaa3..909564c
mode 000000,100644..100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
@@@ -1,0 -1,174 +1,172 @@@
+ /*
+  * 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;
+ 
+ import java.io.Reader;
+ import java.io.StringReader;
+ import java.util.Set;
+ 
+ import org.apache.brooklyn.api.catalog.BrooklynCatalog;
+ import org.apache.brooklyn.api.entity.Application;
+ import org.apache.brooklyn.api.entity.Entity;
+ import org.apache.brooklyn.api.entity.EntitySpec;
+ import org.apache.brooklyn.api.mgmt.ManagementContext;
+ import org.apache.brooklyn.api.mgmt.Task;
+ import org.apache.brooklyn.camp.brooklyn.spi.creation.CampTypePlanTransformer;
+ import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
+ import org.apache.brooklyn.core.entity.Entities;
+ import org.apache.brooklyn.core.entity.trait.Startable;
+ import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
+ import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
+ import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+ import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+ import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts;
+ import org.apache.brooklyn.util.collections.MutableMap;
+ import org.apache.brooklyn.util.core.ResourceUtils;
+ import org.apache.brooklyn.util.stream.Streams;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+ import org.testng.annotations.AfterMethod;
+ import org.testng.annotations.BeforeMethod;
+ 
+ import com.google.common.base.Joiner;
+ 
+ public abstract class AbstractYamlTest {
+ 
+     private static final Logger LOG = LoggerFactory.getLogger(AbstractYamlTest.class);
+     protected static final String TEST_VERSION = "0.1.2";
+ 
+     private ManagementContext brooklynMgmt;
+     protected BrooklynCatalog catalog;
+     protected BrooklynCampPlatform platform;
+     protected BrooklynCampPlatformLauncherNoServer launcher;
+     private boolean forceUpdate;
+     
+     public AbstractYamlTest() {
+         super();
+     }
+ 
+     protected ManagementContext mgmt() { return brooklynMgmt; }
+     
+     @BeforeMethod(alwaysRun = true)
+     public void setUp() {
+         forceUpdate = false;
+         launcher = new BrooklynCampPlatformLauncherNoServer() {
+             @Override
+             protected LocalManagementContext newMgmtContext() {
+                 return newTestManagementContext();
+             }
+         };
+         launcher.launch();
+         brooklynMgmt = launcher.getBrooklynMgmt();
+         catalog = brooklynMgmt.getCatalog();
+         platform = launcher.getCampPlatform();
+     }
+ 
+     protected LocalManagementContext newTestManagementContext() {
+         // TODO they don't all need osgi, just a few do, so could speed it up by specifying when they do
+         return LocalManagementContextForTests.newInstanceWithOsgi();
+     }
+     
+     @AfterMethod(alwaysRun = true)
+     public void tearDown() {
+         if (brooklynMgmt != null) Entities.destroyAll(brooklynMgmt);
+         if (launcher != null) launcher.stopServers();
+     }
+ 
+     protected void waitForApplicationTasks(Entity app) {
+         Set<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(brooklynMgmt.getExecutionManager(), app);
+         getLogger().info("Waiting on " + tasks.size() + " task(s)");
+         for (Task<?> t : tasks) {
+             t.blockUntilEnded();
+         }
+     }
+ 
+     protected Reader loadYaml(String yamlFileName, String ...extraLines) throws Exception {
+         String input = new ResourceUtils(this).getResourceAsString(yamlFileName).trim();
+         StringBuilder builder = new StringBuilder(input);
+         for (String l: extraLines)
+             builder.append("\n").append(l);
+         return new StringReader(builder.toString());
+     }
+     
+     protected Entity createAndStartApplication(String... multiLineYaml) throws Exception {
+         return createAndStartApplication(joinLines(multiLineYaml));
+     }
+     
 -    protected Entity createAndStartApplication(String input) throws Exception {
 -        return createAndStartApplication(new StringReader(input));
++    protected Entity createAndStartApplication(Reader input) throws Exception {
++        return createAndStartApplication(Streams.readFully(input));
+     }
+ 
 -    protected Entity createAndStartApplication(Reader input) throws Exception {
++    protected Entity createAndStartApplication(String input) throws Exception {
+         EntitySpec<?> spec = 
 -            mgmt().getTypeRegistry().createSpecFromPlan(CampTypePlanTransformer.FORMAT, Streams.readFully(input), RegisteredTypeLoadingContexts.spec(Application.class), EntitySpec.class);
++            mgmt().getTypeRegistry().createSpecFromPlan(CampTypePlanTransformer.FORMAT, input, RegisteredTypeLoadingContexts.spec(Application.class), EntitySpec.class);
+         final Entity app = brooklynMgmt.getEntityManager().createEntity(spec);
+         // start the app (happens automatically if we use camp to instantiate, but not if we use crate spec approach)
+         app.invoke(Startable.START, MutableMap.<String,String>of()).get();
+         return app;
+     }
+ 
+     protected Entity createStartWaitAndLogApplication(Reader input) throws Exception {
+         Entity app = createAndStartApplication(input);
+         waitForApplicationTasks(app);
 -
+         getLogger().info("App started: "+app);
 -        
+         return app;
+     }
+ 
+     protected EntitySpec<?> createAppEntitySpec(String... yaml) {
+         return EntityManagementUtils.createEntitySpecForApplication(mgmt(), joinLines(yaml));
+     }
+ 
+     protected void addCatalogItems(Iterable<String> catalogYaml) {
+         addCatalogItems(joinLines(catalogYaml));
+     }
+ 
+     protected void addCatalogItems(String... catalogYaml) {
+         addCatalogItems(joinLines(catalogYaml));
+     }
+ 
+     protected void addCatalogItems(String catalogYaml) {
+         mgmt().getCatalog().addItems(catalogYaml, forceUpdate);
+     }
+ 
+     protected void deleteCatalogEntity(String catalogItem) {
+         mgmt().getCatalog().deleteCatalogItem(catalogItem, TEST_VERSION);
+     }
+ 
+     protected Logger getLogger() {
+         return LOG;
+     }
+ 
+     protected String joinLines(Iterable<String> catalogYaml) {
+         return Joiner.on("\n").join(catalogYaml);
+     }
+ 
+     protected String joinLines(String... catalogYaml) {
+         return Joiner.on("\n").join(catalogYaml);
+     }
+ 
+     protected String ver(String id) {
+         return CatalogUtils.getVersionedId(id, TEST_VERSION);
+     }
+ 
+     public void forceCatalogUpdate() {
+         forceUpdate = true;
+     }
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java
----------------------------------------------------------------------
diff --cc brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java
index 0000000,e075080..210f158
mode 000000,100644..100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java
@@@ -1,0 -1,202 +1,218 @@@
+ /*
+  * 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;
+ 
+ import static org.testng.Assert.assertEquals;
+ import static org.testng.Assert.fail;
+ 
+ import java.io.StringReader;
+ import java.util.Map;
+ 
+ import com.google.common.collect.Iterables;
+ import org.apache.brooklyn.api.entity.Entity;
++import org.apache.brooklyn.api.mgmt.ExecutionContext;
+ import org.apache.brooklyn.api.mgmt.ManagementContext;
+ import org.apache.brooklyn.config.ConfigKey;
+ import org.apache.brooklyn.core.config.ConfigKeys;
+ import org.apache.brooklyn.core.config.external.AbstractExternalConfigSupplier;
+ import org.apache.brooklyn.core.config.external.ExternalConfigSupplier;
+ import org.apache.brooklyn.core.internal.BrooklynProperties;
++import org.apache.brooklyn.core.location.AbstractLocation;
+ import org.apache.brooklyn.core.location.cloud.CloudLocationConfig;
++import org.apache.brooklyn.core.mgmt.internal.CampYamlParser;
+ import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+ import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+ import org.apache.brooklyn.core.test.entity.TestApplication;
+ import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
++import org.apache.brooklyn.util.core.task.DeferredSupplier;
++import org.apache.brooklyn.util.core.task.Tasks;
+ import org.apache.brooklyn.util.exceptions.Exceptions;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+ import org.testng.annotations.Test;
+ 
+ import com.google.common.base.Joiner;
+ 
+ @Test
+ public class ExternalConfigYamlTest extends AbstractYamlTest {
+     private static final Logger log = LoggerFactory.getLogger(ExternalConfigYamlTest.class);
+ 
+     @Override
+     protected LocalManagementContext newTestManagementContext() {
+         BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
+         props.put("brooklyn.external.myprovider", MyExternalConfigSupplier.class.getName());
+         props.put("brooklyn.external.myprovider.mykey", "myval");
+         props.put("brooklyn.external.myproviderWithoutMapArg", MyExternalConfigSupplierWithoutMapArg.class.getName());
+ 
+         return LocalManagementContextForTests.builder(true)
+                 .useProperties(props)
+                 .build();
+     }
+ 
+     @Test
++    public void testCampYamlParserHandlesExternalisedConfig() throws Exception {
++        CampYamlParser parser = mgmt().getConfig().getConfig(CampYamlParser.YAML_PARSER_KEY);
++        
++        DeferredSupplier<?> supplier = (DeferredSupplier<?>) parser.parse("$brooklyn:external(\"myprovider\", \"mykey\")");
++        
++        ExecutionContext exec = mgmt().getServerExecutionContext();
++        String result = Tasks.resolveValue(supplier, String.class, exec);
++        assertEquals(result, "myval");
++    }
++
++    @Test
+     public void testExternalisedConfigReferencedFromYaml() throws Exception {
+         ConfigKey<String> MY_CONFIG_KEY = ConfigKeys.newStringConfigKey("my.config.key");
+ 
+         String yaml = Joiner.on("\n").join(
+             "services:",
+             "- serviceType: org.apache.brooklyn.core.test.entity.TestApplication",
+             "  brooklyn.config:",
+             "    my.config.key: $brooklyn:external(\"myprovider\", \"mykey\")");
+ 
+         TestApplication app = (TestApplication) createAndStartApplication(new StringReader(yaml));
+         waitForApplicationTasks(app);
+ 
+         assertEquals(app.getConfig(MY_CONFIG_KEY), "myval");
+     }
+ 
+     @Test
+     public void testExternalisedLocationConfigReferencedFromYaml() throws Exception {
+         ConfigKey<String> MY_CONFIG_KEY = ConfigKeys.newStringConfigKey("my.config.key");
+ 
+         String yaml = Joiner.on("\n").join(
+             "services:",
+             "- type: org.apache.brooklyn.core.test.entity.TestApplication",
+             "location:",
+             "  localhost:",
+             "    my.config.key: $brooklyn:external(\"myprovider\", \"mykey\")");
+ 
+         TestApplication app = (TestApplication) createAndStartApplication(new StringReader(yaml));
+         waitForApplicationTasks(app);
+         assertEquals(Iterables.getOnlyElement( app.getLocations() ).config().get(MY_CONFIG_KEY), "myval");
+     }
+ 
+     @Test(groups="Integration")
+     public void testExternalisedLocationConfigSetViaProvisioningPropertiesReferencedFromYaml() throws Exception {
+         String yaml = Joiner.on("\n").join(
+             "services:",
+             "- type: "+EmptySoftwareProcess.class.getName(),
+             "  provisioning.properties:",
+             "    credential: $brooklyn:external(\"myprovider\", \"mykey\")",
+             "location: localhost");
+ 
+         Entity app = createAndStartApplication(new StringReader(yaml));
+         waitForApplicationTasks(app);
+         Entity entity = Iterables.getOnlyElement( app.getChildren() );
+         assertEquals(Iterables.getOnlyElement( entity.getLocations() ).config().get(CloudLocationConfig.ACCESS_CREDENTIAL), "myval");
+     }
+ 
+     @Test
+     public void testExternalisedConfigFromSupplierWithoutMapArg() throws Exception {
+         ConfigKey<String> MY_CONFIG_KEY = ConfigKeys.newStringConfigKey("my.config.key");
+ 
+         String yaml = Joiner.on("\n").join(
+             "services:",
+             "- serviceType: org.apache.brooklyn.core.test.entity.TestApplication",
+             "  brooklyn.config:",
+             "    my.config.key: $brooklyn:external(\"myproviderWithoutMapArg\", \"mykey\")");
+ 
+         TestApplication app = (TestApplication) createAndStartApplication(new StringReader(yaml));
+         waitForApplicationTasks(app);
+ 
+         assertEquals(app.getConfig(MY_CONFIG_KEY), "myHardcodedVal");
+     }
+ 
+     @Test
+     public void testWhenExternalisedConfigSupplierDoesNotExist() throws Exception {
+         BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
+         props.put("brooklyn.external.myprovider", "wrong.classname.DoesNotExist");
+ 
+         try {
+             LocalManagementContextForTests.builder(true)
+                     .useProperties(props)
+                     .build();
+             fail();
+         } catch (Exception e) {
+             if (Exceptions.getFirstThrowableOfType(e, ClassNotFoundException.class) == null) {
+                 throw e;
+             }
+         }
+     }
+ 
+     @Test
+     public void testWhenExternalisedConfigSupplierDoesNotHavingRightConstructor() throws Exception {
+         BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
+         props.put("brooklyn.external.myprovider", MyExternalConfigSupplierWithWrongConstructor.class.getName());
+ 
+         try {
+             LocalManagementContext mgmt2 = LocalManagementContextForTests.builder(true)
+                     .useProperties(props)
+                     .build();
+             mgmt2.terminate();
+             fail();
+         } catch (Exception e) {
+             if (!e.toString().contains("No matching constructor")) {
+                 throw e;
+             }
+         }
+     }
+ 
+     @Override
+     protected Logger getLogger() {
+         return log;
+     }
+ 
+     public static class MyExternalConfigSupplier extends AbstractExternalConfigSupplier {
+         private final Map<String, String> conf;
+ 
+         public MyExternalConfigSupplier(ManagementContext mgmt, String name, Map<String, String> conf) {
+             super(mgmt, name);
+             this.conf = conf;
+         }
+ 
+         @Override public String get(String key) {
+             return conf.get(key);
+         }
+     }
+ 
+     public static class MyExternalConfigSupplierWithoutMapArg extends AbstractExternalConfigSupplier {
+         public MyExternalConfigSupplierWithoutMapArg(ManagementContext mgmt, String name) {
+             super(mgmt, name);
+         }
+ 
+         @Override public String get(String key) {
+             return key.equals("mykey") ? "myHardcodedVal" : null;
+         }
+     }
+ 
+     public static class MyExternalConfigSupplierWithWrongConstructor implements ExternalConfigSupplier {
+         public MyExternalConfigSupplierWithWrongConstructor(double d) {
+         }
+ 
+         @Override public String getName() {
+             return "myname";
+         }
+ 
+         @Override public String get(String key) {
+             return null;
+         }
+     }
+ 
+ }


Mime
View raw message