brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [18/19] git commit: address most code review comments, including refactoring to move common add-children logic and some app creation logic to new class EntityManagementUtils
Date Tue, 09 Sep 2014 22:49:21 GMT
address most code review comments, including refactoring to move common add-children logic and some app creation logic to new class EntityManagementUtils


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

Branch: refs/heads/master
Commit: b05a9c11d30fdfc8aefde2c386f34d8bab4efcdd
Parents: f7ce1a2
Author: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Authored: Tue Sep 9 23:08:48 2014 +0100
Committer: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Committed: Tue Sep 9 23:42:06 2014 +0100

----------------------------------------------------------------------
 .../entity/basic/ServiceStateLogic.java         |   4 +-
 .../entity/effector/AddChildrenEffector.java    | 202 +-----------
 .../internal/EntityManagementUtils.java         | 304 +++++++++++++++++++
 .../entity/proxy/nginx/NginxControllerImpl.java |   2 +-
 .../BrooklynAssemblyTemplateInstantiator.java   |  38 +--
 .../webapp/assets/js/libs/brooklyn-utils.js     |  10 +-
 .../webapp/assets/js/view/add-child-invoke.js   |   3 +-
 .../webapp/assets/js/view/change-name-invoke.js |  13 +-
 .../webapp/assets/js/view/entity-advanced.js    |  22 +-
 .../main/webapp/assets/js/view/entity-config.js |  10 +-
 .../webapp/assets/js/view/entity-summary.js     |   5 +-
 .../main/webapp/assets/js/view/policy-new.js    |   1 -
 .../webapp/assets/tpl/apps/add-child-modal.html |   2 +-
 .../main/webapp/assets/tpl/apps/advanced.html   |   5 +-
 .../main/webapp/assets/tpl/apps/summary.html    |   2 +-
 .../specs/view/entity-details-spec.js           |   5 -
 .../main/java/brooklyn/rest/api/EntityApi.java  |   2 +-
 .../java/brooklyn/rest/api/EntityConfigApi.java |   2 +-
 usage/rest-server/pom.xml                       |  14 -
 .../rest/resources/ApplicationResource.java     |  35 +--
 .../brooklyn/rest/resources/EntityResource.java |  12 +-
 .../rest/testing/BrooklynRestApiTest.java       |   2 +-
 22 files changed, 381 insertions(+), 314 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java b/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java
index 7448445..3510121 100644
--- a/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java
+++ b/core/src/main/java/brooklyn/entity/basic/ServiceStateLogic.java
@@ -449,10 +449,10 @@ public class ServiceStateLogic {
                     // quorate
                     return null;
 
-                if (onesNotHealthy.size()==0)
+                if (onesNotHealthy.isEmpty())
                     return "Not enough entities running to be quorate";
             } else {
-                if (onesNotHealthy.size()==0)
+                if (onesNotHealthy.isEmpty())
                     return null;
             }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/core/src/main/java/brooklyn/entity/effector/AddChildrenEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/AddChildrenEffector.java b/core/src/main/java/brooklyn/entity/effector/AddChildrenEffector.java
index f216a53..5ce5bde 100644
--- a/core/src/main/java/brooklyn/entity/effector/AddChildrenEffector.java
+++ b/core/src/main/java/brooklyn/entity/effector/AddChildrenEffector.java
@@ -18,49 +18,22 @@
  */
 package brooklyn.entity.effector;
 
-import io.brooklyn.camp.CampPlatform;
-import io.brooklyn.camp.spi.AssemblyTemplate;
-import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator;
-
-import java.io.Reader;
-import java.io.StringReader;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.camp.brooklyn.api.AssemblyTemplateSpecInstantiator;
-import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.Effector;
 import brooklyn.entity.Entity;
-import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.EntityFunctions;
-import brooklyn.entity.basic.EntityLocal;
 import brooklyn.entity.effector.Effectors.EffectorBuilder;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.trait.Startable;
-import brooklyn.management.ManagementContext;
-import brooklyn.management.Task;
-import brooklyn.management.classloading.BrooklynClassLoadingContext;
-import brooklyn.management.classloading.JavaBrooklynClassLoadingContext;
-import brooklyn.util.collections.MutableList;
+import brooklyn.management.internal.EntityManagementUtils;
+import brooklyn.management.internal.EntityManagementUtils.CreationResult;
 import brooklyn.util.config.ConfigBag;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.task.TaskBuilder;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.text.Strings;
-import brooklyn.util.time.Duration;
 
 import com.google.common.annotations.Beta;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
 import com.google.gson.Gson;
 
 /** Entity initializer which defines an effector which adds a child blueprint to an entity.
@@ -136,175 +109,10 @@ public class AddChildrenEffector extends AddEffector {
             }
 
             log.debug(this+" adding children to "+entity()+":\n"+blueprint);
-            AddChildrenResult result = addChildren(entity(), blueprint, autostart, Duration.ZERO);
-            log.debug(this+" added children to "+entity()+": "+result.getChildren());
-            return result.getTask().getUnchecked();
-        }
-    }
-
-    public static class AddChildrenResult {
-        public final List<Entity> children;
-        public final Task<List<String>> task;
-        public AddChildrenResult(List<Entity> children, Task<List<String>> task) {
-            super();
-            this.children = children;
-            this.task = task;
-        }
-        public List<Entity> getChildren() {
-          return children;
-      }
-        public Task<List<String>> getTask() {
-          return task;
-      }
-    }
-    
-    public static AddChildrenResult addChildren(final EntityLocal parent, String yaml, Boolean start, Duration timeout) {
-        log.debug("Creating child of "+parent+" from yaml:\n{}", yaml);
-        
-        ManagementContext mgmt = parent.getApplication().getManagementContext();
-        CampPlatform camp = BrooklynServerConfig.getCampPlatform(mgmt).get();
-        
-        Reader input = new StringReader(yaml);
-        AssemblyTemplate at = camp.pdp().registerDeploymentPlan(input);
-
-        AssemblyTemplateInstantiator instantiator;
-        try {
-            instantiator = at.getInstantiator().newInstance();
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-        if (instantiator instanceof AssemblyTemplateSpecInstantiator) {
-            BrooklynClassLoadingContext loader = JavaBrooklynClassLoadingContext.newDefault(mgmt);
-            EntitySpec<?> specA = ((AssemblyTemplateSpecInstantiator) instantiator).createSpec(at, camp, loader, false);
-
-            boolean promoted;
-
-            // see whether we can promote children
-            List<EntitySpec<?>> specs = MutableList.of();
-            if (hasNoNameOrCustomKeysOrRoot(at, specA)) {
-                // we can promote
-                promoted = true;
-                for (EntitySpec<?> specC: specA.getChildren()) {
-                    collapseSpec(specA, specC);
-                    specs.add(specC);
-                }
-            } else {
-                // if not promoting, set a nice name if needed
-                if (Strings.isEmpty(specA.getDisplayName())) {
-                    int size = specA.getChildren().size();
-                    String childrenCountString = size+" "+(size!=1 ? "children" : "child");
-                    specA.displayName("Dynamically added "+childrenCountString);
-                }
-                promoted = false;
-                specs.add(specA);
-            }
-
-            final List<Entity> children = MutableList.of();
-            for (EntitySpec<?> spec: specs) {
-                Entity child = (Entity)parent.addChild(spec);
-                Entities.manage(child);
-                children.add(child);
-            }
-
-            String childrenCountString;
-            if (promoted) {
-                int size = children.size();
-                childrenCountString = size+" "+(size!=1 ? "children" : "child"); 
-            } else {
-                int size = specA.getChildren().size();
-                childrenCountString = "entity with "+size+" "+(size!=1 ? "children" : "child");
-            }
-
-            TaskBuilder<List<String>> taskM = Tasks.<List<String>>builder().name("add children")
-                .dynamic(true)
-                .tag(BrooklynTaskTags.NON_TRANSIENT_TASK_TAG)
-                .body(new Callable<List<String>>() {
-                    @Override public List<String> call() throws Exception {
-                        return ImmutableList.copyOf(Iterables.transform(children, EntityFunctions.id()));
-                    }})
-                    .description("Add" + (start==null ? " and potentially start" : start ? " and start" : "") + " "+childrenCountString);
-            TaskBuilder<?> taskS = Tasks.builder().parallel(true).name("add (parallel)")
-                .description(
-                    (start==null ? "Add or start" : start ? "Start" : "Add")+" each new entity");
-
-            // should we autostart?
-            for (Entity child: children) {
-                if (Boolean.TRUE.equals(start) || (start==null && child instanceof Startable)) {
-                    taskS.add(Effectors.invocation(child, Startable.START, ImmutableMap.of("locations", ImmutableList.of())));
-                } else {
-                    taskS.add(Tasks.builder().name("create").description("Created and added as child of "+parent)
-                        .body(new Runnable() { public void run() {} })
-                        .tag(BrooklynTaskTags.tagForTargetEntity(child))
-                        .build());
-                }
-            }
-            taskM.add(taskS.build());
-            Task<List<String>> task = Entities.submit(parent, taskM.build());
-
-            // wait a few ms in case start is trivially simple, save the client a call to get the task result
-            task.blockUntilEnded(timeout);
-
-            return new AddChildrenResult(children, task);
-        } else {
-            throw new IllegalStateException("Spec could not be parsed to supply a compatible instantiator");
-        }
-    }
-
-    /** worker method to combine specs */
-    @Beta //where should this live long-term?
-    public static void collapseSpec(EntitySpec<?> sourceToBeCollapsed, EntitySpec<?> targetToBeExpanded) {
-        if (Strings.isEmpty(targetToBeExpanded.getDisplayName()))
-            targetToBeExpanded.displayName(sourceToBeCollapsed.getDisplayName());
-        if (!sourceToBeCollapsed.getLocations().isEmpty())
-            targetToBeExpanded.locations(sourceToBeCollapsed.getLocations());
-
-        // 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) 
-        targetToBeExpanded.configure(sourceToBeCollapsed.getConfig());
-        targetToBeExpanded.configure(sourceToBeCollapsed.getFlags());
-        
-        // TODO copying tags to all entities is not ideal;
-        // in particular the BrooklynTags.YAML_SPEC tag will show all entities if the root has multiple
-        targetToBeExpanded.tags(sourceToBeCollapsed.getTags());
-    }
-
-    /** worker method to help determine whether child/children can be promoted */
-    @Beta //where should this live long-term?
-    public static boolean hasNoNameOrCustomKeysOrRoot(AssemblyTemplate template, EntitySpec<?> spec) {
-        if (!Strings.isEmpty(template.getName())) {
-            if (spec.getChildren().size()==1) {
-                String childName = Iterables.getOnlyElement(spec.getChildren()).getDisplayName();
-                if (Strings.isEmpty(childName) || childName.equals(template.getName())) {
-                    // if child has no name, or it's the same, could still promote
-                } else {
-                    return false;
-                }
-            } else {
-                // if name set at root and promoting children would be ambiguous, do not promote 
-                return false;
-            }
-        } else if (spec.getChildren().size()>1) {
-            // don't allow multiple children if a name is specified as a root
-            return false;
-        }
-        
-        Set<String> rootAttrs = template.getCustomAttributes().keySet();
-        for (String rootAttr: rootAttrs) {
-            if (rootAttr.equals("brooklyn.catalog") || rootAttr.equals("brooklyn.config")) {
-                // these do not block promotion
-                continue;
-            }
-            if (rootAttr.startsWith("brooklyn.")) {
-                // any others in 'brooklyn' namespace will block promotion
-                return false;
-            }
-            // location is allowed in both, and is copied on promotion
-            // (name also copied)
-            // others are root currently are ignored on promotion; they are usually metadata
-            // TODO might be nice to know what we are excluding
+            CreationResult<List<Entity>, List<String>> result = EntityManagementUtils.addChildren(entity(), blueprint, autostart);
+            log.debug(this+" added children to "+entity()+": "+result.get());
+            return result.task().getUnchecked();
         }
-        
-        return true;
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/core/src/main/java/brooklyn/management/internal/EntityManagementUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/EntityManagementUtils.java b/core/src/main/java/brooklyn/management/internal/EntityManagementUtils.java
new file mode 100644
index 0000000..0db77ab
--- /dev/null
+++ b/core/src/main/java/brooklyn/management/internal/EntityManagementUtils.java
@@ -0,0 +1,304 @@
+/*
+ * 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 brooklyn.management.internal;
+
+import io.brooklyn.camp.CampPlatform;
+import io.brooklyn.camp.spi.Assembly;
+import io.brooklyn.camp.spi.AssemblyTemplate;
+import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator;
+
+import java.io.StringReader;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.camp.brooklyn.api.AssemblyTemplateSpecInstantiator;
+import brooklyn.config.BrooklynServerConfig;
+import brooklyn.entity.Application;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityFunctions;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.entity.effector.Effectors;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.trait.Startable;
+import brooklyn.management.ManagementContext;
+import brooklyn.management.Task;
+import brooklyn.management.classloading.BrooklynClassLoadingContext;
+import brooklyn.management.classloading.JavaBrooklynClassLoadingContext;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.task.TaskBuilder;
+import brooklyn.util.task.Tasks;
+import brooklyn.util.text.Strings;
+import brooklyn.util.time.Duration;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+/** Utility methods for working with entities and applications */
+public class EntityManagementUtils {
+
+    private static final Logger log = LoggerFactory.getLogger(EntityManagementUtils.class);
+
+    /** creates an application from the given app spec, managed by the given management context */
+    public static <T extends Application> T createUnstarted(ManagementContext mgmt, EntitySpec<T> spec) {
+        T app = mgmt.getEntityManager().createEntity(spec);
+        Entities.startManagement(app, mgmt);
+        return app;
+    }
+
+    /** convenience for accessing camp */
+    public static Maybe<CampPlatform> getCampPlatform(ManagementContext mgmt) {
+        return BrooklynServerConfig.getCampPlatform(mgmt);
+    }
+    
+    /** as {@link #createApplication(ManagementContext, EntitySpec)} but for a YAML spec */
+    public static <T extends Application> T createUnstarted(ManagementContext mgmt, String yaml) {
+        AssemblyTemplate at = getCampPlatform(mgmt).get().pdp().registerDeploymentPlan( new StringReader(yaml) );
+        return createUnstarted(mgmt, at);
+    }
+    
+    /** as {@link #createApplication(ManagementContext, EntitySpec)} but for an assembly template */
+    @SuppressWarnings("unchecked")
+    public static <T extends Application> T createUnstarted(ManagementContext mgmt, AssemblyTemplate at) {
+        CampPlatform camp = getCampPlatform(mgmt).get();
+        AssemblyTemplateInstantiator instantiator;
+        try {
+            instantiator = at.getInstantiator().newInstance();
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+        Assembly assembly;
+        if (instantiator instanceof AssemblyTemplateSpecInstantiator) {
+            BrooklynClassLoadingContext loader = JavaBrooklynClassLoadingContext.newDefault(mgmt);
+            
+            EntitySpec<?> spec = ((AssemblyTemplateSpecInstantiator) instantiator).createSpec(at, camp, loader, true);
+            Entity app = mgmt.getEntityManager().createEntity(spec);
+            Entities.startManagement(app);
+            return (T) app;
+        } else {
+            assembly = instantiator.instantiate(at, camp);
+            return (T) mgmt.getEntityManager().getEntity(assembly.getId());
+        }
+    }
+    
+    /** container for operation which creates something and which wants to return both
+     * the items created and any pending create/start task */
+    public static class CreationResult<T,U> {
+        private final T thing;
+        @Nullable private final Task<U> task;
+        public CreationResult(T thing, Task<U> task) {
+            super();
+            this.thing = thing;
+            this.task = task;
+        }
+        
+        protected static <T,U> CreationResult<T,U> of(T thing, @Nullable Task<U> task) {
+            return new CreationResult<T,U>(thing, task);
+        }
+        
+        /** returns the thing/things created */
+        @Nullable public T get() { return thing; }
+        /** associated task, ie the one doing the creation/starting */
+        public Task<U> task() { return task; }
+        public CreationResult<T,U> blockUntilComplete(Duration timeout) { if (task!=null) task.blockUntilEnded(timeout); return this; }
+        public CreationResult<T,U> blockUntilComplete() { if (task!=null) task.blockUntilEnded(); return this; }
+    }
+
+    public static <T extends Application> CreationResult<T,Void> createStarting(ManagementContext mgmt, EntitySpec<T> appSpec) {
+        return start(createUnstarted(mgmt, appSpec));
+    }
+
+    public static CreationResult<? extends Application,Void> createStarting(ManagementContext mgmt, String appSpec) {
+        return start(createUnstarted(mgmt, appSpec));
+    }
+
+    public static CreationResult<? extends Application,Void> createStarting(ManagementContext mgmt, AssemblyTemplate at) {
+        return start(createUnstarted(mgmt, at));
+    }
+
+    public static <T extends Application> CreationResult<T,Void> start(T app) {
+        Task<Void> task = Entities.invokeEffector((EntityLocal)app, app, Startable.START,
+            // locations already set in the entities themselves;
+            // TODO make it so that this arg does not have to be supplied to START !
+            MutableMap.of("locations", MutableList.of()));
+        return CreationResult.of(app, task);
+    }
+    
+    public static CreationResult<List<Entity>, List<String>> addChildren(final EntityLocal parent, String yaml, Boolean start) {
+        if (Boolean.FALSE.equals(start))
+            return CreationResult.of(addChildrenUnstarted(parent, yaml), null);
+        return addChildrenStarting(parent, yaml);
+    }
+    
+    /** adds entities from the given yaml, under the given parent; but does not start them */
+    public static List<Entity> addChildrenUnstarted(final EntityLocal parent, String yaml) {
+        log.debug("Creating child of "+parent+" from yaml:\n{}", yaml);
+        
+        ManagementContext mgmt = parent.getApplication().getManagementContext();
+        CampPlatform camp = BrooklynServerConfig.getCampPlatform(mgmt).get();
+        
+        AssemblyTemplate at = camp.pdp().registerDeploymentPlan( new StringReader(yaml) );
+
+        AssemblyTemplateInstantiator instantiator;
+        try {
+            instantiator = at.getInstantiator().newInstance();
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+        if (instantiator instanceof AssemblyTemplateSpecInstantiator) {
+            BrooklynClassLoadingContext loader = JavaBrooklynClassLoadingContext.newDefault(mgmt);
+            EntitySpec<?> specA = ((AssemblyTemplateSpecInstantiator) instantiator).createSpec(at, camp, loader, false);
+
+            // see whether we can promote children
+            List<EntitySpec<?>> specs = MutableList.of();
+            if (hasNoNameOrCustomKeysOrRoot(at, specA)) {
+                // we can promote
+                for (EntitySpec<?> specC: specA.getChildren()) {
+                    collapseSpec(specA, specC);
+                    specs.add(specC);
+                }
+            } else {
+                // if not promoting, set a nice name if needed
+                if (Strings.isEmpty(specA.getDisplayName())) {
+                    int size = specA.getChildren().size();
+                    String childrenCountString = size+" "+(size!=1 ? "children" : "child");
+                    specA.displayName("Dynamically added "+childrenCountString);
+                }
+                specs.add(specA);
+            }
+
+            final List<Entity> children = MutableList.of();
+            for (EntitySpec<?> spec: specs) {
+                Entity child = (Entity)parent.addChild(spec);
+                Entities.manage(child);
+                children.add(child);
+            }
+            
+            return children;
+        } else {
+            throw new IllegalStateException("Spec could not be parsed to supply a compatible instantiator");
+        }
+    }
+
+    public static CreationResult<List<Entity>,List<String>> addChildrenStarting(final EntityLocal parent, String yaml) {
+        final List<Entity> children = addChildrenUnstarted(parent, yaml);
+        String childrenCountString;
+
+        int size = children.size();
+        childrenCountString = size+" "+(size!=1 ? "children" : "child"); 
+
+        TaskBuilder<List<String>> taskM = Tasks.<List<String>>builder().name("add children")
+            .dynamic(true)
+            .tag(BrooklynTaskTags.NON_TRANSIENT_TASK_TAG)
+            .body(new Callable<List<String>>() {
+                @Override public List<String> call() throws Exception {
+                    return ImmutableList.copyOf(Iterables.transform(children, EntityFunctions.id()));
+                }})
+                .description("Add and start "+childrenCountString);
+
+        TaskBuilder<?> taskS = Tasks.builder().parallel(true).name("add (parallel)").description("Start each new entity");
+
+        // autostart if requested
+        for (Entity child: children) {
+            if (child instanceof Startable) {
+                taskS.add(Effectors.invocation(child, Startable.START, ImmutableMap.of("locations", ImmutableList.of())));
+            } else {
+                // include a task, just to give feedback in the GUI
+                taskS.add(Tasks.builder().name("create").description("Skipping start (not a Startable Entity)")
+                    .body(new Runnable() { public void run() {} })
+                    .tag(BrooklynTaskTags.tagForTargetEntity(child))
+                    .build());
+            }
+        }
+        taskM.add(taskS.build());
+        Task<List<String>> task = Entities.submit(parent, taskM.build());
+
+        return CreationResult.of(children, task);
+    }
+
+    /** worker method to combine specs */
+    @Beta //where should this live long-term?
+    public static void collapseSpec(EntitySpec<?> sourceToBeCollapsed, EntitySpec<?> targetToBeExpanded) {
+        if (Strings.isEmpty(targetToBeExpanded.getDisplayName()))
+            targetToBeExpanded.displayName(sourceToBeCollapsed.getDisplayName());
+        if (!sourceToBeCollapsed.getLocations().isEmpty())
+            targetToBeExpanded.locations(sourceToBeCollapsed.getLocations());
+
+        // 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) 
+        targetToBeExpanded.configure(sourceToBeCollapsed.getConfig());
+        targetToBeExpanded.configure(sourceToBeCollapsed.getFlags());
+        
+        // TODO copying tags to all entities is not ideal;
+        // in particular the BrooklynTags.YAML_SPEC tag will show all entities if the root has multiple
+        targetToBeExpanded.tags(sourceToBeCollapsed.getTags());
+    }
+
+    /** worker method to help determine whether child/children can be promoted */
+    @Beta //where should this live long-term?
+    public static boolean hasNoNameOrCustomKeysOrRoot(AssemblyTemplate template, EntitySpec<?> spec) {
+        if (!Strings.isEmpty(template.getName())) {
+            if (spec.getChildren().size()==1) {
+                String childName = Iterables.getOnlyElement(spec.getChildren()).getDisplayName();
+                if (Strings.isEmpty(childName) || childName.equals(template.getName())) {
+                    // if child has no name, or it's the same, could still promote
+                } else {
+                    return false;
+                }
+            } else {
+                // if name set at root and promoting children would be ambiguous, do not promote 
+                return false;
+            }
+        } else if (spec.getChildren().size()>1) {
+            // don't allow multiple children if a name is specified as a root
+            return false;
+        }
+        
+        Set<String> rootAttrs = template.getCustomAttributes().keySet();
+        for (String rootAttr: rootAttrs) {
+            if (rootAttr.equals("brooklyn.catalog") || rootAttr.equals("brooklyn.config")) {
+                // these do not block promotion
+                continue;
+            }
+            if (rootAttr.startsWith("brooklyn.")) {
+                // any others in 'brooklyn' namespace will block promotion
+                return false;
+            }
+            // location is allowed in both, and is copied on promotion
+            // (name also copied)
+            // others are root currently are ignored on promotion; they are usually metadata
+            // TODO might be nice to know what we are excluding
+        }
+        
+        return true;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxControllerImpl.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxControllerImpl.java b/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxControllerImpl.java
index 2b0edc5..b9d4ecf 100644
--- a/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxControllerImpl.java
+++ b/software/webapp/src/main/java/brooklyn/entity/proxy/nginx/NginxControllerImpl.java
@@ -110,7 +110,7 @@ public class NginxControllerImpl extends AbstractControllerImpl implements Nginx
         
         addEnricher(Enrichers.builder().updatingMap(Attributes.SERVICE_NOT_UP_INDICATORS)
             .from(NGINX_URL_ANSWERS_NICELY)
-            .computing(Functionals.ifNotEquals(true).value("URL where nginx listens is not answering correctly") )
+            .computing(Functionals.ifNotEquals(true).value("URL where nginx listens is not answering correctly (with expected header)") )
             .build());
         connectServiceUpIsRunning();
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
index d21259d..5e1a8ca 100644
--- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
+++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
@@ -47,18 +47,12 @@ import brooklyn.config.BrooklynServerConfig;
 import brooklyn.entity.Application;
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.BasicApplicationImpl;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.EntityLocal;
-import brooklyn.entity.effector.AddChildrenEffector;
 import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.trait.Startable;
 import brooklyn.management.ManagementContext;
-import brooklyn.management.Task;
 import brooklyn.management.classloading.BrooklynClassLoadingContext;
-import brooklyn.management.classloading.JavaBrooklynClassLoadingContext;
+import brooklyn.management.internal.EntityManagementUtils;
+import brooklyn.management.internal.EntityManagementUtils.CreationResult;
 import brooklyn.util.ResourceUtils;
-import brooklyn.util.collections.MutableList;
-import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.net.Urls;
@@ -76,23 +70,14 @@ public class BrooklynAssemblyTemplateInstantiator implements AssemblyTemplateSpe
     @Override
     public Assembly instantiate(AssemblyTemplate template, CampPlatform platform) {
         Application app = create(template, platform);
-        Task<?> task = start(app, platform);
-        log.info("CAMP created "+app+"; starting in "+task);
+        CreationResult<Application, Void> start = EntityManagementUtils.start(app);
+        log.debug("CAMP created "+app+"; starting in "+start.task());
         return platform.assemblies().get(app.getApplicationId());
     }
 
-    // note: based on BrooklynRestResourceUtils, but modified to not allow child entities (yet)
-    // (will want to revise that when building up from a non-brooklyn template)
     public Application create(AssemblyTemplate template, CampPlatform platform) {
-        ManagementContext mgmt = getBrooklynManagementContext(platform);
-        
-        BrooklynClassLoadingContext loader = JavaBrooklynClassLoadingContext.newDefault(mgmt);
-        EntitySpec<? extends Application> spec = createSpec(template, platform, loader, true);
-        
-        Application instance = mgmt.getEntityManager().createEntity(spec);
-        log.info("CAMP placing '{}' under management", instance);
-        Entities.startManagement(instance, mgmt);
-
+        Application instance = EntityManagementUtils.createUnstarted(getBrooklynManagementContext(platform), template);
+        log.debug("CAMP created {}", instance);
         return instance;
     }
     
@@ -100,13 +85,6 @@ public class BrooklynAssemblyTemplateInstantiator implements AssemblyTemplateSpe
         return ((HasBrooklynManagementContext)platform).getBrooklynManagementContext();
     }
     
-    public Task<?> start(Application app, CampPlatform platform) {
-        return Entities.invokeEffector((EntityLocal)app, app, Startable.START,
-            // locations already set in the entities themselves;
-            // TODO make it so that this arg does not have to be supplied to START !
-            MutableMap.of("locations", MutableList.of()));
-    }
-
     @SuppressWarnings("unchecked")
     public EntitySpec<? extends Application> createSpec(AssemblyTemplate template, CampPlatform platform, BrooklynClassLoadingContext loader, boolean autoUnwrapIfPossible) {
         log.debug("CAMP creating application instance for {} ({})", template.getId(), template);
@@ -129,7 +107,7 @@ public class BrooklynAssemblyTemplateInstantiator implements AssemblyTemplateSpe
             
             // if promoted, apply the transformations done to the app
             // (transformations will be done by the resolveSpec call above, but we are collapsing oldApp so transfer to app=newApp)
-            AddChildrenEffector.collapseSpec(oldApp, app);
+            EntityManagementUtils.collapseSpec(oldApp, app);
         }
         
         return app;
@@ -163,7 +141,7 @@ public class BrooklynAssemblyTemplateInstantiator implements AssemblyTemplateSpe
         if (childSpec.getType()==null || !Application.class.isAssignableFrom(childSpec.getType()))
             return false;
 
-        return AddChildrenEffector.hasNoNameOrCustomKeysOrRoot(template, app);
+        return EntityManagementUtils.hasNoNameOrCustomKeysOrRoot(template, app);
     }
 
     private List<EntitySpec<?>> buildTemplateServicesAsSpecs(BrooklynClassLoadingContext loader, AssemblyTemplate template, CampPlatform platform) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn-utils.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn-utils.js b/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn-utils.js
index acd8135..e85913f 100644
--- a/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn-utils.js
+++ b/usage/jsgui/src/main/webapp/assets/js/libs/brooklyn-utils.js
@@ -114,9 +114,17 @@ define([
      * alternate message if parsing fails or the parsed object has no message.
      * @param {jqXHR} xhrResponse
      * @param {string} alternateMessage
+     * @param {string} logMessage, if false or null, does not log; 
+     *   otherwise it logs a message and the xhrResponse, with logMessage (or with alternateMessage if logMessage is true)
      * @returns {*}
      */
-    Util.extractError = function (xhrResponse, alternateMessage) {
+    Util.extractError = function (xhrResponse, alternateMessage, logMessage) {
+        if (logMessage) {
+            if (logMessage==true) log("ERROR: "+alternateMessage);
+            else log("ERROR: "+logMessage);
+            log(xhrResponse);
+        }
+        
         try {
             var response = JSON.parse(xhrResponse.responseText);
             return response.message ? response.message : alternateMessage;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/jsgui/src/main/webapp/assets/js/view/add-child-invoke.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/add-child-invoke.js b/usage/jsgui/src/main/webapp/assets/js/view/add-child-invoke.js
index b29f295..ea7d6c9 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/add-child-invoke.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/add-child-invoke.js
@@ -46,8 +46,7 @@ define([
                     self.options.target.reload();
                 },
                 error: function(response) {
-                    var message = JSON.parse(response.responseText).message;
-                    self.showError(message);
+                    self.showError(Util.extractError(response, "Error contacting server", url));
                 }
             });
             return ajax;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/jsgui/src/main/webapp/assets/js/view/change-name-invoke.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/change-name-invoke.js b/usage/jsgui/src/main/webapp/assets/js/view/change-name-invoke.js
index 95d2abc..30c2277 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/change-name-invoke.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/change-name-invoke.js
@@ -20,9 +20,9 @@
  * Render entity expungement as a modal
  */
 define([
-    "underscore", "jquery", "backbone",
+    "underscore", "jquery", "backbone", "brooklyn-utils",
     "text!tpl/apps/change-name-modal.html"
-], function(_, $, Backbone, ChangeNameModalHtml) {
+], function(_, $, Backbone, Util, ChangeNameModalHtml) {
     return Backbone.View.extend({
         template: _.template(ChangeNameModalHtml),
         initialize: function() {
@@ -44,14 +44,7 @@ define([
                     self.options.target.reload();
                 },
                 error: function(response) {
-                    var message = "Error contacting server";
-                    try {
-                        message = JSON.parse(response.responseText).message;
-                    } catch (e) {
-                        log("UNPARSEABLE RESPONSE");
-                        log(response);
-                    }
-                    self.showError(message);
+                    self.showError(Util.extractError(response, "Error contacting server", url));
                 }
             });
             return ajax;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/jsgui/src/main/webapp/assets/js/view/entity-advanced.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/entity-advanced.js b/usage/jsgui/src/main/webapp/assets/js/view/entity-advanced.js
index 49114f6..362cadb 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/entity-advanced.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/entity-advanced.js
@@ -30,7 +30,7 @@ define(["underscore", "jquery", "backbone", "brooklyn", "brooklyn-utils", "view/
             "click button#change-name": "showChangeNameModal",
             "click button#add-child": "showAddChildModal",
             "click button#add-new-policy": "showNewPolicyModal",
-            "click button#reset-problems": "doResetProblems",
+            "click button#reset-problems": "confirmResetProblems",
             "click button#expunge": "confirmExpunge",
             "click button#unmanage": "confirmUnmanage",
             "click #advanced-tab-error-closer": "closeAdvancedTabError"
@@ -95,6 +95,17 @@ define(["underscore", "jquery", "backbone", "brooklyn", "brooklyn-utils", "view/
                 entity: this.model,
             }));
         },
+        
+        confirmResetProblems: function () {
+            var entity = this.model.get("name");
+            var title = "Confirm the reset of problem indicators in " + entity;
+            var q = "<p>Are you sure you want to reset the problem indicators for this entity?</p>" +
+                "<p>If a problem has been fixed externally, but the fix is not being detected, this will clear problems. " +
+                "If the problem is not actually fixed, many feeds and enrichers will re-detect it, but note that some may not, " +
+                "and the entity may show as healthy when it is not." +
+                "</p>";
+            Brooklyn.view.requestConfirmation(q, title).done(this.doResetProblems);
+        },
         doResetProblems: function() {
             this.post(this.model.get('links').sensors+"/"+"service.notUp.indicators", {});
             this.post(this.model.get('links').sensors+"/"+"service.problems", {});
@@ -110,11 +121,8 @@ define(["underscore", "jquery", "backbone", "brooklyn", "brooklyn-utils", "view/
                 success: function() {
                     self.reload();
                 },
-                error: function(data) {
-                    self.showAdvancedTabError("Error connecting to Brooklyn server");
-
-                    log("ERROR invoking operation");
-                    log(data);
+                error: function(response) {
+                    self.showAdvancedTabError(Util.extractError(response, "Error contacting server", url));
                 }
             });
         },
@@ -161,7 +169,6 @@ define(["underscore", "jquery", "backbone", "brooklyn", "brooklyn-utils", "view/
             self.$("#advanced-tab-error-section").removeClass("hide");
         },
         closeAdvancedTabError: function() {
-            log("close")
             self.$("#advanced-tab-error-section").addClass("hide");
         },
         
@@ -169,6 +176,7 @@ define(["underscore", "jquery", "backbone", "brooklyn", "brooklyn-utils", "view/
             if (this.activeModal)
                 this.activeModal.close();
             this.options.tabView.configView.close();
+            this.model.off();
         }
     });
     return EntityAdvancedView;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/jsgui/src/main/webapp/assets/js/view/entity-config.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/entity-config.js b/usage/jsgui/src/main/webapp/assets/js/view/entity-config.js
index e22763e..175dbcc 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/entity-config.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/entity-config.js
@@ -42,7 +42,7 @@ define([
         zeroClipboard: null,
         
         events:{
-            'click .refresh':'refreshNow',
+            'click .refresh':'updateConfigNow',
             'click .filterEmpty':'toggleFilterEmpty',
             'click .toggleAutoRefresh':'toggleAutoRefresh',
 
@@ -205,7 +205,13 @@ define([
             this.toggleFilterEmpty();
             return this;
         },
-        
+
+        beforeClose: function () {
+            if (this.zeroClipboard) {
+                this.zeroClipboard.destroy();
+            }
+        },
+
         floatMenuActive: false,
         lastFloatMenuRowId: null,
         lastFloatFocusInTextForEventUnmangling: null,

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/jsgui/src/main/webapp/assets/js/view/entity-summary.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/entity-summary.js b/usage/jsgui/src/main/webapp/assets/js/view/entity-summary.js
index 0405ca7..7984fac 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/entity-summary.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/entity-summary.js
@@ -138,9 +138,10 @@ define([
 
             if (this.problemIndicators) {
                 var indicatorText = _.values(this.problemIndicators);
-                for (error in indicatorText) {
-                    if (problemDetails)
+                for (var error in indicatorText) {
+                    if (problemDetails) {
                         problemDetails = problemDetails + "<br style='line-height: 24px;'>";
+                    }
                     problemDetails = problemDetails + _.escape(indicatorText[error]);
                 }
             }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/jsgui/src/main/webapp/assets/js/view/policy-new.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/policy-new.js b/usage/jsgui/src/main/webapp/assets/js/view/policy-new.js
index 3b88290..c190f78 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/policy-new.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/policy-new.js
@@ -26,7 +26,6 @@ define([
 
     return Backbone.View.extend({
         template: _.template(NewPolicyHtml),
-        title: "Attach New Policy",
 
         initialize: function () {
             if (!this.options.entity) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/jsgui/src/main/webapp/assets/tpl/apps/add-child-modal.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/apps/add-child-modal.html b/usage/jsgui/src/main/webapp/assets/tpl/apps/add-child-modal.html
index 1f0f6af..53e7535 100644
--- a/usage/jsgui/src/main/webapp/assets/tpl/apps/add-child-modal.html
+++ b/usage/jsgui/src/main/webapp/assets/tpl/apps/add-child-modal.html
@@ -27,7 +27,7 @@ under the License.
       <textarea id="child-spec" style="width: 100%; height: 120px;"/>
     </div>
     
-    <input type="checkbox" id="child-autostart" checked="checked"> Auto-start
+    <label><input type="checkbox" id="child-autostart" checked="checked"/> Auto-start </label>
 
     <div class="hide alert alert-error child-add-error-container" style="margin-top: 9px;">
         <strong>Error</strong>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/jsgui/src/main/webapp/assets/tpl/apps/advanced.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/apps/advanced.html b/usage/jsgui/src/main/webapp/assets/tpl/apps/advanced.html
index 52b71d0..c9f5f92 100644
--- a/usage/jsgui/src/main/webapp/assets/tpl/apps/advanced.html
+++ b/usage/jsgui/src/main/webapp/assets/tpl/apps/advanced.html
@@ -19,9 +19,8 @@ under the License.
 -->
 
 <div id="advanced-summary">
-  <h3><span id="entity-name">Entity</span>
-  <button id="change-name" class="btn pull-right">Change Name</button><br/>
-  </h3>
+  <button id="change-name" class="btn pull-right">Change Name</button>
+  <h3><span id="entity-name">Entity</span></h3>
 
 <div style="text-align: center; padding-top: 24px;">
   <button id="add-child" class="btn">Add Child</button>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/jsgui/src/main/webapp/assets/tpl/apps/summary.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/apps/summary.html b/usage/jsgui/src/main/webapp/assets/tpl/apps/summary.html
index bf7d5a6..f249552 100644
--- a/usage/jsgui/src/main/webapp/assets/tpl/apps/summary.html
+++ b/usage/jsgui/src/main/webapp/assets/tpl/apps/summary.html
@@ -79,7 +79,7 @@ under the License.
  <div id="entity-spec-yaml-toggler" class="toggler-region region-config hide" style="margin-top: 18px;">
     <div class="toggler-header user-hidden">
       <div class="toggler-icon icon-chevron-left"></div>
-      <div><b>Blueprint YAML</b></div>
+      <div><b>Blueprint</b></div>
     </div>
     <div id="entity-spec-yaml" class="for-textarea hide">
       <textarea readonly="readonly" style="width: 100%;"/>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/jsgui/src/test/javascript/specs/view/entity-details-spec.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/test/javascript/specs/view/entity-details-spec.js b/usage/jsgui/src/test/javascript/specs/view/entity-details-spec.js
index 46d5e59..3ca76ef 100644
--- a/usage/jsgui/src/test/javascript/specs/view/entity-details-spec.js
+++ b/usage/jsgui/src/test/javascript/specs/view/entity-details-spec.js
@@ -89,11 +89,6 @@ define([
             view.render();
         });
 
-        // TODO textarea only appears if it's got yaml
-//        it('must render textarea contents', function () {
-//            expect(view.$("textarea").length).toBe(1);
-//            expect(view.$("textarea").val()).toMatch("Tomcat");
-//        });
     });
 
     // TODO complains about instanceof on a non-object in underscore; probably because we are now doing $.get 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/rest-api/src/main/java/brooklyn/rest/api/EntityApi.java
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/java/brooklyn/rest/api/EntityApi.java b/usage/rest-api/src/main/java/brooklyn/rest/api/EntityApi.java
index 8d68b6b..f99ef18 100644
--- a/usage/rest-api/src/main/java/brooklyn/rest/api/EntityApi.java
+++ b/usage/rest-api/src/main/java/brooklyn/rest/api/EntityApi.java
@@ -103,7 +103,7 @@ public interface EntityApi {
       @ApiParam(name = "timeout", value = "Delay before server should respond with incomplete activity task, rather than completed task: " +
           "'never' means block until complete; " +
           "'0' means return task immediately; " +
-          "and e.g. '20ms' (the default) will wait 20ms for copmleted task information to be available", 
+          "and e.g. '20ms' (the default) will wait 20ms for completed task information to be available", 
           required = false, defaultValue = "20ms")
       @QueryParam("timeout") final String timeout,
       

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/rest-api/src/main/java/brooklyn/rest/api/EntityConfigApi.java
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/java/brooklyn/rest/api/EntityConfigApi.java b/usage/rest-api/src/main/java/brooklyn/rest/api/EntityConfigApi.java
index 58249a5..b299b73 100644
--- a/usage/rest-api/src/main/java/brooklyn/rest/api/EntityConfigApi.java
+++ b/usage/rest-api/src/main/java/brooklyn/rest/api/EntityConfigApi.java
@@ -100,7 +100,7 @@ public interface EntityConfigApi {
   @Path("/{config}")
   @ApiOperation(value = "Manually set a config value")
   @ApiErrors(value = {
-      @ApiError(code = 404, reason = "Could not find application, entity or sensor")
+      @ApiError(code = 404, reason = "Could not find application, entity or config key")
   })
   public void set(
           @ApiParam(value = "Application ID or name", required = true)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/rest-server/pom.xml
----------------------------------------------------------------------
diff --git a/usage/rest-server/pom.xml b/usage/rest-server/pom.xml
index 9379423..8483543 100644
--- a/usage/rest-server/pom.xml
+++ b/usage/rest-server/pom.xml
@@ -142,20 +142,6 @@
             <groupId>org.eclipse.jetty</groupId>
             <artifactId>jetty-servlet</artifactId>
         </dependency>
-<!--
-        <dependency>
-            <groupId>org.eclipse.jetty.orbit</groupId>
-            <artifactId>javax.servlet</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-databind</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.jaxrs</groupId>
-            <artifactId>jackson-jaxrs-json-provider</artifactId>
-        </dependency>
--->
         <dependency>
             <groupId>org.codehaus.jackson</groupId>
             <artifactId>jackson-core-asl</artifactId>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/rest-server/src/main/java/brooklyn/rest/resources/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/ApplicationResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/ApplicationResource.java
index 47faf3a..e1af603 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/ApplicationResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/ApplicationResource.java
@@ -22,12 +22,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
 import static javax.ws.rs.core.Response.created;
 import static javax.ws.rs.core.Response.status;
 import static javax.ws.rs.core.Response.Status.ACCEPTED;
-import io.brooklyn.camp.brooklyn.spi.creation.BrooklynAssemblyTemplateInstantiator;
-import io.brooklyn.camp.spi.Assembly;
 import io.brooklyn.camp.spi.AssemblyTemplate;
-import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator;
 
-import java.io.Reader;
 import java.io.StringReader;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -52,8 +48,6 @@ import brooklyn.entity.Application;
 import brooklyn.entity.Entity;
 import brooklyn.entity.Group;
 import brooklyn.entity.basic.Attributes;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.EntityLocal;
 import brooklyn.entity.basic.Lifecycle;
 import brooklyn.entity.trait.Startable;
 import brooklyn.event.AttributeSensor;
@@ -64,6 +58,8 @@ import brooklyn.management.Task;
 import brooklyn.management.entitlement.EntitlementPredicates;
 import brooklyn.management.entitlement.Entitlements;
 import brooklyn.management.entitlement.Entitlements.EntityAndItem;
+import brooklyn.management.internal.EntityManagementUtils;
+import brooklyn.management.internal.EntityManagementUtils.CreationResult;
 import brooklyn.rest.api.ApplicationApi;
 import brooklyn.rest.domain.ApplicationSpec;
 import brooklyn.rest.domain.ApplicationSummary;
@@ -76,7 +72,6 @@ import brooklyn.rest.transform.TaskTransformer;
 import brooklyn.rest.util.BrooklynRestResourceUtils;
 import brooklyn.rest.util.WebResourceUtils;
 import brooklyn.util.ResourceUtils;
-import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 
@@ -265,8 +260,7 @@ public class ApplicationResource extends AbstractBrooklynRestResource implements
         }
 
         log.debug("Creating app from yaml:\n{}", yaml);
-        Reader input = new StringReader(yaml);
-        AssemblyTemplate at = camp().pdp().registerDeploymentPlan(input);
+        AssemblyTemplate at = camp().pdp().registerDeploymentPlan( new StringReader(yaml) );
         
         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.DEPLOY_APPLICATION, at)) {
             throw WebResourceUtils.unauthorized("User '%s' is not authorized to start application %s",
@@ -278,33 +272,20 @@ public class ApplicationResource extends AbstractBrooklynRestResource implements
 
     private Response launch(AssemblyTemplate at) {
         try {
-            AssemblyTemplateInstantiator instantiator = at.getInstantiator().newInstance();
-            Assembly assembly;
-            Task<?> task = null;
-            if (instantiator instanceof BrooklynAssemblyTemplateInstantiator) {
-                Application app = ((BrooklynAssemblyTemplateInstantiator) instantiator).create(at, camp());
-                assembly = camp().assemblies().get(app.getApplicationId());
-
-                task = Entities.invokeEffector((EntityLocal)app, app, Startable.START,
-                        // locations already set in the entities themselves;
-                        // TODO make it so that this arg does not have to be supplied to START !
-                        MutableMap.of("locations", MutableList.of()));
-            } else {
-                assembly = instantiator.instantiate(at, camp());
-            }
-            Entity app = mgmt().getEntityManager().getEntity(assembly.getId());
+            CreationResult<? extends Application, Void> result = EntityManagementUtils.createStarting(mgmt(), at);
             
+            Application app = result.get();
             if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, EntityAndItem.of(app, Startable.START.getName()))) {
                 throw WebResourceUtils.unauthorized("User '%s' is not authorized to start application %s",
                     Entitlements.getEntitlementContext().user(), at.getType());
             }
 
-            log.info("Launched from YAML: " + assembly + " (" + task + ")");
+            log.info("Launched from YAML: " + at + " -> " + app + " (" + result.task() + ")");
 
             URI ref = URI.create(app.getApplicationId());
             ResponseBuilder response = created(ref);
-            if (task != null)
-                response.entity(TaskTransformer.FROM_TASK.apply(task));
+            if (result.task() != null) 
+                response.entity(TaskTransformer.FROM_TASK.apply(result.task()));
             return response.build();
         } catch (Exception e) {
             throw Exceptions.propagate(e);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/rest-server/src/main/java/brooklyn/rest/resources/EntityResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/EntityResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/EntityResource.java
index 9792c28..ea3e4b7 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/EntityResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/EntityResource.java
@@ -43,11 +43,12 @@ import brooklyn.entity.basic.BrooklynTags;
 import brooklyn.entity.basic.BrooklynTags.NamedStringTag;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.EntityLocal;
-import brooklyn.entity.effector.AddChildrenEffector;
 import brooklyn.location.Location;
 import brooklyn.management.Task;
 import brooklyn.management.entitlement.EntitlementPredicates;
 import brooklyn.management.entitlement.Entitlements;
+import brooklyn.management.internal.EntityManagementUtils;
+import brooklyn.management.internal.EntityManagementUtils.CreationResult;
 import brooklyn.rest.api.EntityApi;
 import brooklyn.rest.domain.EntitySummary;
 import brooklyn.rest.domain.LocationSummary;
@@ -115,11 +116,12 @@ public class EntityResource extends AbstractBrooklynRestResource implements Enti
             throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify entity '%s'",
                 Entitlements.getEntitlementContext().user(), entityToken);
         }
-        AddChildrenEffector.AddChildrenResult added = AddChildrenEffector.addChildren(parent, yaml, start, timeoutS==null ? Duration.millis(20) : Duration.of(timeoutS));
+        CreationResult<List<Entity>, List<String>> added = EntityManagementUtils.addChildren(parent, yaml, start)
+            .blockUntilComplete(timeoutS==null ? Duration.millis(20) : Duration.of(timeoutS));
         ResponseBuilder response;
         
-        if (added.getChildren().size()==1) {
-            Entity child = Iterables.getOnlyElement(added.getChildren());
+        if (added.get().size()==1) {
+            Entity child = Iterables.getOnlyElement(added.get());
             URI ref = uriInfo.getBaseUriBuilder()
                 .path(EntityApi.class)
                 .path(EntityApi.class, "get")
@@ -128,7 +130,7 @@ public class EntityResource extends AbstractBrooklynRestResource implements Enti
         } else {
             response = Response.status(Status.CREATED);
         }
-        return response.entity(TaskTransformer.taskSummary(added.getTask())).build();
+        return response.entity(TaskTransformer.taskSummary(added.task())).build();
     }
 
   @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b05a9c11/usage/rest-server/src/test/java/brooklyn/rest/testing/BrooklynRestApiTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/testing/BrooklynRestApiTest.java b/usage/rest-server/src/test/java/brooklyn/rest/testing/BrooklynRestApiTest.java
index 982dfc0..fc34f0e 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/testing/BrooklynRestApiTest.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/testing/BrooklynRestApiTest.java
@@ -116,7 +116,7 @@ public abstract class BrooklynRestApiTest {
     protected void addDefaultResources() {
         // seems we have to provide our own injector because the jersey test framework 
         // doesn't inject ServletConfig and it all blows up
-//        addProvider(NullServletConfigProvider.class);
+        // and the servlet config provider must be an instance; addClasses doesn't work for some reason
         addResource(new NullServletConfigProvider());
         addProvider(NullHttpServletRequestProvider.class);
     }


Mime
View raw message