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

[01/23] incubator-brooklyn git commit: yaml catalog supports scanning, experimental, and default

Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master 776ad43d3 -> 321d88790


yaml catalog supports scanning, experimental, and default

this restores catalog scanning as the default, based on brooklyn/default.catalog.bom in the cli project;
there are some limitations on what can be scanned, described in the doc.
some of the tests configure other catalogs (with core's brooklyn/empty.catalog.bom used in many)


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

Branch: refs/heads/master
Commit: 1b080fb524c1a60ba6d5a8fe917b864e7e8e4cb7
Parents: 0b9bc3b
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed Apr 29 13:27:09 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:22:21 2015 +0100

----------------------------------------------------------------------
 .../java/brooklyn/catalog/BrooklynCatalog.java  |  3 +-
 .../catalog/internal/BasicBrooklynCatalog.java  | 58 +++++++++++++++++---
 .../brooklyn/catalog/internal/CatalogDo.java    | 26 +++++++--
 .../catalog/internal/CatalogInitialization.java | 47 +++++++++-------
 .../main/resources/brooklyn/empty.catalog.bom   |  2 +-
 .../catalog/internal/CatalogScanTest.java       |  6 +-
 .../test/resources/brooklyn/default.catalog.bom | 19 +++++++
 docs/guide/ops/catalog/index.md                 |  5 ++
 .../main/resources/brooklyn/default.catalog.bom | 19 +++++++
 .../brooklyn/rest/BrooklynRestApiLauncher.java  |  7 ++-
 .../BrooklynRestApiLauncherTestFixture.java     |  4 +-
 .../util/BrooklynRestResourceUtilsTest.java     |  5 +-
 .../resources/brooklyn/scanning.catalog.bom     | 19 +++++++
 13 files changed, 180 insertions(+), 40 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1b080fb5/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
index 5b11cc6..7c36c0e 100644
--- a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
+++ b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
@@ -129,8 +129,9 @@ public interface BrooklynCatalog {
      * adds an item to the 'manual' catalog;
      * this does not update the classpath or have a record to the java Class
      *
-     * @deprecated since 0.7.0 Construct catalogs with OSGi bundles instead
+     * @deprecated since 0.7.0 Construct catalogs with yaml (referencing OSGi bundles) instead
      */
+    // TODO maybe this should stay on the API? -AH Apr 2015 
     @Deprecated
     void addItem(CatalogItem<?,?> item);
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1b080fb5/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
index 9d68465..f1d5a3c 100644
--- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
@@ -45,6 +45,7 @@ import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.CatalogItem.CatalogBundle;
 import brooklyn.catalog.CatalogItem.CatalogItemType;
 import brooklyn.catalog.CatalogPredicates;
+import brooklyn.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.location.Location;
 import brooklyn.location.LocationSpec;
@@ -74,11 +75,14 @@ import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
+import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.collect.Iterables;
 
+/* TODO the complex tree-structured catalogs are only useful when we are relying on those separate catalog classloaders
+ * to isolate classpaths. with osgi everything is just put into the "manual additions" catalog. */
 public class BasicBrooklynCatalog implements BrooklynCatalog {
     private static final String POLICIES_KEY = "brooklyn.policies";
     private static final String LOCATIONS_KEY = "brooklyn.locations";
@@ -185,12 +189,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
             return null;
         }
 
-        String versionedId = CatalogUtils.getVersionedId(symbolicName, fixedVersionId);
-        CatalogItemDo<?, ?> item = null;
-        //TODO should remove "manual additions" bucket; just have one map a la osgi
-        if (manualAdditionsCatalog!=null) item = manualAdditionsCatalog.getIdCache().get(versionedId);
-        if (item == null) item = catalog.getIdCache().get(versionedId);
-        return item;
+        return catalog.getIdCache().get( CatalogUtils.getVersionedId(symbolicName, fixedVersionId) );
     }
     
     private String getFixedVersionId(String symbolicName, String version) {
@@ -596,7 +595,22 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         Collection<CatalogBundle> libraryBundles = CatalogItemDtoAbstract.parseLibraries(librariesCombined);
 
         // TODO as this may take a while if downloading, the REST call should be async
+        // (this load is required for the scan below and I think also for yaml resolution)
         CatalogUtils.installLibraries(mgmt, libraryBundlesNew);
+
+        Boolean scanJavaAnnotations = getFirstAs(itemMetadata, Boolean.class, "scanJavaAnnotations", "scan_java_annotations").orNull();
+        if (scanJavaAnnotations==null || !scanJavaAnnotations) {
+            // don't scan
+        } else {
+            // scan for annotations: if libraries here, scan them; if inherited libraries error; else scan classpath
+            if (!libraryBundlesNew.isEmpty()) {
+                result.addAll(scanAnnotations(mgmt, libraryBundlesNew));
+            } else if (libraryBundles.isEmpty()) {
+                result.addAll(scanAnnotations(mgmt, null));
+            } else {
+                throw new IllegalStateException("Cannot scan catalog node no local bundles, and with inherited bundles we will not scan the classpath");
+            }
+        }
         
         Object items = catalogMetadata.remove("items");
         Object item = catalogMetadata.remove("item");
@@ -749,7 +763,36 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         return oldValue;
     }
 
-    
+    /** scans the given libraries for annotated items, or if null scans the local classpath */ 
+    private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotations(ManagementContext mgmt, Collection<CatalogBundle> libraries) {
+//        CatalogDto dto = CatalogDto.newDefaultLocalScanningDto(CatalogClasspathDo.CatalogScanningModes.ANNOTATIONS);
+        CatalogDto dto;
+        String[] urls = null;
+        if (libraries==null) {
+            dto = CatalogDto.newNamedInstance("Local Scanned Catalog", "All annotated Brooklyn entities detected in the classpath", "scanning-local-classpath");
+        } else {
+            dto = CatalogDto.newNamedInstance("Bundles Scanned Catalog", "All annotated Brooklyn entities detected in the classpath", "scanning-bundles-classpath-"+libraries.hashCode());
+            urls = new String[libraries.size()];
+            int i=0;
+            for (CatalogBundle b: libraries)
+                urls[i++] = b.getUrl();
+        }
+        CatalogDo subCatalog = new CatalogDo(dto);
+        subCatalog.mgmt = mgmt;
+        if (urls!=null) {
+            subCatalog.addToClasspath(urls);
+        } // else use local classpath
+        subCatalog.setClasspathScanForEntities(CatalogScanningModes.ANNOTATIONS);
+        subCatalog.load();
+        // TODO apply metadata?  (extract YAML from the items returned)
+        // also see doc .../catalog/index.md which says we might not apply metadata
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        Collection<CatalogItemDtoAbstract<?, ?>> result = (Collection<CatalogItemDtoAbstract<?, ?>>)(Collection)Collections2.transform(
+                (Collection<CatalogItemDo<Object,Object>>)(Collection)subCatalog.getIdCache().values(), 
+                itemDoToDto());
+        return result;
+    }
+
     private class PlanInterpreterGuessingType {
 
         final String id;
@@ -1084,6 +1127,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         return new Function<CatalogItemDo<T,SpecT>, CatalogItem<T,SpecT>>() {
             @Override
             public CatalogItem<T,SpecT> apply(@Nullable CatalogItemDo<T,SpecT> item) {
+                if (item==null) return null;
                 return item.getDto();
             }
         };

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1b080fb5/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
index 0efe85c..2142ce9 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
@@ -208,19 +208,28 @@ public class CatalogDo {
                 child.clearCache(true);
             }
         }
+        clearParentCache();
+    }
+    protected void clearParentCache() {
+        if (this.parent!=null)
+            this.parent.clearCache(false);
     }
     
     /**
      * Adds the given entry to the catalog, with no enrichment.
      * Callers may prefer {@link CatalogClasspathDo#addCatalogEntry(CatalogItemDtoAbstract, Class)}
      */
-    @SuppressWarnings({ "unchecked", "rawtypes" })
     public synchronized void addEntry(CatalogItemDtoAbstract<?,?> entry) {
         dto.addEntry(entry);
+        
+        // could do clearCache(false); but this is slightly more efficient...
         if (cacheById != null) {
+            @SuppressWarnings({ "unchecked", "rawtypes" })
             CatalogItemDo<?, ?> cdo = new CatalogItemDo(this, entry);
             cacheById.put(entry.getId(), cdo);
-        }
+        }        
+        clearParentCache();
+        
         if (mgmt != null) {
             mgmt.getRebindManager().getChangeListener().onManaged(entry);
         }
@@ -231,12 +240,19 @@ public class CatalogDo {
      */
     public synchronized void deleteEntry(CatalogItemDtoAbstract<?, ?> entry) {
         dto.removeEntry(entry);
+        
+        // could do clearCache(false); but this is slightly more efficient...
         if (cacheById != null) {
             cacheById.remove(entry.getId());
         }
+        clearParentCache();
+        
         if (mgmt != null) {
             // TODO: Can the entry be in more than one catalogue? The management context has no notion of
             // catalogue hierarchy so this will effectively remove it from all catalogues.
+            // (YES- we're assuming ID's are unique across all catalogues; if not, things get out of sync;
+            // however see note at top of BasicBrooklynCatalog --
+            // manualCatalog and OSGi is used for everything now except legacy XML trees)
             mgmt.getRebindManager().getChangeListener().onUnmanaged(entry);
         }
     }
@@ -254,8 +270,10 @@ public class CatalogDo {
     public synchronized void addToClasspath(String ...urls) {
         if (dto.classpath == null)
             dto.classpath = new CatalogClasspathDto();
-        for (String url: urls)
-            dto.classpath.addEntry(url);
+        for (String url: urls) {
+            if (url!=null)
+                dto.classpath.addEntry(url);
+        }
         if (isLoaded())
             throw new IllegalStateException("dynamic classpath entry value update not supported");
         // easy enough to add, just support unload+reload (and can also allow dynamic setScan below)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1b080fb5/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java b/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
index db7343a..15d5d76 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
@@ -38,7 +38,6 @@ import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.net.Urls;
 import brooklyn.util.text.Strings;
-import brooklyn.util.yaml.Yamls;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
@@ -189,15 +188,18 @@ public class CatalogInitialization {
             return;
         }
 
-        // TODO scan for default.catalog.bom files and add all of them
+        // otherwise look for for classpath:/brooklyn/default.catalog.bom --
+        // there is one on the classpath which says to scan, and provides a few templates;
+        // if one is supplied by user in the conf/ dir that will override the item from the classpath
+        // (TBD - we might want to scan for all such bom's?)
         
-//        // TODO optionally scan for classpath items
-//        // retry, either an error, or was blank
-//        dto = CatalogDto.newDefaultLocalScanningDto(CatalogClasspathDo.CatalogScanningModes.ANNOTATIONS);
-//        if (log.isDebugEnabled()) {
-//            log.debug("Loaded default (local classpath) catalog: " + catalogDo);
-//        }
+        catalogUrl = "classpath:/brooklyn/default.catalog.bom";
+        if (new ResourceUtils(this).doesUrlExist(catalogUrl)) {
+            populateInitialFromUri(catalog, managementContext, catalogUrl, PopulateMode.YAML);
+            return;
+        }
         
+        log.info("No catalog found on classpath or specified; catalog will not be initialized.");
         return;
     }
     
@@ -228,17 +230,7 @@ public class CatalogInitialization {
         
         if (result==null && contents!=null && (mode==PopulateMode.XML || mode==PopulateMode.AUTODETECT)) {
             // then try XML
-            CatalogDto dto = null;
-            try {
-                dto = CatalogDto.newDtoFromXmlContents(contents, catalogUrl);
-                problem = null;
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                if (problem==null) problem = e;
-            }
-            if (dto!=null) {
-                catalog.reset(dto);
-            }
+            problem = populateInitialFromUriXml(catalog, catalogUrl, problem, contents);
         }
         
         if (result!=null) {
@@ -251,6 +243,23 @@ public class CatalogInitialization {
 
     }
 
+    // deprecated XML format
+    @SuppressWarnings("deprecation")
+    private Exception populateInitialFromUriXml(BasicBrooklynCatalog catalog, String catalogUrl, Exception problem, String contents) {
+        CatalogDto dto = null;
+        try {
+            dto = CatalogDto.newDtoFromXmlContents(contents, catalogUrl);
+            problem = null;
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            if (problem==null) problem = e;
+        }
+        if (dto!=null) {
+            catalog.reset(dto);
+        }
+        return problem;
+    }
+
     protected void populateAdditions(BasicBrooklynCatalog catalog, ManagementContext mgmt) {
         if (Strings.isNonBlank(additionsUri)) {
             if (disallowLocal) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1b080fb5/core/src/main/resources/brooklyn/empty.catalog.bom
----------------------------------------------------------------------
diff --git a/core/src/main/resources/brooklyn/empty.catalog.bom b/core/src/main/resources/brooklyn/empty.catalog.bom
index 401d2fa..58cfb1b 100644
--- a/core/src/main/resources/brooklyn/empty.catalog.bom
+++ b/core/src/main/resources/brooklyn/empty.catalog.bom
@@ -15,4 +15,4 @@
 # specific language governing permissions and limitations
 # under the License.
 #
-brooklyn.catalog: {}
+brooklyn.catalog: {}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1b080fb5/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
index 949877f..07c40af 100644
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
+++ b/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
@@ -162,13 +162,13 @@ public class CatalogScanTest {
     
     @Test
     public void testAnnotationIsDefault() {
-        loadAnnotationsOnlyCatalog();
         loadTheDefaultCatalog();
+        int numInDefault = Iterables.size(defaultCatalog.getCatalogItems(Predicates.alwaysTrue()));
         
+        loadAnnotationsOnlyCatalog();
         int numFromAnnots = Iterables.size(annotsCatalog.getCatalogItems(Predicates.alwaysTrue()));
-        int numInDefault = Iterables.size(defaultCatalog.getCatalogItems(Predicates.alwaysTrue()));
         
-        Assert.assertEquals(numFromAnnots, numInDefault);
+        Assert.assertEquals(numInDefault, numFromAnnots);
     }
 
     // a simple test asserting no errors when listing the real catalog, and listing them for reference

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1b080fb5/core/src/test/resources/brooklyn/default.catalog.bom
----------------------------------------------------------------------
diff --git a/core/src/test/resources/brooklyn/default.catalog.bom b/core/src/test/resources/brooklyn/default.catalog.bom
new file mode 100644
index 0000000..cddb832
--- /dev/null
+++ b/core/src/test/resources/brooklyn/default.catalog.bom
@@ -0,0 +1,19 @@
+# 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.
+#
+brooklyn.catalog:
+  scanJavaAnnotations: true

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1b080fb5/docs/guide/ops/catalog/index.md
----------------------------------------------------------------------
diff --git a/docs/guide/ops/catalog/index.md b/docs/guide/ops/catalog/index.md
index 8477204..dcedcd8 100644
--- a/docs/guide/ops/catalog/index.md
+++ b/docs/guide/ops/catalog/index.md
@@ -103,6 +103,11 @@ The following optional catalog metadata is supported:
   (to prevent requiring all OSGi bundles to be loaded at launch).
   Icons are instead typically installed either at the server from which the OSGi bundles or catalog items are supplied 
   or in the `conf` folder of the Brooklyn distro.
+- `scanJavaAnnotations` [experimental]: if provided (as `true`), this will scan any locally provided
+  libraries for types annotated `@Catalog` and extract metadata to include them as catalog items.
+  If no libraries are specified this will scan the default classpath.
+  This feature is experimental and may change or be removed.
+  Also note that other metadata (such as versions, etc) may not be applied.
 - `brooklyn.libraries`: a list of pointers to OSGi bundles required for the catalog item.
   This can be omitted if blueprints are pure YAML and everything required is included in the classpath and catalog.
   Where custom Java code or bundled resources is needed, however, OSGi JARs supply

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1b080fb5/usage/cli/src/main/resources/brooklyn/default.catalog.bom
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/default.catalog.bom b/usage/cli/src/main/resources/brooklyn/default.catalog.bom
new file mode 100644
index 0000000..cddb832
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/default.catalog.bom
@@ -0,0 +1,19 @@
+# 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.
+#
+brooklyn.catalog:
+  scanJavaAnnotations: true

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1b080fb5/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java b/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java
index f189815..dff7925 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java
@@ -87,6 +87,7 @@ public class BrooklynRestApiLauncher {
 
     private static final Logger log = LoggerFactory.getLogger(BrooklynRestApiLauncher.class);
     final static int FAVOURITE_PORT = 8081;
+    public static final String SCANNING_CATALOG_BOM_URL = "classpath://brooklyn/scanning.catalog.bom";
 
     enum StartMode {
         FILTER, SERVLET, WEB_XML
@@ -200,10 +201,12 @@ public class BrooklynRestApiLauncher {
         }
 
         if (forceUseOfDefaultCatalogWithJavaClassPath) {
-            // don't use any catalog.xml which is set
-            ((BrooklynProperties) mgmt.getConfig()).put(BrooklynServerConfig.BROOKLYN_CATALOG_URL, ManagementContextInternal.EMPTY_CATALOG_URL);
             // sets URLs for a surefire
+            ((BrooklynProperties) mgmt.getConfig()).put(BrooklynServerConfig.BROOKLYN_CATALOG_URL, SCANNING_CATALOG_BOM_URL);
             ((LocalManagementContext) mgmt).setBaseClassPathForScanning(ClasspathHelper.forJavaClassPath());
+        } else {
+            // don't use any catalog.xml which is set
+            ((BrooklynProperties) mgmt.getConfig()).put(BrooklynServerConfig.BROOKLYN_CATALOG_URL, ManagementContextInternal.EMPTY_CATALOG_URL);
         }
 
         return startServer(mgmt, context, summary, disableHighAvailability);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1b080fb5/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncherTestFixture.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncherTestFixture.java b/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncherTestFixture.java
index 86be4c7..1778786 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncherTestFixture.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncherTestFixture.java
@@ -82,8 +82,10 @@ public abstract class BrooklynRestApiLauncherTestFixture {
     }
 
     public static void forceUseOfDefaultCatalogWithJavaClassPath(ManagementContext manager) {
+        // TODO duplication with BrooklynRestApiLauncher ?
+        
         // don't use any catalog.xml which is set
-        ((BrooklynProperties)manager.getConfig()).put(BrooklynServerConfig.BROOKLYN_CATALOG_URL, ManagementContextInternal.EMPTY_CATALOG_URL);
+        ((BrooklynProperties)manager.getConfig()).put(BrooklynServerConfig.BROOKLYN_CATALOG_URL, BrooklynRestApiLauncher.SCANNING_CATALOG_BOM_URL);
         // sets URLs for a surefire
         ((LocalManagementContext)manager).setBaseClassPathForScanning(ClasspathHelper.forJavaClassPath());
         // this also works

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1b080fb5/usage/rest-server/src/test/java/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java b/usage/rest-server/src/test/java/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java
index 833a87a..5922fcb 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java
@@ -44,6 +44,7 @@ import brooklyn.policy.Policy;
 import brooklyn.policy.basic.AbstractPolicy;
 import brooklyn.rest.domain.ApplicationSpec;
 import brooklyn.rest.domain.EntitySpec;
+import brooklyn.test.entity.LocalManagementContextForTests;
 import brooklyn.test.entity.TestEntityImpl;
 import brooklyn.util.collections.MutableMap;
 
@@ -58,7 +59,7 @@ public class BrooklynRestResourceUtilsTest {
 
     @BeforeMethod(alwaysRun=true)
     public void setUp() throws Exception {
-        managementContext = new LocalManagementContext();
+        managementContext = LocalManagementContextForTests.newInstance();
         util = new BrooklynRestResourceUtils(managementContext);
     }
     
@@ -114,7 +115,7 @@ public class BrooklynRestResourceUtilsTest {
             .javaType(SampleNoOpApplication.class.getName())
             .build();
         managementContext.getCatalog().addItem(item);
-
+        
         ApplicationSpec spec = ApplicationSpec.builder()
                 .name("myname")
                 .type(type)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1b080fb5/usage/rest-server/src/test/resources/brooklyn/scanning.catalog.bom
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/resources/brooklyn/scanning.catalog.bom b/usage/rest-server/src/test/resources/brooklyn/scanning.catalog.bom
new file mode 100644
index 0000000..cddb832
--- /dev/null
+++ b/usage/rest-server/src/test/resources/brooklyn/scanning.catalog.bom
@@ -0,0 +1,19 @@
+# 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.
+#
+brooklyn.catalog:
+  scanJavaAnnotations: true


[03/23] incubator-brooklyn git commit: allow "addition" of a catalog item which already exists if it's exactly the same

Posted by he...@apache.org.
allow "addition" of a catalog item which already exists if it's exactly the same

adds equals and hashCode to CatlogItemDto and CatalogBundleDto


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

Branch: refs/heads/master
Commit: fab1caf220f0d6eae661c4951683ee46b3eedf77
Parents: 48ce0df
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed May 6 23:02:03 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:22:22 2015 +0100

----------------------------------------------------------------------
 .../catalog/internal/BasicBrooklynCatalog.java  | 22 ++++++++++++--
 .../catalog/internal/CatalogBundleDto.java      | 19 ++++++++++++
 .../internal/CatalogItemDtoAbstract.java        | 32 ++++++++++++++++++++
 .../brooklyn/catalog/CatalogYamlEntityTest.java | 20 ++++++++++--
 .../catalog/CatalogYamlVersioningTest.java      | 11 +++++--
 usage/cli/src/main/java/brooklyn/cli/Main.java  |  2 +-
 6 files changed, 96 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/fab1caf2/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
index 2e31532..7e4763d 100644
--- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
@@ -1045,7 +1045,12 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     }
     
     private CatalogItem<?,?> addItemDto(CatalogItemDtoAbstract<?, ?> itemDto, boolean forceUpdate) {
-        checkItemNotExists(itemDto, forceUpdate);
+        CatalogItem<?, ?> existingDto = checkItemIsDuplicateOrDisallowed(itemDto, true, forceUpdate);
+        if (existingDto!=null) {
+            // it's a duplicate, and not forced, just return it
+            log.trace("Using existing duplicate for catalog item {}", itemDto.getId());
+            return existingDto;
+        }
 
         if (manualAdditionsCatalog==null) loadManualAdditionsCatalog();
         manualAdditionsCatalog.addEntry(itemDto);
@@ -1067,8 +1072,19 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         return itemDto;
     }
 
-    private void checkItemNotExists(CatalogItem<?,?> itemDto, boolean forceUpdate) {
-        if (!forceUpdate && getCatalogItemDo(itemDto.getSymbolicName(), itemDto.getVersion()) != null) {
+    /** returns item DTO if item is an allowed duplicate, null if it should be added, or false if the item is an allowed duplicate,
+     * throwing if item cannot be added */
+    private CatalogItem<?, ?> checkItemIsDuplicateOrDisallowed(CatalogItem<?,?> itemDto, boolean allowDuplicates, boolean forceUpdate) {
+        if (forceUpdate) return null;
+        CatalogItemDo<?, ?> existingItem = getCatalogItemDo(itemDto.getSymbolicName(), itemDto.getVersion());
+        if (existingItem == null) return null;
+        // check if they are equal
+        CatalogItem<?, ?> existingDto = existingItem.getDto();
+        if (existingDto.equals(itemDto)) {
+            if (allowDuplicates) return existingItem;
+            throw new IllegalStateException("Updating existing catalog entries, even with the same content, is forbidden: " +
+                    itemDto.getSymbolicName() + ":" + itemDto.getVersion() + ". Use forceUpdate argument to override.");
+        } else {
             throw new IllegalStateException("Updating existing catalog entries is forbidden: " +
                     itemDto.getSymbolicName() + ":" + itemDto.getVersion() + ". Use forceUpdate argument to override.");
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/fab1caf2/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
index 35353c4..5cbd23a 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
@@ -71,4 +71,23 @@ public class CatalogBundleDto implements CatalogBundle {
                 .add("url", url)
                 .toString();
     }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(symbolicName, version, url);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        CatalogBundleDto other = (CatalogBundleDto) obj;
+        if (!Objects.equal(symbolicName, other.symbolicName)) return false;
+        if (!Objects.equal(version, other.version)) return false;
+        if (!Objects.equal(url, other.url)) return false;
+        return true;
+    }
+    
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/fab1caf2/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
index 523cd79..c6ff97e 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
@@ -39,6 +39,7 @@ import brooklyn.util.collections.MutableList;
 import brooklyn.util.flags.FlagUtils;
 import brooklyn.util.flags.SetFromFlag;
 
+import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -162,6 +163,37 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
     }
 
     @Override
+    public int hashCode() {
+        return Objects.hashCode(symbolicName, planYaml, javaType, nullIfEmpty(libraries), version, getCatalogItemId());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        CatalogItemDtoAbstract<?,?> other = (CatalogItemDtoAbstract<?,?>) obj;
+        if (!Objects.equal(symbolicName, other.symbolicName)) return false;
+        if (!Objects.equal(planYaml, other.planYaml)) return false;
+        if (!Objects.equal(javaType, other.javaType)) return false;
+        if (!Objects.equal(nullIfEmpty(libraries), nullIfEmpty(other.libraries))) return false;
+        if (!Objects.equal(getCatalogItemId(), other.getCatalogItemId())) return false;
+        if (!Objects.equal(version, other.version)) return false;
+        if (!Objects.equal(deprecated, other.deprecated)) return false;
+        if (!Objects.equal(description, other.description)) return false;
+        if (!Objects.equal(displayName, other.displayName)) return false;
+        if (!Objects.equal(iconUrl, other.iconUrl)) return false;
+        if (!Objects.equal(tags, other.tags)) return false;
+        // 'type' not checked, because deprecated, we might want to allow removal in future
+        return true;
+    }
+
+    private static <T> Collection<T> nullIfEmpty(Collection<T> coll) {
+        if (coll==null || coll.isEmpty()) return null;
+        return coll;
+    }
+
+    @Override
     public String toString() {
         return getClass().getSimpleName()+"["+getId()+"/"+getDisplayName()+"]";
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/fab1caf2/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
index 1e321dc..9353108 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
@@ -486,14 +486,23 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
                     OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL + "}");
         }
     }
+
+    @Test
+    public void testUpdatingItemAllowedIfSame() {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+
+        String id = "my.catalog.app.id.duplicate";
+        addCatalogOSGiEntity(id);
+        addCatalogOSGiEntity(id);
+    }
     
     @Test(expectedExceptions = IllegalStateException.class)
-    public void testUpdatingItemFails() {
+    public void testUpdatingItemFailsIfDifferent() {
         TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
 
         String id = "my.catalog.app.id.duplicate";
         addCatalogOSGiEntity(id);
-        addCatalogOSGiEntity(id);
+        addCatalogOSGiEntity(id, SIMPLE_ENTITY_TYPE, true);
     }
 
     @Test
@@ -620,6 +629,10 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
     }
 
     private void addCatalogOSGiEntity(String symbolicName, String serviceType) {
+        addCatalogOSGiEntity(symbolicName, serviceType, false);
+    }
+    
+    private void addCatalogOSGiEntity(String symbolicName, String serviceType, boolean extraLib) {
         addCatalogItem(
             "brooklyn.catalog:",
             "  id: " + symbolicName,
@@ -628,7 +641,8 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
             "  icon_url: classpath://path/to/myicon.jpg",
             "  version: " + TEST_VERSION,
             "  libraries:",
-            "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
+            "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL +
+            (extraLib ? "\n"+"  - url: "+OsgiStandaloneTest.BROOKLYN_OSGI_TEST_A_0_1_0_URL : ""),
             "  item:",
             "    type: " + serviceType);
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/fab1caf2/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlVersioningTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlVersioningTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlVersioningTest.java
index c1176ea..dda60dc 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlVersioningTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlVersioningTest.java
@@ -66,12 +66,13 @@ public class CatalogYamlVersioningTest extends AbstractYamlTest {
     }
 
     @Test
-    public void testAddSameVersionFails() {
+    public void testAddSameVersionFailsWhenIconIsDifferent() {
         String symbolicName = "sampleId";
         String version = "0.1.0";
         addCatalogEntity(symbolicName, version);
+        addCatalogEntity(symbolicName, version);
         try {
-            addCatalogEntity(symbolicName, version);
+            addCatalogEntity(symbolicName, version, BasicEntity.class.getName(), "classpath:/another/icon.png");
             fail("Expected to fail");
         } catch (IllegalStateException e) {
             assertEquals(e.getMessage(), "Updating existing catalog entries is forbidden: " + symbolicName + ":" + version + ". Use forceUpdate argument to override.");
@@ -238,12 +239,16 @@ public class CatalogYamlVersioningTest extends AbstractYamlTest {
     }
 
     private void addCatalogEntity(String symbolicName, String version, String type) {
+        addCatalogEntity(symbolicName, version, type, "classpath://path/to/myicon.jpg");
+    }
+    
+    private void addCatalogEntity(String symbolicName, String version, String type, String iconUrl) {
         addCatalogItem(
             "brooklyn.catalog:",
             "  id: " + symbolicName,
             "  name: My Catalog App",
             "  description: My description",
-            "  icon_url: classpath://path/to/myicon.jpg",
+            "  icon_url: "+iconUrl,
             (version != null ? "  version: " + version : ""),
             "",
             "services:",

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/fab1caf2/usage/cli/src/main/java/brooklyn/cli/Main.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/brooklyn/cli/Main.java b/usage/cli/src/main/java/brooklyn/cli/Main.java
index ad6aeb1..e6330ea 100644
--- a/usage/cli/src/main/java/brooklyn/cli/Main.java
+++ b/usage/cli/src/main/java/brooklyn/cli/Main.java
@@ -219,7 +219,7 @@ public class Main extends AbstractMain {
 
         @Option(name = { "--catalogInitial" }, title = "catalog initial bom URI",
             description = "Specifies a catalog.bom URI to be used to populate the initial catalog, "
-                + "if nothing is yet persisted in the catalog (or if it is reset)")
+                + "loaded on first run, or when persistence is off/empty or the catalog is reset")
         public String catalogInitial;
 
         @Option(name = { "--catalogReset" }, 


[07/23] incubator-brooklyn git commit: allow access to catalog classloader without populating catalog

Posted by he...@apache.org.
allow access to catalog classloader without populating catalog

required changes to how catalog is initialized, but i think it's a bit cleaner now (overwriting other changes in this PR).
(lots of files touched, unfortunately, but not a lot different here.)


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

Branch: refs/heads/master
Commit: ddbb43c604b57c8ef522f7e99d6e1f45517292bc
Parents: 21707da
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed May 6 16:37:20 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:22:22 2015 +0100

----------------------------------------------------------------------
 .../java/brooklyn/catalog/BrooklynCatalog.java  |   4 +-
 .../brooklyn/management/ManagementContext.java  |   5 +
 .../catalog/internal/BasicBrooklynCatalog.java  |  11 +-
 .../catalog/internal/CatalogInitialization.java | 108 ++++++++-----------
 .../brooklyn/entity/rebind/RebindIteration.java |   6 +-
 .../entity/rebind/RebindManagerImpl.java        |   4 +-
 .../persister/BrooklynPersistenceUtils.java     |   4 +-
 .../location/basic/BasicLocationRegistry.java   |   2 +-
 .../JavaBrooklynClassLoadingContext.java        |   2 +-
 .../ha/HighAvailabilityManagerImpl.java         |   2 +-
 .../internal/AbstractManagementContext.java     |  36 +++----
 .../internal/ManagementContextInternal.java     |   8 --
 .../NonDeploymentManagementContext.java         |  21 ++--
 .../location/jclouds/JcloudsLocation.java       |   4 +-
 .../BrooklynComponentTemplateResolver.java      |   7 +-
 .../brooklyn/launcher/BrooklynLauncher.java     |   9 +-
 .../rest/resources/ApplicationResource.java     |   2 +-
 .../brooklyn/rest/resources/EntityResource.java |   2 +-
 .../rest/util/BrooklynRestResourceUtils.java    |   4 +
 .../util/javalang/AggregateClassLoader.java     |  24 ++++-
 20 files changed, 130 insertions(+), 135 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
index 7c36c0e..82b865d 100644
--- a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
+++ b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
@@ -69,7 +69,9 @@ public interface BrooklynCatalog {
     public void persist(CatalogItem<?, ?> catalogItem);
 
     /** @return The classloader which should be used to load classes and entities;
-     * this includes all the catalog's classloaders in the right order */
+     * this includes all the catalog's classloaders in the right order.
+     * This is a wrapper which will update as the underlying catalog items change,
+     * so it is safe for callers to keep a handle on this. */
     public ClassLoader getRootClassLoader();
 
     /** creates a spec for the given catalog item, throwing exceptions if any problems */

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/api/src/main/java/brooklyn/management/ManagementContext.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/management/ManagementContext.java b/api/src/main/java/brooklyn/management/ManagementContext.java
index 12ead70..b34dba1 100644
--- a/api/src/main/java/brooklyn/management/ManagementContext.java
+++ b/api/src/main/java/brooklyn/management/ManagementContext.java
@@ -197,6 +197,11 @@ public interface ManagementContext {
     /** Record of configured Brooklyn entities (and templates and policies) which can be loaded */
     BrooklynCatalog getCatalog();
 
+    /** Returns the class loader to be used to load items. 
+     * Temporary routine while catalog supports classloader-based and OSGi-based classloading. */
+    @Beta
+    ClassLoader getCatalogClassLoader();
+
     LocationManager getLocationManager();
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
index 375b29e..2e31532 100644
--- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
@@ -112,6 +112,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     private CatalogDo catalog;
     private volatile CatalogDo manualAdditionsCatalog;
     private volatile LoadedClassLoader manualAdditionsClasses;
+    private final AggregateClassLoader rootClassLoader = AggregateClassLoader.newInstanceWithNoLoaders();
 
     public BasicBrooklynCatalog(ManagementContext mgmt) {
         this(mgmt, CatalogDto.newNamedInstance("empty catalog", "empty catalog", "empty catalog, expected to be reset later"));
@@ -143,6 +144,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         catalog.load(mgmt, null);
         CatalogUtils.logDebugOrTraceIfRebinding(log, "Reloaded catalog for "+this+", now switching");
         this.catalog = catalog;
+        resetRootClassLoader();
         this.manualAdditionsCatalog = null;
 
         // Inject management context into and persist all the new entries.
@@ -295,7 +297,14 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     
     @Override
     public ClassLoader getRootClassLoader() {
-        return catalog.getRootClassLoader();
+        if (rootClassLoader.isEmpty() && catalog!=null) {
+            resetRootClassLoader();
+        }
+        return rootClassLoader;
+    }
+
+    private void resetRootClassLoader() {
+        rootClassLoader.reset(ImmutableList.of(catalog.getRootClassLoader()));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java b/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
index 69dc877..1710384 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
@@ -21,12 +21,10 @@ package brooklyn.catalog.internal;
 import java.io.File;
 import java.util.Collection;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.management.ManagementContext;
@@ -45,6 +43,7 @@ import brooklyn.util.text.Strings;
 import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
 @Beta
@@ -79,12 +78,14 @@ public class CatalogInitialization implements ManagementContextInjectable {
 
     boolean disallowLocal = false;
     List<Function<CatalogInitialization, Void>> callbacks = MutableList.of();
-    AtomicInteger runCount = new AtomicInteger();
+    boolean hasRunBestEffort = false, hasRunOfficial = false, isPopulating = false;
     
     ManagementContext managementContext;
     boolean isStartingUp = false;
     boolean failOnStartupErrors = false;
     
+    Object mutex = new Object();
+    
     public CatalogInitialization(String initialUri, boolean reset, String additionUri, boolean force) {
         this.initialUri = initialUri;
         this.reset = reset;
@@ -115,36 +116,31 @@ public class CatalogInitialization implements ManagementContextInjectable {
         return reset;
     }
 
-    public int getRunCount() {
-        return runCount.get();
-    }
-    
-    public boolean hasRun() {
-        return getRunCount()>0;
-    }
+    public boolean hasRunOfficial() { return hasRunOfficial; }
+    public boolean hasRunIncludingBestEffort() { return hasRunOfficial || hasRunBestEffort; }
 
     /** makes or updates the mgmt catalog, based on the settings in this class */
     public void populateCatalog(boolean needsInitial, Collection<CatalogItem<?, ?>> optionalItemsForResettingCatalog) {
         try {
-            BasicBrooklynCatalog catalog;
-            Maybe<BrooklynCatalog> cm = ((ManagementContextInternal)managementContext).getCatalogIfSet();
-            if (cm.isAbsent()) {
-                if (hasRun()) {
-                    log.warn("Catalog initialization has already run but management context has no catalog; re-creating");
-                }
-                catalog = new BasicBrooklynCatalog(managementContext);
-                setCatalog(managementContext, catalog, "Replacing catalog with newly populated catalog", true);
-            } else {
-                if (!hasRun()) {
-                    log.warn("Catalog initialization has not properly run but management context has a catalog; re-populating, possibly overwriting items installed during earlier access (it may have been an early web request)");
+            isPopulating = true;
+            synchronized (mutex) {
+                BasicBrooklynCatalog catalog = (BasicBrooklynCatalog) managementContext.getCatalog();
+                if (!catalog.getCatalog().isLoaded()) {
+                    catalog.load();
+                } else {
+                    if (hasRunOfficial || hasRunBestEffort) {
+                        // an indication that something caused it to load early; not severe, but unusual
+                        log.warn("Catalog initialization has not properly run but management context has a catalog; re-populating, possibly overwriting items installed during earlier access (it may have been an early web request)");
+                        catalog.reset(ImmutableList.<CatalogItem<?,?>>of());
+                    }
                 }
-                catalog = (BasicBrooklynCatalog) cm.get();
-            }
+                hasRunOfficial = true;
 
-            populateCatalog(catalog, needsInitial, true, optionalItemsForResettingCatalog);
-            
+                populateCatalog(catalog, needsInitial, true, optionalItemsForResettingCatalog);
+            }
         } finally {
-            runCount.incrementAndGet();
+            hasRunOfficial = true;
+            isPopulating = false;
         }
     }
 
@@ -170,7 +166,7 @@ public class CatalogInitialization implements ManagementContextInjectable {
     
     protected void populateInitial(BasicBrooklynCatalog catalog) {
         if (disallowLocal) {
-            if (!hasRun()) {
+            if (!hasRunOfficial()) {
                 log.debug("CLI initial catalog not being read with disallow-local mode set.");
             }
             return;
@@ -278,24 +274,27 @@ public class CatalogInitialization implements ManagementContextInjectable {
         return problem;
     }
 
+    boolean hasRunAdditions = false;
     protected void populateAdditions(BasicBrooklynCatalog catalog) {
         if (Strings.isNonBlank(additionsUri)) {
             if (disallowLocal) {
-                if (!hasRun()) {
+                if (!hasRunAdditions) {
                     log.warn("CLI additions supplied but not supported in disallow-local mode; ignoring.");
                 }
                 return;
             }   
-            if (!hasRun()) {
+            if (!hasRunAdditions) {
                 log.debug("Adding to catalog from CLI: "+additionsUri+" (force: "+force+")");
             }
             Iterable<? extends CatalogItem<?, ?>> items = catalog.addItems(
                 new ResourceUtils(this).getResourceAsString(additionsUri), force);
             
-            if (!hasRun())
+            if (!hasRunAdditions)
                 log.debug("Added to catalog from CLI: "+items);
             else
                 log.debug("Added to catalog from CLI: count "+Iterables.size(items));
+            
+            hasRunAdditions = true;
         }
     }
 
@@ -332,43 +331,22 @@ public class CatalogInitialization implements ManagementContextInjectable {
 
     /** makes the catalog, warning if persistence is on and hasn't run yet 
      * (as the catalog will be subsequently replaced) */
-    @Beta
-    public BrooklynCatalog getCatalogPopulatingBestEffort() {
-        Maybe<BrooklynCatalog> cm = ((ManagementContextInternal)managementContext).getCatalogIfSet();
-        if (cm.isPresent()) return cm.get();
-
-        BrooklynCatalog oldC = setCatalog(managementContext, new BasicBrooklynCatalog(managementContext),
-            "Request to make local catalog early, but someone else has created it, reverting to that", false);
-        if (oldC==null) {
-            // our catalog was added, so run population
-            // NB: we need the catalog to be saved already so that we can run callbacks
-            populateCatalog((BasicBrooklynCatalog) managementContext.getCatalog(), true, true, null);
-        }
-        
-        return managementContext.getCatalog();
-    }
-
-    /** Sets the catalog in the given management context, warning and choosing appropriately if one already exists. 
-     * Returns any previously existing catalog (whether or not changed). */
-    @Beta
-    public static BrooklynCatalog setCatalog(ManagementContext managementContext, BrooklynCatalog catalog, String messageIfAlready, boolean preferNew) {
-        Maybe<BrooklynCatalog> cm;
-        synchronized (managementContext) {
-            cm = ((ManagementContextInternal)managementContext).getCatalogIfSet();
-            if (cm.isAbsent()) {
-                ((ManagementContextInternal)managementContext).setCatalog(catalog);
-                return null;
-            }
-            if (preferNew) {
-                // set to null first to prevent errors
-                ((ManagementContextInternal)managementContext).setCatalog(null);
-                ((ManagementContextInternal)managementContext).setCatalog(catalog);
+    public void populateBestEffort(BasicBrooklynCatalog catalog) {
+        synchronized (mutex) {
+            if (hasRunOfficial || hasRunBestEffort || isPopulating) return;
+            // if a thread calls back in to this, ie calling to it from a getCatalog() call while populating,
+            // it will own the mutex and observe isRunningBestEffort, returning quickly 
+            isPopulating = true;
+            try {
+                if (isStartingUp) {
+                    log.warn("Catalog access requested when not yet initialized; populating best effort rather than through recommended pathway. Catalog data may be replaced subsequently.");
+                }
+                populateCatalog(catalog, true, true, null);
+            } finally {
+                hasRunBestEffort = true;
+                isPopulating = false;
             }
         }
-        if (Strings.isNonBlank(messageIfAlready)) {
-            log.warn(messageIfAlready);
-        }
-        return cm.get();
     }
 
     public void setStartingUp(boolean isStartingUp) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
index 13d02e8..3355851 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
@@ -350,7 +350,7 @@ public abstract class RebindIteration {
         Collection<CatalogItem<?,?>> itemsForResettingCatalog = null;
         boolean needsInitialCatalog;
         if (rebindManager.persistCatalogItemsEnabled) {
-            if (!catInit.hasRun() && catInit.isInitialResetRequested()) {
+            if (!catInit.hasRunOfficial() && catInit.isInitialResetRequested()) {
                 String message = "RebindManager resetting catalog on first run (catalog persistence enabled, but reset explicitly specified). ";
                 if (catalogItems.isEmpty()) {
                     message += "Catalog was empty anyway.";
@@ -371,7 +371,7 @@ public abstract class RebindIteration {
                     itemsForResettingCatalog = rebindContext.getCatalogItems();
                     needsInitialCatalog = false;
                 } else {
-                    if (catInit.hasRun()) {
+                    if (catInit.hasRunOfficial()) {
                         logRebindingDebug("RebindManager will re-add any new items (persisted state empty)");
                         needsInitialCatalog = false;
                     } else {
@@ -381,7 +381,7 @@ public abstract class RebindIteration {
                 }
             }
         } else {
-            if (catInit.hasRun()) {
+            if (catInit.hasRunOfficial()) {
                 logRebindingDebug("RebindManager skipping catalog init because it has already run (catalog persistence disabled)");
                 needsInitialCatalog = false;
             } else {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
index a31c1de..71d5218 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
@@ -371,7 +371,7 @@ public class RebindManagerImpl implements RebindManager {
         
     public void rebindPartialActive(CompoundTransformer transformer, Iterator<BrooklynObject> objectsToRebind) {
         final ClassLoader classLoader = 
-            managementContext.getCatalog().getRootClassLoader();
+            managementContext.getCatalogClassLoader();
         // TODO we might want different exception handling for partials;
         // failure at various points should leave proxies in a sensible state,
         // either pointing at old or at new, though this is relatively untested,
@@ -474,7 +474,7 @@ public class RebindManagerImpl implements RebindManager {
     @Override
     public List<Application> rebind(ClassLoader classLoaderO, RebindExceptionHandler exceptionHandlerO, ManagementNodeState modeO) {
         final ClassLoader classLoader = classLoaderO!=null ? classLoaderO :
-            managementContext.getCatalog().getRootClassLoader();
+            managementContext.getCatalogClassLoader();
         final RebindExceptionHandler exceptionHandler = exceptionHandlerO!=null ? exceptionHandlerO :
             RebindExceptionHandlerImpl.builder()
                 .danglingRefFailureMode(danglingRefFailureMode)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynPersistenceUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynPersistenceUtils.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynPersistenceUtils.java
index 24a2d38..8a76833 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynPersistenceUtils.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynPersistenceUtils.java
@@ -107,7 +107,7 @@ public class BrooklynPersistenceUtils {
         BrooklynMementoPersisterToObjectStore persister = new BrooklynMementoPersisterToObjectStore(
             destinationObjectStore,
             ((ManagementContextInternal)managementContext).getBrooklynProperties(),
-            managementContext.getCatalog().getRootClassLoader());
+            managementContext.getCatalogClassLoader());
         PersistenceExceptionHandler exceptionHandler = PersistenceExceptionHandlerImpl.builder().build();
         persister.enableWriteAccess();
         persister.checkpoint(memento, exceptionHandler);
@@ -117,7 +117,7 @@ public class BrooklynPersistenceUtils {
             PersistenceObjectStore destinationObjectStore) {
         if (optionalPlaneRecord != null) {
             ManagementPlaneSyncRecordPersisterToObjectStore managementPersister = new ManagementPlaneSyncRecordPersisterToObjectStore(
-                    managementContext, destinationObjectStore, managementContext.getCatalog().getRootClassLoader());
+                    managementContext, destinationObjectStore, managementContext.getCatalogClassLoader());
             managementPersister.checkpoint(optionalPlaneRecord);
         }
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/core/src/main/java/brooklyn/location/basic/BasicLocationRegistry.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/BasicLocationRegistry.java b/core/src/main/java/brooklyn/location/basic/BasicLocationRegistry.java
index c59298b..2b1980c 100644
--- a/core/src/main/java/brooklyn/location/basic/BasicLocationRegistry.java
+++ b/core/src/main/java/brooklyn/location/basic/BasicLocationRegistry.java
@@ -149,7 +149,7 @@ public class BasicLocationRegistry implements LocationRegistry {
     }
 
     protected void findServices() {
-        ServiceLoader<LocationResolver> loader = ServiceLoader.load(LocationResolver.class, mgmt.getCatalog().getRootClassLoader());
+        ServiceLoader<LocationResolver> loader = ServiceLoader.load(LocationResolver.class, mgmt.getCatalogClassLoader());
         for (LocationResolver r: loader) {
             registerResolver(r);
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java b/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java
index 29e07af..ebc2329 100644
--- a/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java
+++ b/core/src/main/java/brooklyn/management/classloading/JavaBrooklynClassLoadingContext.java
@@ -69,7 +69,7 @@ public class JavaBrooklynClassLoadingContext extends AbstractBrooklynClassLoadin
     
     private ClassLoader getClassLoader() {
         if (loader != null) return loader;
-        if (mgmt!=null) return mgmt.getCatalog().getRootClassLoader();
+        if (mgmt!=null) return mgmt.getCatalogClassLoader();
         return JavaBrooklynClassLoadingContext.class.getClassLoader();
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java b/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
index b004f6b..70bb13d 100644
--- a/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
+++ b/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
@@ -814,7 +814,7 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager {
         setInternalNodeState(ManagementNodeState.MASTER);
         publishPromotionToMaster();
         try {
-            managementContext.getRebindManager().rebind(managementContext.getCatalog().getRootClassLoader(), null, getInternalNodeState());
+            managementContext.getRebindManager().rebind(managementContext.getCatalogClassLoader(), null, getInternalNodeState());
         } catch (Exception e) {
             LOG.error("Management node "+managementContext.getManagementNodeId()+" enountered problem during rebind when promoting self to master; demoting to FAILED and rethrowing: "+e);
             demoteTo(ManagementNodeState.FAILED);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
index 8ff6dfc..1cbe312 100644
--- a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
+++ b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
@@ -148,7 +148,7 @@ public abstract class AbstractManagementContext implements ManagementContextInte
 
     protected BrooklynProperties configMap;
     protected BasicLocationRegistry locationRegistry;
-    protected volatile BasicBrooklynCatalog catalog;
+    protected final BasicBrooklynCatalog catalog;
     protected ClassLoader baseClassLoader;
     protected Iterable<URL> baseClassPathForScanning;
 
@@ -183,7 +183,9 @@ public abstract class AbstractManagementContext implements ManagementContextInte
             datagridFactory = loadDataGridFactory(brooklynProperties);
         }
         DataGrid datagrid = datagridFactory.newDataGrid(this);
-         
+
+        this.catalog = new BasicBrooklynCatalog(this);
+        
         this.storage = new BrooklynStorageImpl(datagrid);
         this.rebindManager = new RebindManagerImpl(this); // TODO leaking "this" reference; yuck
         this.highAvailabilityManager = new HighAvailabilityManagerImpl(this); // TODO leaking "this" reference; yuck
@@ -359,28 +361,20 @@ public abstract class AbstractManagementContext implements ManagementContextInte
     }
 
     @Override
-    public Maybe<BrooklynCatalog> getCatalogIfSet() {
-        return Maybe.<BrooklynCatalog>fromNullable(catalog);
-    }
-
-    @Override
-    public void setCatalog(BrooklynCatalog catalog) {
-        if (this.catalog!=null && catalog!=null) {
-            // should only happen if process has accessed catalog before rebind/startup populated it
-            log.warn("Replacing catalog in management context; new catalog is: "+catalog);
+    public BrooklynCatalog getCatalog() {
+        if (!getCatalogInitialization().hasRunIncludingBestEffort()) {
+            // catalog init is needed; normally this will be done from start sequence,
+            // but if accessed early -- and in tests -- we will load it here
+            getCatalogInitialization().injectManagementContext(this);
+            getCatalogInitialization().populateBestEffort(catalog);
         }
-        this.catalog = (BasicBrooklynCatalog) catalog;
+        return catalog;
     }
-
+    
     @Override
-    public BrooklynCatalog getCatalog() {
-        if (catalog!=null) 
-            return catalog;
-        
-        // catalog init is needed; normally this will be done from start sequence,
-        // but if accessed early -- and in tests -- we will load it here
-        // TODO log if in launcher mode
-        return getCatalogInitialization().getCatalogPopulatingBestEffort();
+    public ClassLoader getCatalogClassLoader() {
+        // catalog does not have to be initialized
+        return catalog.getRootClassLoader();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java b/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java
index d5123a6..20f9113 100644
--- a/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java
+++ b/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java
@@ -24,7 +24,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 
-import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.Effector;
@@ -119,11 +118,4 @@ public interface ManagementContextInternal extends ManagementContext {
     @Beta
     void setCatalogInitialization(CatalogInitialization catalogInitialization);
 
-    @Beta
-    public Maybe<BrooklynCatalog> getCatalogIfSet();
-    
-    /** For use from {@link CatalogInitialization} to set the catalog */
-    @Beta
-    public void setCatalog(BrooklynCatalog catalog);
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java b/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
index 77eb4de..00639a5 100644
--- a/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
+++ b/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
@@ -35,7 +35,6 @@ import org.slf4j.LoggerFactory;
 
 import brooklyn.basic.BrooklynObject;
 import brooklyn.catalog.BrooklynCatalog;
-import brooklyn.catalog.internal.BasicBrooklynCatalog;
 import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.StringConfigMap;
@@ -75,7 +74,6 @@ import brooklyn.mementos.BrooklynMementoRawData;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.time.Duration;
 
-import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 
 public class NonDeploymentManagementContext implements ManagementContextInternal {
@@ -324,6 +322,12 @@ public class NonDeploymentManagementContext implements ManagementContextInternal
     }
     
     @Override
+    public ClassLoader getCatalogClassLoader() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getCatalogClassLoader();
+    }
+    
+    @Override
     public EntitlementManager getEntitlementManager() {
         return entitlementManager;
     }
@@ -462,19 +466,6 @@ public class NonDeploymentManagementContext implements ManagementContextInternal
         initialManagementContext.setCatalogInitialization(catalogInitialization);
     }
 
-    @Override
-    public Maybe<BrooklynCatalog> getCatalogIfSet() {
-        checkInitialManagementContextReal();
-        return initialManagementContext.getCatalogIfSet();
-    }
-    
-    /** For use from {@link CatalogInitialization} to set the catalog */
-    @Beta @Override
-    public void setCatalog(BrooklynCatalog catalog) {
-        checkInitialManagementContextReal();
-        initialManagementContext.setCatalog(catalog);
-    }
-    
     /**
      * For when the initial management context is not "real"; the changeListener is a no-op, but everything else forbidden.
      * 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
index 698731b..45019d5 100644
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
+++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
@@ -321,7 +321,7 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
     protected CloudMachineNamer getCloudMachineNamer(ConfigBag config) {
         String namerClass = config.get(LocationConfigKeys.CLOUD_MACHINE_NAMER_CLASS);
         if (Strings.isNonBlank(namerClass)) {
-            Optional<CloudMachineNamer> cloudNamer = Reflections.invokeConstructorWithArgs(getManagementContext().getCatalog().getRootClassLoader(), namerClass, config);
+            Optional<CloudMachineNamer> cloudNamer = Reflections.invokeConstructorWithArgs(getManagementContext().getCatalogClassLoader(), namerClass, config);
             if (cloudNamer.isPresent()) {
                 return cloudNamer.get();
             } else {
@@ -341,7 +341,7 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
         @SuppressWarnings("deprecation")
         String customizersSupplierType = setup.get(JCLOUDS_LOCATION_CUSTOMIZERS_SUPPLIER_TYPE);
 
-        ClassLoader catalogClassLoader = getManagementContext().getCatalog().getRootClassLoader();
+        ClassLoader catalogClassLoader = getManagementContext().getCatalogClassLoader();
         List<JcloudsLocationCustomizer> result = new ArrayList<JcloudsLocationCustomizer>();
         if (customizer != null) result.add(customizer);
         if (customizers != null) result.addAll(customizers);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
index 618d4a3..492ef65 100644
--- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
+++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
@@ -28,7 +28,6 @@ import io.brooklyn.camp.spi.PlatformComponentTemplate;
 
 import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.ServiceLoader;
 import java.util.Set;
@@ -121,7 +120,7 @@ public class BrooklynComponentTemplateResolver {
             if (type.indexOf(':') != -1) {
                 String prefix = Splitter.on(":").splitToList(type).get(0);
                 ServiceLoader<ServiceTypeResolver> loader = ServiceLoader.load(ServiceTypeResolver.class,
-                        context.getManagementContext().getCatalog().getRootClassLoader());
+                        context.getManagementContext().getCatalogClassLoader());
                 for (ServiceTypeResolver resolver : loader) {
                    if (prefix.equals(resolver.getTypePrefix())) {
                        return resolver;
@@ -220,7 +219,7 @@ public class BrooklynComponentTemplateResolver {
         return spec;
     }
 
-    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @SuppressWarnings({ "unchecked" })
     protected <T extends Entity> EntitySpec<T> createSpec(Set<String> encounteredCatalogTypes) {
         CatalogItem<Entity, EntitySpec<?>> item = getServiceTypeResolver().getCatalogItem(this, getDeclaredType());
         if (encounteredCatalogTypes==null) encounteredCatalogTypes = MutableSet.of();
@@ -258,6 +257,7 @@ public class BrooklynComponentTemplateResolver {
         }
     }
     
+    @SuppressWarnings("unchecked")
     protected <T extends Entity> EntitySpec<T> createSpecFromJavaType() {
         Class<T> type = (Class<T>) loadEntityClass();
         
@@ -267,6 +267,7 @@ public class BrooklynComponentTemplateResolver {
         } else {
             // If this is a concrete class, particularly for an Application class, we want the proxy
             // to expose all interfaces it implements.
+            @SuppressWarnings("rawtypes")
             Class interfaceclazz = (Application.class.isAssignableFrom(type)) ? Application.class : Entity.class;
             List<Class<?>> additionalInterfaceClazzes = Reflections.getAllInterfaces(type);
             spec = EntitySpec.create(interfaceclazz).impl(type).additionalInterfaces(additionalInterfaceClazzes);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
index 71f53a5..3a87087 100644
--- a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
+++ b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
@@ -598,7 +598,7 @@ public class BrooklynLauncher {
         try {
             // run cat init now if it hasn't yet been run; 
             // will also run if there was an ignored error in catalog above, allowing it to fail startup here if requested
-            if (catInit!=null && !catInit.hasRun()) {
+            if (catInit!=null && !catInit.hasRunOfficial()) {
                 LOG.debug("Loading catalog as part of launcher (persistence did not run it)");
                 catInit.populateCatalog(true, null);
             }
@@ -792,7 +792,7 @@ public class BrooklynLauncher {
                 BrooklynMementoPersisterToObjectStore persister = new BrooklynMementoPersisterToObjectStore(
                     objectStore,
                     ((ManagementContextInternal)managementContext).getBrooklynProperties(),
-                    managementContext.getCatalog().getRootClassLoader());
+                    managementContext.getCatalogClassLoader());
                 PersistenceExceptionHandler persistenceExceptionHandler = PersistenceExceptionHandlerImpl.builder().build();
                 ((RebindManagerImpl) rebindManager).setPeriodicPersistPeriod(persistPeriod);
                 rebindManager.setPersister(persister, persistenceExceptionHandler);
@@ -816,7 +816,8 @@ public class BrooklynLauncher {
             HighAvailabilityManager haManager = managementContext.getHighAvailabilityManager();
             ManagementPlaneSyncRecordPersister persister =
                 new ManagementPlaneSyncRecordPersisterToObjectStore(managementContext,
-                    objectStore, managementContext.getCatalog().getRootClassLoader());
+                    objectStore,
+                    managementContext.getCatalogClassLoader());
             ((HighAvailabilityManagerImpl)haManager).setHeartbeatTimeout(haHeartbeatTimeoutOverride);
             ((HighAvailabilityManagerImpl)haManager).setPollPeriod(haHeartbeatPeriodOverride);
             haManager.setPersister(persister);
@@ -866,7 +867,7 @@ public class BrooklynLauncher {
         else
             LOG.info("Management node (no HA) rebinding to entities on file system in "+persistenceDir);
 
-        ClassLoader classLoader = managementContext.getCatalog().getRootClassLoader();
+        ClassLoader classLoader = managementContext.getCatalogClassLoader();
         try {
             rebindManager.rebind(classLoader, null, ManagementNodeState.MASTER);
         } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/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 27a93a9..0144d56 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
@@ -404,7 +404,7 @@ public class ApplicationResource extends AbstractBrooklynRestResource implements
     private void checkEntityTypeIsValid(String type) {
         if (CatalogUtils.getCatalogItemOptionalVersion(mgmt(), type) == null) {
             try {
-                brooklyn().getCatalog().getRootClassLoader().loadClass(type);
+                brooklyn().getCatalogClassLoader().loadClass(type);
             } catch (ClassNotFoundException e) {
                 log.debug("Class not found for type '" + type + "'; reporting 404", e);
                 throw WebResourceUtils.notFound("Undefined type '%s'", type);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/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 5572121..3e94068 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
@@ -170,7 +170,7 @@ public class EntityResource extends AbstractBrooklynRestResource implements Enti
             // paths (ie non-protocol) and
             // NB, for security, file URL's are NOT served
             MediaType mime = WebResourceUtils.getImageMediaTypeFromExtension(Files.getFileExtension(url));
-            Object content = ResourceUtils.create(brooklyn().getCatalog().getRootClassLoader()).getResourceFromUrl(url);
+            Object content = ResourceUtils.create(brooklyn().getCatalogClassLoader()).getResourceFromUrl(url);
             return Response.ok(content, mime).build();
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/usage/rest-server/src/main/java/brooklyn/rest/util/BrooklynRestResourceUtils.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/util/BrooklynRestResourceUtils.java b/usage/rest-server/src/main/java/brooklyn/rest/util/BrooklynRestResourceUtils.java
index 2dbed30..c3499b8 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/util/BrooklynRestResourceUtils.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/util/BrooklynRestResourceUtils.java
@@ -95,6 +95,10 @@ public class BrooklynRestResourceUtils {
         return mgmt.getCatalog();
     }
     
+    public ClassLoader getCatalogClassLoader() {
+        return mgmt.getCatalogClassLoader();
+    }
+    
     public LocationRegistry getLocationRegistry() {
         return mgmt.getLocationRegistry();
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddbb43c6/utils/common/src/main/java/brooklyn/util/javalang/AggregateClassLoader.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/javalang/AggregateClassLoader.java b/utils/common/src/main/java/brooklyn/util/javalang/AggregateClassLoader.java
index 07af10a..1a5dd93 100644
--- a/utils/common/src/main/java/brooklyn/util/javalang/AggregateClassLoader.java
+++ b/utils/common/src/main/java/brooklyn/util/javalang/AggregateClassLoader.java
@@ -20,6 +20,7 @@ package brooklyn.util.javalang;
 
 import java.io.IOException;
 import java.net.URL;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
@@ -34,7 +35,7 @@ import com.google.common.collect.Sets;
  * exposing more info, a few conveniences, and a nice toString */
 public class AggregateClassLoader extends ClassLoader {
 
-    private final List<ClassLoader> classLoaders = new CopyOnWriteArrayList<ClassLoader>();
+    private final CopyOnWriteArrayList<ClassLoader> classLoaders = new CopyOnWriteArrayList<ClassLoader>();
 
     private AggregateClassLoader() {
         //Don't pass load requests to the app classloader,
@@ -69,8 +70,25 @@ public class AggregateClassLoader extends ClassLoader {
         if (classLoader != null) classLoaders.add(index, classLoader);
     }
     
-    /** Returns the _live_ (and modifiable) list of classloaders 
-     * @return */ 
+    /** Resets the classloader shown here to be the given set */
+    public void reset(Collection<? extends ClassLoader> newClassLoaders) {
+        synchronized (classLoaders) {
+            int count = classLoaders.size();
+            classLoaders.addAll(newClassLoaders);
+            for (int i=0; i<count; i++) {
+                classLoaders.remove(0);
+            }
+        }
+    }
+
+    /** True if nothing is in the list here */
+    public boolean isEmpty() {
+        return classLoaders.isEmpty();
+    }
+    
+    /** Returns the _live_ (and modifiable) list of classloaders; dangerous and discouraged. 
+     * @deprecated since 0.7.0 */
+    @Deprecated
     public List<ClassLoader> getList() {
         return classLoaders;
     }



[16/23] incubator-brooklyn git commit: enable password auth if needed when a password is requested to be set for a user being created

Posted by he...@apache.org.
enable password auth if needed when a password is requested to be set for a user being created


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

Branch: refs/heads/master
Commit: 3ed4459e70864e15c6d706a7ba5ee90a8037fec5
Parents: 38c92c8
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri May 8 17:11:37 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:51:50 2015 +0100

----------------------------------------------------------------------
 .../src/main/java/brooklyn/location/jclouds/JcloudsLocation.java  | 3 +++
 1 file changed, 3 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/3ed4459e/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
index 4e1c24e..1118409 100644
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
+++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
@@ -1555,6 +1555,9 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
                 createdUserCreds = LoginCredentials.builder().user(user).privateKey(privKey).build();
             } else if (passwordToSet!=null) {
                 createdUserCreds = LoginCredentials.builder().user(user).password(passwordToSet).build();
+                
+                // if setting a password also ensure password is permitted for ssh
+                statements.add(org.jclouds.scriptbuilder.statements.ssh.SshStatements.sshdConfig(ImmutableMap.of("PasswordAuthentication", "yes")));
             }
         }
 


[17/23] incubator-brooklyn git commit: tidying and bug-fixing around domain names and hostname salting

Posted by he...@apache.org.
tidying and bug-fixing around domain names and hostname salting


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

Branch: refs/heads/master
Commit: 5d767b69500fb26e2100bac300f881d425a8930b
Parents: b4518dd
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri May 8 17:48:12 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:51:51 2015 +0100

----------------------------------------------------------------------
 .../location/cloud/CloudLocationConfig.java      |  4 ++--
 .../cloud/names/AbstractCloudMachineNamer.java   |  1 +
 docs/guide/ops/locations/index.md                |  5 +++--
 .../location/jclouds/JcloudsLocation.java        | 19 +++++++++----------
 4 files changed, 15 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5d767b69/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java b/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
index 6831726..6c941a3 100644
--- a/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
+++ b/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
@@ -57,9 +57,9 @@ public interface CloudLocationConfig {
     /** @deprecated since 0.6.0; included here so it gets picked up in auto-detect routines */ @Deprecated
     public static final ConfigKey<String> LEGACY_PRIVATE_KEY_PASSPHRASE = LocationConfigKeys.LEGACY_PRIVATE_KEY_PASSPHRASE;
 
-    // default is just shy of common 64-char boundary (could perhaps increase slightly...)
+    // default is just shy of common 64-char boundary, leaving 4 chars plus our salt allowance (default 4+1) which allows up to -12345678 by jclouds
     public static final ConfigKey<Integer> VM_NAME_MAX_LENGTH = ConfigKeys.newIntegerConfigKey(
-        "vmNameMaxLength", "Maximum length of VM name", 61);
+        "vmNameMaxLength", "Maximum length of VM name", 60);
 
     public static final ConfigKey<Integer> VM_NAME_SALT_LENGTH = ConfigKeys.newIntegerConfigKey(
         "vmNameSaltLength", "Number of characters to use for a random identifier inserted in hostname "

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5d767b69/core/src/main/java/brooklyn/location/cloud/names/AbstractCloudMachineNamer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/cloud/names/AbstractCloudMachineNamer.java b/core/src/main/java/brooklyn/location/cloud/names/AbstractCloudMachineNamer.java
index 4e81a46..8e4cd41 100644
--- a/core/src/main/java/brooklyn/location/cloud/names/AbstractCloudMachineNamer.java
+++ b/core/src/main/java/brooklyn/location/cloud/names/AbstractCloudMachineNamer.java
@@ -95,6 +95,7 @@ public abstract class AbstractCloudMachineNamer implements CloudMachineNamer {
         return defaultMachineNameMaxLength;  
     }
     
+    // sometimes we create salt string, sometimes jclouds does
     public int getLengthForMachineUniqueNameSalt(ConfigBag setup, boolean includeSeparator) {
         int saltLen;
         if (setup.containsKey(CloudLocationConfig.VM_NAME_SALT_LENGTH)) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5d767b69/docs/guide/ops/locations/index.md
----------------------------------------------------------------------
diff --git a/docs/guide/ops/locations/index.md b/docs/guide/ops/locations/index.md
index 02e82bb..78f185d 100644
--- a/docs/guide/ops/locations/index.md
+++ b/docs/guide/ops/locations/index.md
@@ -136,8 +136,9 @@ For more keys and more detail on the keys below, see
   `cloudMachineNamer: brooklyn.location.cloud.names.CustomMachineNamer`.
   {% include java_link.html class_name="CustomMachineNamer" package_path="brooklyn/location/cloud/names" project_subpath="core" %}
   will use the entity's name or following a template you supply.
-  For all names, a random suffix will be appended to help guarantee uniqueness;
-  this can be removed by setting `vmNameSaltLength: 0`.
+  On many clouds, a random suffix will be appended to help guarantee uniqueness;
+  this can be removed by setting `vmNameSaltLength: 0` (selected clouds only).
+  <!-- TODO jclouds softlayer includes a 3-char hex suffix -->
   
 - A DNS domain name where this host should be placed can be specified with `domainName`
   (in selected clouds only)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5d767b69/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
index c74cfe2..4cef9d0 100644
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
+++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
@@ -600,7 +600,9 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
                 }
 
                 templateTimestamp = Duration.of(provisioningStopwatch);
-                // "Name" sets jclouds hostname
+                // "Name" metadata seems to set the display name; at least in AWS
+                // TODO it would be nice if this salt comes from the location's ID (but we don't know that yet as the ssh machine location isn't created yet)
+                // TODO in softlayer we want to control the suffix of the hostname which is 3 random hex digits
                 template.getOptions().getUserMetadata().put("Name", cloudMachineNamer.generateNewMachineUniqueNameFromGroupId(setup, groupId));
                 
                 if (setup.get(JcloudsLocationConfig.INCLUDE_BROOKLYN_USER_METADATA)) {
@@ -1068,7 +1070,7 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
               .put(DOMAIN_NAME, new CustomizeTemplateOptions() {
                     public void apply(TemplateOptions t, ConfigBag props, Object v) {
                         if (t instanceof SoftLayerTemplateOptions) {
-                            ((SoftLayerTemplateOptions)t).domainName((String)v);
+                            ((SoftLayerTemplateOptions)t).domainName(TypeCoercions.coerce(v, String.class));
                         } else {
                             LOG.info("ignoring domain-name({}) in VM creation because not supported for cloud/type ({})", v, t);                            
                         }
@@ -1122,20 +1124,17 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
         // these things are nice on softlayer
         if (template.getOptions() instanceof SoftLayerTemplateOptions) {
             SoftLayerTemplateOptions slT = ((SoftLayerTemplateOptions)template.getOptions());
-            if (Strings.isEmpty(slT.getDomainName())) {
-                // set a quasi-sensible domain name if none was provided (better than the default, jclouds.org) 
-                slT.domainName("brooklyn.local");
+            if (Strings.isBlank(slT.getDomainName()) || "jclouds.org".equals(slT.getDomainName())) {
+                // set a quasi-sensible domain name if none was provided (better than the default, jclouds.org)
+                // NB: things like brooklyn.local are disallowed
+                slT.domainName("local.brooklyncentral.org");
             }
             // convert user metadata to tags because user metadata is otherwise ignored
             Map<String, String> md = slT.getUserMetadata();
             if (md!=null && !md.isEmpty()) {
                 Set<String> tags = MutableSet.copyOf(slT.getTags());
                 for (Map.Entry<String,String> entry: md.entrySet()) {
-                    if ("Name".equalsIgnoreCase(entry.getKey())) {
-                        // skip "Name" but use the other tags
-                    } else {
-                        tags.add(AbstractCloudMachineNamer.sanitize(entry.getKey())+":"+AbstractCloudMachineNamer.sanitize(entry.getValue()));
-                    }
+                    tags.add(AbstractCloudMachineNamer.sanitize(entry.getKey())+":"+AbstractCloudMachineNamer.sanitize(entry.getValue()));
                 }
                 slT.tags(tags);
             }


[14/23] incubator-brooklyn git commit: set a bunch of tags telling more about the user/app/entity for which a VM is created

Posted by he...@apache.org.
set a bunch of tags telling more about the user/app/entity for which a VM is created


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

Branch: refs/heads/master
Commit: 41c2d9b90867222e5248806bc9aabe09a776b74d
Parents: 8008df4
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri May 8 14:21:06 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:51:50 2015 +0100

----------------------------------------------------------------------
 .../location/jclouds/JcloudsLocation.java       | 65 +++++++++++++++++---
 .../location/jclouds/JcloudsLocationConfig.java |  5 +-
 2 files changed, 60 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/41c2d9b9/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
index 1e1dccf..4e1c24e 100644
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
+++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
@@ -90,6 +90,7 @@ import org.slf4j.LoggerFactory;
 import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;
 import brooklyn.config.ConfigUtils;
+import brooklyn.entity.Entity;
 import brooklyn.entity.basic.Sanitizer;
 import brooklyn.entity.rebind.persister.LocationWithObjectStore;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore;
@@ -108,6 +109,7 @@ import brooklyn.location.basic.LocationConfigUtils.OsCredential;
 import brooklyn.location.basic.SshMachineLocation;
 import brooklyn.location.cloud.AbstractCloudMachineProvisioningLocation;
 import brooklyn.location.cloud.AvailabilityZoneExtension;
+import brooklyn.location.cloud.names.AbstractCloudMachineNamer;
 import brooklyn.location.cloud.names.CloudMachineNamer;
 import brooklyn.location.jclouds.JcloudsPredicates.NodeInLocation;
 import brooklyn.location.jclouds.networking.JcloudsPortForwarderExtension;
@@ -117,6 +119,7 @@ import brooklyn.management.AccessController;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
+import brooklyn.util.collections.MutableSet;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.crypto.SecureKeys;
 import brooklyn.util.exceptions.CompoundRuntimeException;
@@ -596,21 +599,33 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
                     userCredentials = initTemplateForCreateUser(template, setup);
                 }
 
-                //FIXME initialCredentials = initUserTemplateOptions(template, setup);
-                for (JcloudsLocationCustomizer customizer : getCustomizers(setup)) {
-                    customizer.customize(this, computeService, template);
-                    customizer.customize(this, computeService, template.getOptions());
+                templateTimestamp = Duration.of(provisioningStopwatch);
+                // "Name" sets jclouds hostname
+                template.getOptions().getUserMetadata().put("Name", cloudMachineNamer.generateNewMachineUniqueNameFromGroupId(setup, groupId));
+                
+                if (setup.get(JcloudsLocationConfig.INCLUDE_BROOKLYN_USER_METADATA)) {
+                    template.getOptions().getUserMetadata().put("brooklyn-user", System.getProperty("user.name"));
+                    
+                    Object context = setup.get(CALLER_CONTEXT);
+                    if (context instanceof Entity) {
+                        Entity entity = (Entity)context;
+                        template.getOptions().getUserMetadata().put("brooklyn-app-id", entity.getApplicationId());
+                        template.getOptions().getUserMetadata().put("brooklyn-app-name", entity.getApplication().getDisplayName());
+                        template.getOptions().getUserMetadata().put("brooklyn-entity-id", entity.getId());
+                        template.getOptions().getUserMetadata().put("brooklyn-entity-name", entity.getDisplayName());
+                        template.getOptions().getUserMetadata().put("brooklyn-server-creation-date", Time.makeDateSimpleStampString());
+                    }
                 }
+                
+                customizeTemplate(setup, computeService, template);
+                
                 LOG.debug("jclouds using template {} / options {} to provision machine in {}",
                         new Object[] {template, template.getOptions(), setup.getDescription()});
 
                 if (!setup.getUnusedConfig().isEmpty())
                     LOG.debug("NOTE: unused flags passed to obtain VM in "+setup.getDescription()+": "+
                             setup.getUnusedConfig());
-
-                templateTimestamp = Duration.of(provisioningStopwatch);
-                template.getOptions().getUserMetadata().put("Name", cloudMachineNamer.generateNewMachineUniqueNameFromGroupId(setup, groupId));
-
+                
                 nodes = computeService.createNodesInGroup(groupId, 1, template);
                 provisionTimestamp = Duration.of(provisioningStopwatch);
             } finally {
@@ -1089,6 +1104,37 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
               })
             .build();
 
+    /** hook whereby template customizations can be made for various clouds */
+    protected void customizeTemplate(ConfigBag setup, ComputeService computeService, Template template) {
+        for (JcloudsLocationCustomizer customizer : getCustomizers(setup)) {
+            customizer.customize(this, computeService, template);
+            customizer.customize(this, computeService, template.getOptions());
+        }
+
+        // these things are nice on softlayer
+        if (template.getOptions() instanceof SoftLayerTemplateOptions) {
+            SoftLayerTemplateOptions slT = ((SoftLayerTemplateOptions)template.getOptions());
+            if (Strings.isEmpty(slT.getDomainName())) {
+                // set a quasi-sensible domain name if none was provided (better than the default, jclouds.org) 
+                slT.domainName("brooklyn.local");
+            }
+            // convert user metadata to tags because user metadata is otherwise ignored
+            Map<String, String> md = slT.getUserMetadata();
+            if (md!=null && !md.isEmpty()) {
+                Set<String> tags = MutableSet.copyOf(slT.getTags());
+                for (Map.Entry<String,String> entry: md.entrySet()) {
+                    if ("Name".equalsIgnoreCase(entry.getKey())) {
+                        // skip "Name" but use the other tags
+                    } else {
+                        tags.add(AbstractCloudMachineNamer.sanitize(entry.getKey())+":"+AbstractCloudMachineNamer.sanitize(entry.getValue()));
+                    }
+                }
+                slT.tags(tags);
+            }
+            // TODO put user metadata and tags into notes, when jclouds exposes notes, because metadata not exposed via web portal 
+        }
+    }
+    
     private static boolean listedAvailableTemplatesOnNoSuchTemplate = false;
 
     /** returns the jclouds Template which describes the image to be built, for the given config and compute service */
@@ -1465,6 +1511,7 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
             AdminAccess.Builder adminBuilder = AdminAccess.builder()
                     .adminUsername(user)
                     .grantSudoToAdminUser(grantUserSudo);
+            adminBuilder.cryptFunction(Sha512Crypt.function());
 
             boolean useKey = Strings.isNonBlank(pubKey);
             adminBuilder.cryptFunction(Sha512Crypt.function());
@@ -1519,7 +1566,7 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
         LOG.debug("Machine we are about to create in "+this+" will be customized with: "+
             statements);
 
-        return new UserCreation(createdUserCreds, statements);
+        return new UserCreation(createdUserCreds, statements);  
     }
 
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/41c2d9b9/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java
index 3e03837..a3a0304 100644
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java
+++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java
@@ -77,7 +77,7 @@ public interface JcloudsLocationConfig extends CloudLocationConfig {
     @SuppressWarnings("serial")
     public static final ConfigKey<List<String>> EXTRA_PUBLIC_KEY_URLS_TO_AUTH = ConfigKeys.newConfigKey(new TypeToken<List<String>>() {}, 
         "extraSshPublicKeyUrls", "Additional public keys (files or URLs, in SSH2/RFC4716/id_rsa.pub format) to add to authorized_keys", null);
-    
+
     public static final ConfigKey<Boolean> DONT_CREATE_USER = ConfigKeys.newBooleanConfigKey("dontCreateUser", 
             "Whether to skip creation of 'user' when provisioning machines (default false)", false);
     public static final ConfigKey<Boolean> GRANT_USER_SUDO = ConfigKeys.newBooleanConfigKey("grantUserSudo",
@@ -161,6 +161,9 @@ public interface JcloudsLocationConfig extends CloudLocationConfig {
     @Deprecated /** @deprecated since 0.7.0 use #USER_METADATA_MAP */
     public static final ConfigKey<Object> USER_METADATA = USER_METADATA_MAP;
 
+    public static final ConfigKey<Boolean> INCLUDE_BROOKLYN_USER_METADATA = ConfigKeys.newBooleanConfigKey("includeBrooklynUserMetadata", 
+        "Whether to set metadata about the context of a machine, e.g. brooklyn-entity-id, brooklyn-app-name (default true)", true);
+
     public static final ConfigKey<Boolean> MAP_DEV_RANDOM_TO_DEV_URANDOM = ConfigKeys.newBooleanConfigKey(
             "installDevUrandom", "Map /dev/random to /dev/urandom to prevent halting on insufficient entropy", true);
 


[21/23] incubator-brooklyn git commit: This closes #634

Posted by he...@apache.org.
This closes #634


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

Branch: refs/heads/master
Commit: f4de73e8a5b3646d8b0185eead50863d7ec36940
Parents: 776ad43 5d767b6
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri May 22 10:04:08 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 22 10:04:08 2015 +0100

----------------------------------------------------------------------
 .../java/brooklyn/catalog/BrooklynCatalog.java  |   7 +-
 .../brooklyn/management/ManagementContext.java  |   5 +
 .../mementos/BrooklynMementoPersister.java      |   7 +-
 .../mementos/BrooklynMementoRawData.java        |   6 +
 .../java/brooklyn/catalog/CatalogLoadMode.java  |   3 +
 .../brooklyn/catalog/CatalogPredicates.java     |   1 +
 .../catalog/internal/BasicBrooklynCatalog.java  | 141 ++--
 .../catalog/internal/CatalogBundleDto.java      |  19 +
 .../brooklyn/catalog/internal/CatalogDo.java    |  26 +-
 .../brooklyn/catalog/internal/CatalogDto.java   |  10 +
 .../catalog/internal/CatalogInitialization.java | 377 ++++++++++
 .../internal/CatalogItemDtoAbstract.java        |  32 +
 .../brooklyn/config/BrooklynServerConfig.java   |  14 +-
 .../brooklyn/entity/rebind/RebindIteration.java |  82 ++-
 .../entity/rebind/RebindManagerImpl.java        |   4 +-
 .../AbstractBrooklynMementoPersister.java       |  12 +-
 .../BrooklynMementoPersisterToMultiFile.java    |   6 +-
 .../BrooklynMementoPersisterToObjectStore.java  |  19 +-
 .../persister/BrooklynPersistenceUtils.java     |   4 +-
 .../location/basic/BasicLocationRegistry.java   |   2 +-
 .../location/cloud/CloudLocationConfig.java     |  11 +-
 .../location/cloud/CloudMachineNamer.java       | 169 -----
 .../location/cloud/CustomMachineNamer.java      |  72 --
 .../cloud/names/AbstractCloudMachineNamer.java  | 150 ++++
 .../cloud/names/BasicCloudMachineNamer.java     |  91 +++
 .../location/cloud/names/CloudMachineNamer.java |  61 ++
 .../cloud/names/CustomMachineNamer.java         |  72 ++
 .../JavaBrooklynClassLoadingContext.java        |   2 +-
 .../ha/HighAvailabilityManagerImpl.java         |   2 +-
 .../internal/AbstractManagementContext.java     |  50 +-
 .../internal/ManagementContextInternal.java     |  13 +-
 .../NonDeploymentManagementContext.java         |  19 +
 .../main/resources/brooklyn/empty.catalog.bom   |  18 +
 .../catalog/internal/CatalogScanTest.java       |  17 +-
 .../entity/rebind/RebindCatalogEntityTest.java  |  34 +-
 .../entity/rebind/RebindCatalogItemTest.java    |   4 +-
 .../brooklyn/entity/rebind/RebindTestUtils.java |   2 +-
 .../location/cloud/CloudMachineNamerTest.java   |  45 +-
 .../location/cloud/CustomMachineNamerTest.java  |   7 +-
 .../entity/LocalManagementContextForTests.java  |   3 +-
 .../test/resources/brooklyn/default.catalog.bom |  19 +
 docs/guide/ops/catalog/index.md                 |  20 +
 docs/guide/ops/install-on-server.md             |  23 +-
 docs/guide/ops/locations/index.md               | 119 +--
 docs/guide/start/blueprints.md                  |  13 +-
 docs/guide/start/catalog.xml                    |  22 -
 .../location/jclouds/JcloudsLocation.java       |  85 ++-
 .../location/jclouds/JcloudsLocationConfig.java |   6 +-
 .../location/jclouds/JcloudsMachineNamer.java   |  11 +-
 .../location/jclouds/JcloudsLocationTest.java   |   8 +-
 .../jclouds/JcloudsMachineNamerTest.java        |  10 +-
 pom.xml                                         |   5 +-
 .../entity/brooklynnode/BrooklynNode.java       |   6 +
 .../entity/service/InitdServiceInstaller.java   |   4 +-
 .../BrooklynNodeIntegrationTest.java            |   8 +-
 .../java/brooklyn/entity/nosql/riak/riak.png    | Bin 110651 -> 0 bytes
 .../brooklyn/entity/nosql/riak/riak.png         | Bin 0 -> 110651 bytes
 .../BrooklynComponentTemplateResolver.java      |   7 +-
 .../brooklyn/catalog/CatalogYamlEntityTest.java |  20 +-
 .../catalog/CatalogYamlVersioningTest.java      |  11 +-
 usage/cli/pom.xml                               |   5 +-
 .../src/main/java/brooklyn/cli/ItemLister.java  |  77 +-
 usage/cli/src/main/java/brooklyn/cli/Main.java  | 113 +--
 .../java/brooklyn/cli/lister/ClassFinder.java   |   7 +-
 .../brooklyn/cli/lister/ItemDescriptors.java    |  20 +-
 .../main/resources/brooklyn-object-list.html    | 147 ----
 .../main/resources/brooklyn/default.catalog.bom |  23 +
 .../statics/brooklyn-object-list.html           | 147 ++++
 .../brooklyn/item-lister/statics/common.js      |  94 +++
 .../brooklyn/item-lister/statics/items.css      | 153 ++++
 .../statics/style/js/catalog/bloodhound.js      | 727 +++++++++++++++++++
 .../statics/style/js/underscore-min.js          |   6 +
 .../statics/style/js/underscore-min.map         |   1 +
 .../item-lister/templates/enricher.html         |  59 ++
 .../brooklyn/item-lister/templates/entity.html  |  66 ++
 .../item-lister/templates/location.html         |  62 ++
 .../brooklyn/item-lister/templates/policy.html  |  59 ++
 usage/cli/src/main/resources/common.js          |  94 ---
 usage/cli/src/main/resources/enricher.html      |  59 --
 usage/cli/src/main/resources/entity.html        |  66 --
 usage/cli/src/main/resources/items.css          | 153 ----
 .../src/main/resources/libs/js/bloodhound.js    | 727 -------------------
 usage/cli/src/main/resources/location.html      |  62 --
 usage/cli/src/main/resources/policy.html        |  59 --
 usage/dist/pom.xml                              |  10 +
 .../main/dist/conf/brooklyn/default.catalog.bom |  41 ++
 usage/dist/src/main/dist/conf/catalog.xml       |  37 -
 usage/dist/src/main/dist/conf/logback.xml       |  18 -
 .../brooklyn/launcher/BrooklynLauncher.java     |  63 +-
 .../brooklyn/launcher/BrooklynLauncherTest.java |   9 +-
 .../rest/resources/ApplicationResource.java     |   2 +-
 .../brooklyn/rest/resources/EntityResource.java |   2 +-
 .../rest/util/BrooklynRestResourceUtils.java    |   4 +
 .../brooklyn/rest/BrooklynRestApiLauncher.java  |   8 +-
 .../BrooklynRestApiLauncherTestFixture.java     |   5 +-
 .../util/BrooklynRestResourceUtilsTest.java     |   5 +-
 .../resources/brooklyn/scanning.catalog.bom     |  19 +
 .../util/javalang/AggregateClassLoader.java     |  24 +-
 98 files changed, 3161 insertions(+), 2088 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f4de73e8/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f4de73e8/docs/guide/ops/catalog/index.md
----------------------------------------------------------------------
diff --cc docs/guide/ops/catalog/index.md
index 074bbf3,7cb0e84..b93481c
--- a/docs/guide/ops/catalog/index.md
+++ b/docs/guide/ops/catalog/index.md
@@@ -5,9 -5,11 +5,11 @@@ children
  - { section: General YAML Schema }
  - { section: Catalog Metadata }
  - { section: Catalog YAML Examples }
 -- { section: Adding to the Catalog, title: Adding and Deleting } 
  - { section: Templates and the Add-Application Wizard, title: Templates }
 +- { section: Adding to the Catalog, title: Adding and Deleting } 
  - { section: Versioning } 
+ - { section: CLI Options }
+  
  ---
  
  Brooklyn provides a **catalog**, which is a persisted collection of versioned blueprints and other resources. 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f4de73e8/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f4de73e8/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f4de73e8/pom.xml
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f4de73e8/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
----------------------------------------------------------------------


[04/23] incubator-brooklyn git commit: delete catalog items from persistence store on reset

Posted by he...@apache.org.
delete catalog items from persistence store on reset


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

Branch: refs/heads/master
Commit: 48ce0df851b34e0f313178cb6c4a9dc582e88f13
Parents: ddbb43c
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed May 6 17:49:56 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:22:22 2015 +0100

----------------------------------------------------------------------
 .../mementos/BrooklynMementoPersister.java       |  7 +++++--
 .../mementos/BrooklynMementoRawData.java         |  6 ++++++
 .../brooklyn/entity/rebind/RebindIteration.java  |  9 ++++++++-
 .../AbstractBrooklynMementoPersister.java        | 12 +++++++++++-
 .../BrooklynMementoPersisterToMultiFile.java     |  6 +++++-
 .../BrooklynMementoPersisterToObjectStore.java   | 19 ++++++++++++++++++-
 6 files changed, 53 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/48ce0df8/api/src/main/java/brooklyn/mementos/BrooklynMementoPersister.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/mementos/BrooklynMementoPersister.java b/api/src/main/java/brooklyn/mementos/BrooklynMementoPersister.java
index a9eb65f..33f1de7 100644
--- a/api/src/main/java/brooklyn/mementos/BrooklynMementoPersister.java
+++ b/api/src/main/java/brooklyn/mementos/BrooklynMementoPersister.java
@@ -97,10 +97,13 @@ public interface BrooklynMementoPersister {
     /** @deprecated since 0.7.0, use {@link #checkpoint(BrooklynMementoRawData, PersistenceExceptionHandler)} 
      * and javadoc on implementations of that */ @Deprecated  // pretty sure this is not used outwith deprecated code
     void checkpoint(BrooklynMemento memento, PersistenceExceptionHandler exceptionHandler);
-    
-    void checkpoint(BrooklynMementoRawData newMemento, PersistenceExceptionHandler exceptionHandler);
 
+    /** applies a full checkpoint (write) of all state */  
+    void checkpoint(BrooklynMementoRawData newMemento, PersistenceExceptionHandler exceptionHandler);
+    /** applies a partial write of state delta */  
     void delta(Delta delta, PersistenceExceptionHandler exceptionHandler);
+    /** inserts an additional delta to be written on the next delta request */
+    void queueDelta(Delta delta);
 
     void enableWriteAccess();
     void disableWriteAccess(boolean graceful);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/48ce0df8/api/src/main/java/brooklyn/mementos/BrooklynMementoRawData.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/mementos/BrooklynMementoRawData.java b/api/src/main/java/brooklyn/mementos/BrooklynMementoRawData.java
index 32c7232..049b0c2 100644
--- a/api/src/main/java/brooklyn/mementos/BrooklynMementoRawData.java
+++ b/api/src/main/java/brooklyn/mementos/BrooklynMementoRawData.java
@@ -160,6 +160,12 @@ public class BrooklynMementoRawData {
         return Collections.unmodifiableMap(catalogItems);
     }
     
+    // to handle reset catalog
+    @Beta
+    public void clearCatalogItems() {
+        catalogItems.clear();
+    }
+    
     public boolean isEmpty() {
         return entities.isEmpty() && locations.isEmpty() && policies.isEmpty() && enrichers.isEmpty() && feeds.isEmpty() && catalogItems.isEmpty();
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/48ce0df8/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
index 3355851..136cb5b 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
@@ -363,7 +363,14 @@ public abstract class RebindIteration {
                 logRebindingDebug(message);
 
                 itemsForResettingCatalog = MutableList.<CatalogItem<?,?>>of();
-                // TODO destroy persisted items
+                
+                PersisterDeltaImpl delta = new PersisterDeltaImpl();
+                for (String catalogItemId: mementoRawData.getCatalogItems().keySet()) {
+                    delta.removedCatalogItemIds.add(catalogItemId);
+                }
+                getPersister().queueDelta(delta);
+                
+                mementoRawData.clearCatalogItems();
                 needsInitialCatalog = true;
             } else {
                 if (!isEmpty) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/48ce0df8/core/src/main/java/brooklyn/entity/rebind/persister/AbstractBrooklynMementoPersister.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/AbstractBrooklynMementoPersister.java b/core/src/main/java/brooklyn/entity/rebind/persister/AbstractBrooklynMementoPersister.java
index d296e35..d3f85df 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/AbstractBrooklynMementoPersister.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/AbstractBrooklynMementoPersister.java
@@ -19,6 +19,10 @@
 package brooklyn.entity.rebind.persister;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import brooklyn.entity.rebind.PersistenceExceptionHandler;
 import brooklyn.entity.rebind.RebindExceptionHandler;
 import brooklyn.entity.rebind.dto.BrooklynMementoManifestImpl;
@@ -39,6 +43,8 @@ import brooklyn.mementos.PolicyMemento;
 @Deprecated
 public abstract class AbstractBrooklynMementoPersister implements BrooklynMementoPersister {
 
+    private static final Logger log = LoggerFactory.getLogger(AbstractBrooklynMementoPersister.class);
+    
     protected volatile MutableBrooklynMemento memento = new MutableBrooklynMemento();
     
     @Override
@@ -96,7 +102,6 @@ public abstract class AbstractBrooklynMementoPersister implements BrooklynMement
         throw new IllegalStateException("Not supported; use "+BrooklynMementoPersisterToObjectStore.class);
     }
     
-
     @Override
     public void delta(Delta delta, PersistenceExceptionHandler exceptionHanlder) {
         memento.removeEntities(delta.removedEntityIds());
@@ -110,6 +115,11 @@ public abstract class AbstractBrooklynMementoPersister implements BrooklynMement
         memento.updateEnricherMementos(delta.enrichers());
         memento.updateCatalogItemMementos(delta.catalogItems());
     }
+
+    @Override
+    public void queueDelta(Delta delta) {
+        log.warn("Legacy persister ignoring queued delta: "+delta);
+    }
     
     @Override
     public String getBackingStoreDescription() {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/48ce0df8/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
index d3318b3..afb3d61 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
@@ -47,7 +47,6 @@ import brooklyn.mementos.EntityMemento;
 import brooklyn.mementos.LocationMemento;
 import brooklyn.mementos.PolicyMemento;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.io.FileUtil;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
@@ -441,6 +440,11 @@ public class BrooklynMementoPersisterToMultiFile implements BrooklynMementoPersi
         }
     }
 
+    @Override
+    public void queueDelta(Delta delta) {
+        LOG.warn("Legacy persister ignoring queued delta: "+delta);
+    }
+    
     @VisibleForTesting
     public File getDir() {
         return dir;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/48ce0df8/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
index 81b2cc6..47eefdc 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
@@ -24,6 +24,7 @@ import java.io.IOException;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
@@ -110,6 +111,8 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
     private volatile boolean writesShuttingDown = false;
     private StringConfigMap brooklynProperties;
     
+    private List<Delta> queuedDeltas = new CopyOnWriteArrayList<BrooklynMementoPersister.Delta>();
+    
     /**
      * Lock used on writes (checkpoint + delta) so that {@link #waitForWritesCompleted(Duration)} can block
      * for any concurrent call to complete.
@@ -560,9 +563,18 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
     public void delta(Delta delta, PersistenceExceptionHandler exceptionHandler) {
         checkWritesAllowed();
 
+        while (!queuedDeltas.isEmpty()) {
+            Delta extraDelta = queuedDeltas.remove(0);
+            doDelta(extraDelta, exceptionHandler, false);
+        }
+
+        doDelta(delta, exceptionHandler, false);
+    }
+    
+    protected void doDelta(Delta delta, PersistenceExceptionHandler exceptionHandler, boolean previouslyQueued) {
         Stopwatch stopwatch = deltaImpl(delta, exceptionHandler);
         
-        if (LOG.isDebugEnabled()) LOG.debug("Checkpointed delta of memento in {}: "
+        if (LOG.isDebugEnabled()) LOG.debug("Checkpointed "+(previouslyQueued ? "previously queued " : "")+"delta of memento in {}: "
                 + "updated {} entities, {} locations, {} policies, {} enrichers, {} catalog items; "
                 + "removed {} entities, {} locations, {} policies, {} enrichers, {} catalog items",
                     new Object[] {Time.makeTimeStringRounded(stopwatch),
@@ -570,6 +582,11 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
                         delta.removedEntityIds().size(), delta.removedLocationIds().size(), delta.removedPolicyIds().size(), delta.removedEnricherIds().size(), delta.removedCatalogItemIds().size()});
     }
     
+    @Override
+    public void queueDelta(Delta delta) {
+        queuedDeltas.add(delta);
+    }
+    
     /**
      * Concurrent calls will queue-up (the lock is "fair", which means an "approximately arrival-order policy").
      * Current usage is with the {@link PeriodicDeltaChangeListener} so we expect only one call at a time.


[05/23] incubator-brooklyn git commit: item lister resources put in an appropriate subdir; other related tidies

Posted by he...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/underscore-min.map
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/underscore-min.map b/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/underscore-min.map
new file mode 100644
index 0000000..73c951e
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/underscore-min.map
@@ -0,0 +1 @@
+{"version":3,"file":"underscore-min.js","sources":["underscore.js"],"names":["root","this","previousUnderscore","_","ArrayProto","Array","prototype","ObjProto","Object","FuncProto","Function","push","slice","concat","toString","hasOwnProperty","nativeIsArray","isArray","nativeKeys","keys","nativeBind","bind","obj","_wrapped","exports","module","VERSION","createCallback","func","context","argCount","value","call","other","index","collection","accumulator","apply","arguments","iteratee","identity","isFunction","isObject","matches","property","each","forEach","i","length","map","collect","currentKey","results","reduceError","reduce","foldl","inject","memo","TypeError","reduceRight","foldr","find","detect","predicate","result","some","list","filter","select","reject","negate","every","all","any","contains","include","target","values","indexOf","invoke","method","args","isFunc","pluck","key","where","attrs","findWhere","max","computed","Infinity","lastComputed","min","shuffle","rand","se
 t","shuffled","random","sample","n","guard","Math","sortBy","criteria","sort","left","right","a","b","group","behavior","groupBy","has","indexBy","countBy","sortedIndex","array","low","high","mid","toArray","size","partition","pass","fail","first","head","take","initial","last","rest","tail","drop","compact","flatten","input","shallow","strict","output","isArguments","without","difference","uniq","unique","isSorted","isBoolean","seen","union","intersection","argsLength","item","j","zip","object","lastIndexOf","from","idx","range","start","stop","step","ceil","Ctor","bound","self","partial","boundArgs","position","bindAll","Error","memoize","hasher","cache","address","delay","wait","setTimeout","defer","throttle","options","timeout","previous","later","leading","now","remaining","clearTimeout","trailing","debounce","immediate","timestamp","callNow","wrap","wrapper","compose","after","times","before","once","pairs","invert","functions","methods","names","extend","source","prop","pick"
 ,"omit","String","defaults","clone","tap","interceptor","eq","aStack","bStack","className","aCtor","constructor","bCtor","pop","isEqual","isEmpty","isString","isElement","nodeType","type","name","isFinite","isNaN","parseFloat","isNumber","isNull","isUndefined","noConflict","constant","noop","pair","accum","floor","Date","getTime","escapeMap","&","<",">","\"","'","`","unescapeMap","createEscaper","escaper","match","join","testRegexp","RegExp","replaceRegexp","string","test","replace","escape","unescape","idCounter","uniqueId","prefix","id","templateSettings","evaluate","interpolate","noMatch","escapes","\\","\r","\n","
","
","escapeChar","template","text","settings","oldSettings","matcher","offset","variable","render","e","data","argument","chain","instance","_chain","mixin","define","amd"],"mappings":"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"}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn/item-lister/templates/enricher.html
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/item-lister/templates/enricher.html b/usage/cli/src/main/resources/brooklyn/item-lister/templates/enricher.html
new file mode 100644
index 0000000..0d4558a
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/item-lister/templates/enricher.html
@@ -0,0 +1,59 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+  <head>
+    <title>Brooklyn Enricher - ${name}</title>
+    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+    <link rel="stylesheet" href="../items.css" type="text/css" media="screen"/>
+  </head>
+  <body>
+    <div id="container">
+      <div id="header">
+        <div id="identity">
+          <a href="https://brooklyn.incubator.apache.org/" rel="home">Brooklyn</a>
+        </div>
+      </div>
+
+      <div id="summary"></div>
+
+      <ul class="nav nav-tabs">
+        <li class="active"><a href="#configKeys" data-toggle="tab">Config Keys</a></li>
+      </ul>
+
+      <div class="tab-content">
+        <div id="configKeys"></div>
+      </div>
+
+    </div>
+
+    <script src="../../../style/js/underscore-min.js" type="text/javascript"></script>
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script>
+    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
+    <script src="../common.js" type="text/javascript"></script>
+    <script src="../items.js" type="text/javascript"></script>
+    <script type="text/javascript">
+        $(document).ready(function () {
+            var item = brooklyn.findItemOfType(items.enrichers, "${type}");
+            $("#summary").html(brooklyn.typeSummary(item));
+            item.config.forEach(function (element) { $("#configKeys").append(brooklyn.configKeyCard(element)); });
+        });
+    </script>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn/item-lister/templates/entity.html
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/item-lister/templates/entity.html b/usage/cli/src/main/resources/brooklyn/item-lister/templates/entity.html
new file mode 100644
index 0000000..397f857
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/item-lister/templates/entity.html
@@ -0,0 +1,66 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+  <head>
+    <title>Brooklyn Entity - ${name}</title>
+    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+    <link rel="stylesheet" href="../items.css" type="text/css" media="screen"/>
+  </head>
+  <body>
+    <div id="container">
+      <div id="header">
+        <div id="identity">
+          <a href="https://brooklyn.incubator.apache.org/" rel="home">Brooklyn</a>
+        </div>
+      </div>
+
+      <div id="summary"></div>
+
+      <ul class="nav nav-tabs">
+        <li class="active"><a href="#configKeys" data-toggle="tab">Config Keys</a></li>
+        <li><a href="#sensors" data-toggle="tab">Sensors</a></li>
+        <li><a href="#effectors" data-toggle="tab">Effectors</a></li>
+      </ul>
+
+      <div class="tab-content">
+        <div class="tab-pane active" id="configKeys"></div>
+        <div class="tab-pane" id="sensors"></div>
+        <div class="tab-pane" id="effectors"></div>
+      </div>
+    </div>
+
+    <script src="../../../style/js/underscore-min.js" type="text/javascript"></script>
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script>
+    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
+    <script src="../common.js" type="text/javascript"></script>
+    <script src="../items.js" type="text/javascript"></script>
+    <script type="text/javascript">
+        $(document).ready(function () {
+            var item = brooklyn.findItemOfType(items.entities, "${type}");
+            $("#summary").html(brooklyn.typeSummary(item));
+            item.config.forEach(function (element) { $("#configKeys").append(brooklyn.configKeyCard(element)); });
+            item.sensors.forEach(function (element) { $("#sensors").append(brooklyn.sensorCard(element)); });
+            if (item.effectors != undefined) {
+                item.effectors.forEach(function (element) { $("#effectors").append(brooklyn.effectorCard(element)); });
+            }
+        });
+    </script>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn/item-lister/templates/location.html
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/item-lister/templates/location.html b/usage/cli/src/main/resources/brooklyn/item-lister/templates/location.html
new file mode 100644
index 0000000..74eb49b
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/item-lister/templates/location.html
@@ -0,0 +1,62 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head>
+    <title>Brooklyn Location - ${type}</title>
+    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>
+    <link rel="stylesheet" href="../items.css" type="text/css" media="screen"/>
+</head>
+<body>
+<div id="container">
+    <div id="header">
+        <div id="identity">
+            <a href="https://brooklyn.incubator.apache.org/" rel="home">Brooklyn</a>
+        </div>
+    </div>
+    <div id="content" class="objectContent">
+        <h1>${type}</h1>
+        <h2 class="typeLabel">Type:</h2><span id="type"></span>
+
+        <h2>Config Keys</h2>
+        <table id="configKeys">
+            <tr>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Default Value</th>
+                <th>Description</th>
+            </tr>
+        </table>
+        <br>
+    </div>
+</div>
+<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script>
+<script src="../items.js" type="text/javascript"></script>
+<script type="text/javascript">
+    $(document).ready(function () {
+        var item = $.grep((items.locations), function (entity) {
+            return entity.type == "${type}";
+        })[0];
+        $("#type").html(item.type);
+        item.config.forEach(function (element) {
+            $("#configKeys").find("tr:last").after("<tr><td>" + element.name + "</td><td>" + element.type + "</td><td>" + element.defaultValue + "</td><td class='wordWrap'>" + element.description + "</td></tr>")
+        });
+    });
+</script>
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn/item-lister/templates/policy.html
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/item-lister/templates/policy.html b/usage/cli/src/main/resources/brooklyn/item-lister/templates/policy.html
new file mode 100644
index 0000000..481adb2
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/item-lister/templates/policy.html
@@ -0,0 +1,59 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+  <head>
+    <title>Brooklyn Policy - ${name}</title>
+    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+    <link rel="stylesheet" href="../items.css" type="text/css" media="screen"/>
+  </head>
+  <body>
+    <div id="container">
+      <div id="header">
+        <div id="identity">
+          <a href="https://brooklyn.incubator.apache.org/" rel="home">Brooklyn</a>
+        </div>
+      </div>
+
+      <div id="summary"></div>
+
+      <ul class="nav nav-tabs">
+        <li class="active"><a href="#configKeys" data-toggle="tab">Config Keys</a></li>
+      </ul>
+
+      <div class="tab-content">
+        <div id="configKeys"></div>
+      </div>
+
+    </div>
+
+    <script src="../../../style/js/underscore-min.js" type="text/javascript"></script>
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script>
+    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
+    <script src="../common.js" type="text/javascript"></script>
+    <script src="../items.js" type="text/javascript"></script>
+    <script type="text/javascript">
+        $(document).ready(function () {
+            var item = brooklyn.findItemOfType(items.policies, "${type}");
+            $("#summary").html(brooklyn.typeSummary(item));
+            item.config.forEach(function (element) { $("#configKeys").append(brooklyn.configKeyCard(element)); });
+        });
+    </script>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/common.js
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/common.js b/usage/cli/src/main/resources/common.js
deleted file mode 100644
index 8e8a45f..0000000
--- a/usage/cli/src/main/resources/common.js
+++ /dev/null
@@ -1,94 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- * 
- *   http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-var brooklyn = (function ($, _) {
-
-    return {
-        findItemOfType: function(items, type) {
-            return _.findWhere(items, { type: type });
-        },
-
-        entityCard: _.template(
-            "<a class='plain' data-type='<%= type %>' href='entities/<%= type %>.html'>" +
-            "<div class='card'>" +
-            "<span class='glyphicon glyphicon-chevron-right'/>" +
-            "<div class='name'><%=name%></div>" +
-            "<div class='type'><%=type%></div>" +
-            "<div class='description'><%=description%></div>" +
-            "</div>" +
-            "</a>"
-        ),
-        policyCard: _.template(
-            "<a class='plain' data-type='<%= type %>' href='policies/<%= type %>.html'>" +
-            "<div class='card'>" +
-            "<span class='glyphicon glyphicon-chevron-right'/>" +
-            "<div class='name'><%=name%></div>" +
-            "<div class='type'><%=type%></div>" +
-            "<div class='description'><%=description%></div>" +
-            "</div>" +
-            "</a>"
-        ),
-        enricherCard: _.template(
-            "<a class='plain' data-type='<%= type %>' href='enrichers/<%= type %>.html'>" +
-            "<div class='card'>" +
-            "<span class='glyphicon glyphicon-chevron-right'/>" +
-            "<div class='name'><%=name%></div>" +
-            "<div class='type'><%=type%></div>" +
-            "<div class='description'><%=description%></div>" +
-            "</div>" +
-            "</a>"
-        ),
-
-        typeSummary: _.template(
-            "<div class='summaryLabel'><%=name%></div>" +
-            "<div class='summaryType'><%=type%></div>" +
-            "<div class='description'><%=description%></div>"
-        ),
-
-        configKeyCard: _.template(
-            "<div class='card configKey'>" +
-            "<div class='name'><%=name%></div>" +
-            "<dl>" +
-            "<dt>description</dt><dd><%=(description||'&nbsp;')%></dd>" +
-            "<dt>value type</dt><dd class='java'><%=(type||'&nbsp;')%></dd>" +
-            "<dt>default value</dt><dd><%=(defaultValue||'&nbsp;')%></dd>" +
-            "</dl>" +
-            "</div>"
-        ),
-        sensorCard: _.template(
-            "<div class='card sensor'>" +
-            "<div class='name'><%=name%></div>" +
-            "<dl>" +
-            "<dt>description</dt><dd><%=(description||'&nbsp;')%></dd>" +
-            "<dt>value type</dt><dd class='java'><%=(type||'&nbsp;')%></dd>" +
-            "</dl>" +
-            "</div>"
-        ),
-        effectorCard: _.template(
-            "<div class='card effector'>" +
-            "<div class='name'><%=name%></div>" +
-            "<dl>" +
-            "<dt>description</dt><dd><%=(description||'&nbsp;')%></dd>" +
-            "<dt>return type</dt><dd class='java'><%=(returnType||'&nbsp;')%></dd>" +
-            "</dl>" +
-            "</div>"
-        )
-    };
-
-}(jQuery, _));

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/enricher.html
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/enricher.html b/usage/cli/src/main/resources/enricher.html
deleted file mode 100644
index 0d4558a..0000000
--- a/usage/cli/src/main/resources/enricher.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<!--
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
--->
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
-  <head>
-    <title>Brooklyn Enricher - ${name}</title>
-    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>
-    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
-    <link rel="stylesheet" href="../items.css" type="text/css" media="screen"/>
-  </head>
-  <body>
-    <div id="container">
-      <div id="header">
-        <div id="identity">
-          <a href="https://brooklyn.incubator.apache.org/" rel="home">Brooklyn</a>
-        </div>
-      </div>
-
-      <div id="summary"></div>
-
-      <ul class="nav nav-tabs">
-        <li class="active"><a href="#configKeys" data-toggle="tab">Config Keys</a></li>
-      </ul>
-
-      <div class="tab-content">
-        <div id="configKeys"></div>
-      </div>
-
-    </div>
-
-    <script src="../../../style/js/underscore-min.js" type="text/javascript"></script>
-    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script>
-    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
-    <script src="../common.js" type="text/javascript"></script>
-    <script src="../items.js" type="text/javascript"></script>
-    <script type="text/javascript">
-        $(document).ready(function () {
-            var item = brooklyn.findItemOfType(items.enrichers, "${type}");
-            $("#summary").html(brooklyn.typeSummary(item));
-            item.config.forEach(function (element) { $("#configKeys").append(brooklyn.configKeyCard(element)); });
-        });
-    </script>
-  </body>
-</html>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/entity.html
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/entity.html b/usage/cli/src/main/resources/entity.html
deleted file mode 100644
index 397f857..0000000
--- a/usage/cli/src/main/resources/entity.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<!--
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
--->
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
-  <head>
-    <title>Brooklyn Entity - ${name}</title>
-    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>
-    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
-    <link rel="stylesheet" href="../items.css" type="text/css" media="screen"/>
-  </head>
-  <body>
-    <div id="container">
-      <div id="header">
-        <div id="identity">
-          <a href="https://brooklyn.incubator.apache.org/" rel="home">Brooklyn</a>
-        </div>
-      </div>
-
-      <div id="summary"></div>
-
-      <ul class="nav nav-tabs">
-        <li class="active"><a href="#configKeys" data-toggle="tab">Config Keys</a></li>
-        <li><a href="#sensors" data-toggle="tab">Sensors</a></li>
-        <li><a href="#effectors" data-toggle="tab">Effectors</a></li>
-      </ul>
-
-      <div class="tab-content">
-        <div class="tab-pane active" id="configKeys"></div>
-        <div class="tab-pane" id="sensors"></div>
-        <div class="tab-pane" id="effectors"></div>
-      </div>
-    </div>
-
-    <script src="../../../style/js/underscore-min.js" type="text/javascript"></script>
-    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script>
-    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
-    <script src="../common.js" type="text/javascript"></script>
-    <script src="../items.js" type="text/javascript"></script>
-    <script type="text/javascript">
-        $(document).ready(function () {
-            var item = brooklyn.findItemOfType(items.entities, "${type}");
-            $("#summary").html(brooklyn.typeSummary(item));
-            item.config.forEach(function (element) { $("#configKeys").append(brooklyn.configKeyCard(element)); });
-            item.sensors.forEach(function (element) { $("#sensors").append(brooklyn.sensorCard(element)); });
-            if (item.effectors != undefined) {
-                item.effectors.forEach(function (element) { $("#effectors").append(brooklyn.effectorCard(element)); });
-            }
-        });
-    </script>
-  </body>
-</html>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/items.css
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/items.css b/usage/cli/src/main/resources/items.css
deleted file mode 100644
index a0bf0f0..0000000
--- a/usage/cli/src/main/resources/items.css
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
-*/
-/* landing page */
-body {
-  margin: 0px;
-  padding: 10px 0px 20px 0px;
-  font-family: arial, helvetica, sans-serif;
-  background-color: #ffffff;
-  color: #393939;
-  font-size: 15px;
-}
-
-.nav-tabs {
-  clear: both;
-  font-weight: bold;
-  font-size: 12pt;
-}
-.nav-tabs a {
-  color: #4d9d3a;
-}
-.nav-tabs a:hover {
-  color: #4d9d3a;
-}
-
-.tab-content {
-  padding: 20px;
-  padding-bottom: 10px;
-  border: 1px solid #ddd;
-  border-top: none;
-}
-
-a:hover > .card {
-  top: -2px;
-  background-color: #f4f4f4;
-  box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.2);
-}
-.card {
-  position: relative;
-  padding: 12px;
-  background-color: #f8f8f8;
-  color: #333;
-  border: 1px solid #E1E1E8;
-  border-radius: 6px;
-  font-size: 11pt;
-}
-.card,
-.filter {
-    margin-bottom: 10px;
-}
-a .glyphicon {
-  display: block;
-  position: absolute;
-  right: 0;
-  top: 0;
-  padding: 8px;
-  font-size: 16pt;
-  color: #aaa;
-}
-a:hover .glyphicon {
-  color: #888;
-}
-a.plain {
-  text-decoration: none !important;
-}
-.name {
-  font-size: 12pt;
-  font-weight: bold;
-}
-.type {
-  font-family: monospace;
-  color: #888;
-  margin-top: 2px;
-}
-#summary .description {
-  margin: 15px 0 25px 0;
-}
-.card .description {
-  margin: 10px 40px 0 20px;
-}
-
-#summary {
-  clear: both;
-  margin: 10px 0 20px 0;
-}
-.summaryLabel {
-  font-size: 20px;
-  font-weight: bold;
-}
-.summaryType {
-  font-family: monospace;
-  font-size: 12pt;
-  color: #888;
-}
-.java {
-  font-family: monospace;
-}
-
-.card dl {
-  margin-bottom: 0;
-  margin-top: 5px;
-}
-dt {
-  clear: both;
-  float: left;
-  width: 8em;
-  text-align: right;
-  font-weight: normal;
-}
-dd {
-  margin-left: 9em;
-}
-
-#container {
-  width: 980px;
-  padding: 0;
-  margin: 0 auto;
-}
-
-#identity {
-  float: left;
-  margin: 0;
-  padding: 30px 0 15px 10px;
-}
-
-#identity a {
-  text-decoration: none;
-  display: block;
-  margin: 0;
-  color: #4d9d3a;
-  font-size: 2.5em;
-  padding: 0;
-  background: transparent url(images/brooklyn.gif) no-repeat 0 0;
-  width: 206px;
-  height: 44px;
-  text-indent: -1000px;
-  overflow: hidden;
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/libs/js/bloodhound.js
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/libs/js/bloodhound.js b/usage/cli/src/main/resources/libs/js/bloodhound.js
deleted file mode 100644
index 96a4c43..0000000
--- a/usage/cli/src/main/resources/libs/js/bloodhound.js
+++ /dev/null
@@ -1,727 +0,0 @@
-/*!
- * typeahead.js 0.10.5
- * https://github.com/twitter/typeahead.js
- * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT
- */
-
-(function($) {
-    var _ = function() {
-        "use strict";
-        return {
-            isMsie: function() {
-                return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
-            },
-            isBlankString: function(str) {
-                return !str || /^\s*$/.test(str);
-            },
-            escapeRegExChars: function(str) {
-                return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
-            },
-            isString: function(obj) {
-                return typeof obj === "string";
-            },
-            isNumber: function(obj) {
-                return typeof obj === "number";
-            },
-            isArray: $.isArray,
-            isFunction: $.isFunction,
-            isObject: $.isPlainObject,
-            isUndefined: function(obj) {
-                return typeof obj === "undefined";
-            },
-            toStr: function toStr(s) {
-                return _.isUndefined(s) || s === null ? "" : s + "";
-            },
-            bind: $.proxy,
-            each: function(collection, cb) {
-                $.each(collection, reverseArgs);
-                function reverseArgs(index, value) {
-                    return cb(value, index);
-                }
-            },
-            map: $.map,
-            filter: $.grep,
-            every: function(obj, test) {
-                var result = true;
-                if (!obj) {
-                    return result;
-                }
-                $.each(obj, function(key, val) {
-                    if (!(result = test.call(null, val, key, obj))) {
-                        return false;
-                    }
-                });
-                return !!result;
-            },
-            some: function(obj, test) {
-                var result = false;
-                if (!obj) {
-                    return result;
-                }
-                $.each(obj, function(key, val) {
-                    if (result = test.call(null, val, key, obj)) {
-                        return false;
-                    }
-                });
-                return !!result;
-            },
-            mixin: $.extend,
-            getUniqueId: function() {
-                var counter = 0;
-                return function() {
-                    return counter++;
-                };
-            }(),
-            templatify: function templatify(obj) {
-                return $.isFunction(obj) ? obj : template;
-                function template() {
-                    return String(obj);
-                }
-            },
-            defer: function(fn) {
-                setTimeout(fn, 0);
-            },
-            debounce: function(func, wait, immediate) {
-                var timeout, result;
-                return function() {
-                    var context = this, args = arguments, later, callNow;
-                    later = function() {
-                        timeout = null;
-                        if (!immediate) {
-                            result = func.apply(context, args);
-                        }
-                    };
-                    callNow = immediate && !timeout;
-                    clearTimeout(timeout);
-                    timeout = setTimeout(later, wait);
-                    if (callNow) {
-                        result = func.apply(context, args);
-                    }
-                    return result;
-                };
-            },
-            throttle: function(func, wait) {
-                var context, args, timeout, result, previous, later;
-                previous = 0;
-                later = function() {
-                    previous = new Date();
-                    timeout = null;
-                    result = func.apply(context, args);
-                };
-                return function() {
-                    var now = new Date(), remaining = wait - (now - previous);
-                    context = this;
-                    args = arguments;
-                    if (remaining <= 0) {
-                        clearTimeout(timeout);
-                        timeout = null;
-                        previous = now;
-                        result = func.apply(context, args);
-                    } else if (!timeout) {
-                        timeout = setTimeout(later, remaining);
-                    }
-                    return result;
-                };
-            },
-            noop: function() {}
-        };
-    }();
-    var VERSION = "0.10.5";
-    var tokenizers = function() {
-        "use strict";
-        return {
-            nonword: nonword,
-            whitespace: whitespace,
-            obj: {
-                nonword: getObjTokenizer(nonword),
-                whitespace: getObjTokenizer(whitespace)
-            }
-        };
-        function whitespace(str) {
-            str = _.toStr(str);
-            return str ? str.split(/\s+/) : [];
-        }
-        function nonword(str) {
-            str = _.toStr(str);
-            return str ? str.split(/\W+/) : [];
-        }
-        function getObjTokenizer(tokenizer) {
-            return function setKey() {
-                var args = [].slice.call(arguments, 0);
-                return function tokenize(o) {
-                    var tokens = [];
-                    _.each(args, function(k) {
-                        tokens = tokens.concat(tokenizer(_.toStr(o[k])));
-                    });
-                    return tokens;
-                };
-            };
-        }
-    }();
-    var LruCache = function() {
-        "use strict";
-        function LruCache(maxSize) {
-            this.maxSize = _.isNumber(maxSize) ? maxSize : 100;
-            this.reset();
-            if (this.maxSize <= 0) {
-                this.set = this.get = $.noop;
-            }
-        }
-        _.mixin(LruCache.prototype, {
-            set: function set(key, val) {
-                var tailItem = this.list.tail, node;
-                if (this.size >= this.maxSize) {
-                    this.list.remove(tailItem);
-                    delete this.hash[tailItem.key];
-                }
-                if (node = this.hash[key]) {
-                    node.val = val;
-                    this.list.moveToFront(node);
-                } else {
-                    node = new Node(key, val);
-                    this.list.add(node);
-                    this.hash[key] = node;
-                    this.size++;
-                }
-            },
-            get: function get(key) {
-                var node = this.hash[key];
-                if (node) {
-                    this.list.moveToFront(node);
-                    return node.val;
-                }
-            },
-            reset: function reset() {
-                this.size = 0;
-                this.hash = {};
-                this.list = new List();
-            }
-        });
-        function List() {
-            this.head = this.tail = null;
-        }
-        _.mixin(List.prototype, {
-            add: function add(node) {
-                if (this.head) {
-                    node.next = this.head;
-                    this.head.prev = node;
-                }
-                this.head = node;
-                this.tail = this.tail || node;
-            },
-            remove: function remove(node) {
-                node.prev ? node.prev.next = node.next : this.head = node.next;
-                node.next ? node.next.prev = node.prev : this.tail = node.prev;
-            },
-            moveToFront: function(node) {
-                this.remove(node);
-                this.add(node);
-            }
-        });
-        function Node(key, val) {
-            this.key = key;
-            this.val = val;
-            this.prev = this.next = null;
-        }
-        return LruCache;
-    }();
-    var PersistentStorage = function() {
-        "use strict";
-        var ls, methods;
-        try {
-            ls = window.localStorage;
-            ls.setItem("~~~", "!");
-            ls.removeItem("~~~");
-        } catch (err) {
-            ls = null;
-        }
-        function PersistentStorage(namespace) {
-            this.prefix = [ "__", namespace, "__" ].join("");
-            this.ttlKey = "__ttl__";
-            this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix));
-        }
-        if (ls && window.JSON) {
-            methods = {
-                _prefix: function(key) {
-                    return this.prefix + key;
-                },
-                _ttlKey: function(key) {
-                    return this._prefix(key) + this.ttlKey;
-                },
-                get: function(key) {
-                    if (this.isExpired(key)) {
-                        this.remove(key);
-                    }
-                    return decode(ls.getItem(this._prefix(key)));
-                },
-                set: function(key, val, ttl) {
-                    if (_.isNumber(ttl)) {
-                        ls.setItem(this._ttlKey(key), encode(now() + ttl));
-                    } else {
-                        ls.removeItem(this._ttlKey(key));
-                    }
-                    return ls.setItem(this._prefix(key), encode(val));
-                },
-                remove: function(key) {
-                    ls.removeItem(this._ttlKey(key));
-                    ls.removeItem(this._prefix(key));
-                    return this;
-                },
-                clear: function() {
-                    var i, key, keys = [], len = ls.length;
-                    for (i = 0; i < len; i++) {
-                        if ((key = ls.key(i)).match(this.keyMatcher)) {
-                            keys.push(key.replace(this.keyMatcher, ""));
-                        }
-                    }
-                    for (i = keys.length; i--; ) {
-                        this.remove(keys[i]);
-                    }
-                    return this;
-                },
-                isExpired: function(key) {
-                    var ttl = decode(ls.getItem(this._ttlKey(key)));
-                    return _.isNumber(ttl) && now() > ttl ? true : false;
-                }
-            };
-        } else {
-            methods = {
-                get: _.noop,
-                set: _.noop,
-                remove: _.noop,
-                clear: _.noop,
-                isExpired: _.noop
-            };
-        }
-        _.mixin(PersistentStorage.prototype, methods);
-        return PersistentStorage;
-        function now() {
-            return new Date().getTime();
-        }
-        function encode(val) {
-            return JSON.stringify(_.isUndefined(val) ? null : val);
-        }
-        function decode(val) {
-            return JSON.parse(val);
-        }
-    }();
-    var Transport = function() {
-        "use strict";
-        var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10);
-        function Transport(o) {
-            o = o || {};
-            this.cancelled = false;
-            this.lastUrl = null;
-            this._send = o.transport ? callbackToDeferred(o.transport) : $.ajax;
-            this._get = o.rateLimiter ? o.rateLimiter(this._get) : this._get;
-            this._cache = o.cache === false ? new LruCache(0) : sharedCache;
-        }
-        Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
-            maxPendingRequests = num;
-        };
-        Transport.resetCache = function resetCache() {
-            sharedCache.reset();
-        };
-        _.mixin(Transport.prototype, {
-            _get: function(url, o, cb) {
-                var that = this, jqXhr;
-                if (this.cancelled || url !== this.lastUrl) {
-                    return;
-                }
-                if (jqXhr = pendingRequests[url]) {
-                    jqXhr.done(done).fail(fail);
-                } else if (pendingRequestsCount < maxPendingRequests) {
-                    pendingRequestsCount++;
-                    pendingRequests[url] = this._send(url, o).done(done).fail(fail).always(always);
-                } else {
-                    this.onDeckRequestArgs = [].slice.call(arguments, 0);
-                }
-                function done(resp) {
-                    cb && cb(null, resp);
-                    that._cache.set(url, resp);
-                }
-                function fail() {
-                    cb && cb(true);
-                }
-                function always() {
-                    pendingRequestsCount--;
-                    delete pendingRequests[url];
-                    if (that.onDeckRequestArgs) {
-                        that._get.apply(that, that.onDeckRequestArgs);
-                        that.onDeckRequestArgs = null;
-                    }
-                }
-            },
-            get: function(url, o, cb) {
-                var resp;
-                if (_.isFunction(o)) {
-                    cb = o;
-                    o = {};
-                }
-                this.cancelled = false;
-                this.lastUrl = url;
-                if (resp = this._cache.get(url)) {
-                    _.defer(function() {
-                        cb && cb(null, resp);
-                    });
-                } else {
-                    this._get(url, o, cb);
-                }
-                return !!resp;
-            },
-            cancel: function() {
-                this.cancelled = true;
-            }
-        });
-        return Transport;
-        function callbackToDeferred(fn) {
-            return function customSendWrapper(url, o) {
-                var deferred = $.Deferred();
-                fn(url, o, onSuccess, onError);
-                return deferred;
-                function onSuccess(resp) {
-                    _.defer(function() {
-                        deferred.resolve(resp);
-                    });
-                }
-                function onError(err) {
-                    _.defer(function() {
-                        deferred.reject(err);
-                    });
-                }
-            };
-        }
-    }();
-    var SearchIndex = function() {
-        "use strict";
-        function SearchIndex(o) {
-            o = o || {};
-            if (!o.datumTokenizer || !o.queryTokenizer) {
-                $.error("datumTokenizer and queryTokenizer are both required");
-            }
-            this.datumTokenizer = o.datumTokenizer;
-            this.queryTokenizer = o.queryTokenizer;
-            this.reset();
-        }
-        _.mixin(SearchIndex.prototype, {
-            bootstrap: function bootstrap(o) {
-                this.datums = o.datums;
-                this.trie = o.trie;
-            },
-            add: function(data) {
-                var that = this;
-                data = _.isArray(data) ? data : [ data ];
-                _.each(data, function(datum) {
-                    var id, tokens;
-                    id = that.datums.push(datum) - 1;
-                    tokens = normalizeTokens(that.datumTokenizer(datum));
-                    _.each(tokens, function(token) {
-                        var node, chars, ch;
-                        node = that.trie;
-                        chars = token.split("");
-                        while (ch = chars.shift()) {
-                            node = node.children[ch] || (node.children[ch] = newNode());
-                            node.ids.push(id);
-                        }
-                    });
-                });
-            },
-            get: function get(query) {
-                var that = this, tokens, matches;
-                tokens = normalizeTokens(this.queryTokenizer(query));
-                _.each(tokens, function(token) {
-                    var node, chars, ch, ids;
-                    if (matches && matches.length === 0) {
-                        return false;
-                    }
-                    node = that.trie;
-                    chars = token.split("");
-                    while (node && (ch = chars.shift())) {
-                        node = node.children[ch];
-                    }
-                    if (node && chars.length === 0) {
-                        ids = node.ids.slice(0);
-                        matches = matches ? getIntersection(matches, ids) : ids;
-                    } else {
-                        matches = [];
-                        return false;
-                    }
-                });
-                return matches ? _.map(unique(matches), function(id) {
-                    return that.datums[id];
-                }) : [];
-            },
-            reset: function reset() {
-                this.datums = [];
-                this.trie = newNode();
-            },
-            serialize: function serialize() {
-                return {
-                    datums: this.datums,
-                    trie: this.trie
-                };
-            }
-        });
-        return SearchIndex;
-        function normalizeTokens(tokens) {
-            tokens = _.filter(tokens, function(token) {
-                return !!token;
-            });
-            tokens = _.map(tokens, function(token) {
-                return token.toLowerCase();
-            });
-            return tokens;
-        }
-        function newNode() {
-            return {
-                ids: [],
-                children: {}
-            };
-        }
-        function unique(array) {
-            var seen = {}, uniques = [];
-            for (var i = 0, len = array.length; i < len; i++) {
-                if (!seen[array[i]]) {
-                    seen[array[i]] = true;
-                    uniques.push(array[i]);
-                }
-            }
-            return uniques;
-        }
-        function getIntersection(arrayA, arrayB) {
-            var ai = 0, bi = 0, intersection = [];
-            arrayA = arrayA.sort(compare);
-            arrayB = arrayB.sort(compare);
-            var lenArrayA = arrayA.length, lenArrayB = arrayB.length;
-            while (ai < lenArrayA && bi < lenArrayB) {
-                if (arrayA[ai] < arrayB[bi]) {
-                    ai++;
-                } else if (arrayA[ai] > arrayB[bi]) {
-                    bi++;
-                } else {
-                    intersection.push(arrayA[ai]);
-                    ai++;
-                    bi++;
-                }
-            }
-            return intersection;
-            function compare(a, b) {
-                return a - b;
-            }
-        }
-    }();
-    var oParser = function() {
-        "use strict";
-        return {
-            local: getLocal,
-            prefetch: getPrefetch,
-            remote: getRemote
-        };
-        function getLocal(o) {
-            return o.local || null;
-        }
-        function getPrefetch(o) {
-            var prefetch, defaults;
-            defaults = {
-                url: null,
-                thumbprint: "",
-                ttl: 24 * 60 * 60 * 1e3,
-                filter: null,
-                ajax: {}
-            };
-            if (prefetch = o.prefetch || null) {
-                prefetch = _.isString(prefetch) ? {
-                    url: prefetch
-                } : prefetch;
-                prefetch = _.mixin(defaults, prefetch);
-                prefetch.thumbprint = VERSION + prefetch.thumbprint;
-                prefetch.ajax.type = prefetch.ajax.type || "GET";
-                prefetch.ajax.dataType = prefetch.ajax.dataType || "json";
-                !prefetch.url && $.error("prefetch requires url to be set");
-            }
-            return prefetch;
-        }
-        function getRemote(o) {
-            var remote, defaults;
-            defaults = {
-                url: null,
-                cache: true,
-                wildcard: "%QUERY",
-                replace: null,
-                rateLimitBy: "debounce",
-                rateLimitWait: 300,
-                send: null,
-                filter: null,
-                ajax: {}
-            };
-            if (remote = o.remote || null) {
-                remote = _.isString(remote) ? {
-                    url: remote
-                } : remote;
-                remote = _.mixin(defaults, remote);
-                remote.rateLimiter = /^throttle$/i.test(remote.rateLimitBy) ? byThrottle(remote.rateLimitWait) : byDebounce(remote.rateLimitWait);
-                remote.ajax.type = remote.ajax.type || "GET";
-                remote.ajax.dataType = remote.ajax.dataType || "json";
-                delete remote.rateLimitBy;
-                delete remote.rateLimitWait;
-                !remote.url && $.error("remote requires url to be set");
-            }
-            return remote;
-            function byDebounce(wait) {
-                return function(fn) {
-                    return _.debounce(fn, wait);
-                };
-            }
-            function byThrottle(wait) {
-                return function(fn) {
-                    return _.throttle(fn, wait);
-                };
-            }
-        }
-    }();
-    (function(root) {
-        "use strict";
-        var old, keys;
-        old = root.Bloodhound;
-        keys = {
-            data: "data",
-            protocol: "protocol",
-            thumbprint: "thumbprint"
-        };
-        root.Bloodhound = Bloodhound;
-        function Bloodhound(o) {
-            if (!o || !o.local && !o.prefetch && !o.remote) {
-                $.error("one of local, prefetch, or remote is required");
-            }
-            this.limit = o.limit || 5;
-            this.sorter = getSorter(o.sorter);
-            this.dupDetector = o.dupDetector || ignoreDuplicates;
-            this.local = oParser.local(o);
-            this.prefetch = oParser.prefetch(o);
-            this.remote = oParser.remote(o);
-            this.cacheKey = this.prefetch ? this.prefetch.cacheKey || this.prefetch.url : null;
-            this.index = new SearchIndex({
-                datumTokenizer: o.datumTokenizer,
-                queryTokenizer: o.queryTokenizer
-            });
-            this.storage = this.cacheKey ? new PersistentStorage(this.cacheKey) : null;
-        }
-        Bloodhound.noConflict = function noConflict() {
-            root.Bloodhound = old;
-            return Bloodhound;
-        };
-        Bloodhound.tokenizers = tokenizers;
-        _.mixin(Bloodhound.prototype, {
-            _loadPrefetch: function loadPrefetch(o) {
-                var that = this, serialized, deferred;
-                if (serialized = this._readFromStorage(o.thumbprint)) {
-                    this.index.bootstrap(serialized);
-                    deferred = $.Deferred().resolve();
-                } else {
-                    deferred = $.ajax(o.url, o.ajax).done(handlePrefetchResponse);
-                }
-                return deferred;
-                function handlePrefetchResponse(resp) {
-                    that.clear();
-                    that.add(o.filter ? o.filter(resp) : resp);
-                    that._saveToStorage(that.index.serialize(), o.thumbprint, o.ttl);
-                }
-            },
-            _getFromRemote: function getFromRemote(query, cb) {
-                var that = this, url, uriEncodedQuery;
-                if (!this.transport) {
-                    return;
-                }
-                query = query || "";
-                uriEncodedQuery = encodeURIComponent(query);
-                url = this.remote.replace ? this.remote.replace(this.remote.url, query) : this.remote.url.replace(this.remote.wildcard, uriEncodedQuery);
-                return this.transport.get(url, this.remote.ajax, handleRemoteResponse);
-                function handleRemoteResponse(err, resp) {
-                    err ? cb([]) : cb(that.remote.filter ? that.remote.filter(resp) : resp);
-                }
-            },
-            _cancelLastRemoteRequest: function cancelLastRemoteRequest() {
-                this.transport && this.transport.cancel();
-            },
-            _saveToStorage: function saveToStorage(data, thumbprint, ttl) {
-                if (this.storage) {
-                    this.storage.set(keys.data, data, ttl);
-                    this.storage.set(keys.protocol, location.protocol, ttl);
-                    this.storage.set(keys.thumbprint, thumbprint, ttl);
-                }
-            },
-            _readFromStorage: function readFromStorage(thumbprint) {
-                var stored = {}, isExpired;
-                if (this.storage) {
-                    stored.data = this.storage.get(keys.data);
-                    stored.protocol = this.storage.get(keys.protocol);
-                    stored.thumbprint = this.storage.get(keys.thumbprint);
-                }
-                isExpired = stored.thumbprint !== thumbprint || stored.protocol !== location.protocol;
-                return stored.data && !isExpired ? stored.data : null;
-            },
-            _initialize: function initialize() {
-                var that = this, local = this.local, deferred;
-                deferred = this.prefetch ? this._loadPrefetch(this.prefetch) : $.Deferred().resolve();
-                local && deferred.done(addLocalToIndex);
-                this.transport = this.remote ? new Transport(this.remote) : null;
-                return this.initPromise = deferred.promise();
-                function addLocalToIndex() {
-                    that.add(_.isFunction(local) ? local() : local);
-                }
-            },
-            initialize: function initialize(force) {
-                return !this.initPromise || force ? this._initialize() : this.initPromise;
-            },
-            add: function add(data) {
-                this.index.add(data);
-            },
-            get: function get(query, cb) {
-                var that = this, matches = [], cacheHit = false;
-                matches = this.index.get(query);
-                matches = this.sorter(matches).slice(0, this.limit);
-                matches.length < this.limit ? cacheHit = this._getFromRemote(query, returnRemoteMatches) : this._cancelLastRemoteRequest();
-                if (!cacheHit) {
-                    (matches.length > 0 || !this.transport) && cb && cb(matches);
-                }
-                function returnRemoteMatches(remoteMatches) {
-                    var matchesWithBackfill = matches.slice(0);
-                    _.each(remoteMatches, function(remoteMatch) {
-                        var isDuplicate;
-                        isDuplicate = _.some(matchesWithBackfill, function(match) {
-                            return that.dupDetector(remoteMatch, match);
-                        });
-                        !isDuplicate && matchesWithBackfill.push(remoteMatch);
-                        return matchesWithBackfill.length < that.limit;
-                    });
-                    cb && cb(that.sorter(matchesWithBackfill));
-                }
-            },
-            clear: function clear() {
-                this.index.reset();
-            },
-            clearPrefetchCache: function clearPrefetchCache() {
-                this.storage && this.storage.clear();
-            },
-            clearRemoteCache: function clearRemoteCache() {
-                this.transport && Transport.resetCache();
-            },
-            ttAdapter: function ttAdapter() {
-                return _.bind(this.get, this);
-            }
-        });
-        return Bloodhound;
-        function getSorter(sortFn) {
-            return _.isFunction(sortFn) ? sort : noSort;
-            function sort(array) {
-                return array.sort(sortFn);
-            }
-            function noSort(array) {
-                return array;
-            }
-        }
-        function ignoreDuplicates() {
-            return false;
-        }
-    })(this);
-})(window.jQuery);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/location.html
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/location.html b/usage/cli/src/main/resources/location.html
deleted file mode 100644
index 74eb49b..0000000
--- a/usage/cli/src/main/resources/location.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<!--
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
--->
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
-<head>
-    <title>Brooklyn Location - ${type}</title>
-    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>
-    <link rel="stylesheet" href="../items.css" type="text/css" media="screen"/>
-</head>
-<body>
-<div id="container">
-    <div id="header">
-        <div id="identity">
-            <a href="https://brooklyn.incubator.apache.org/" rel="home">Brooklyn</a>
-        </div>
-    </div>
-    <div id="content" class="objectContent">
-        <h1>${type}</h1>
-        <h2 class="typeLabel">Type:</h2><span id="type"></span>
-
-        <h2>Config Keys</h2>
-        <table id="configKeys">
-            <tr>
-                <th>Name</th>
-                <th>Type</th>
-                <th>Default Value</th>
-                <th>Description</th>
-            </tr>
-        </table>
-        <br>
-    </div>
-</div>
-<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script>
-<script src="../items.js" type="text/javascript"></script>
-<script type="text/javascript">
-    $(document).ready(function () {
-        var item = $.grep((items.locations), function (entity) {
-            return entity.type == "${type}";
-        })[0];
-        $("#type").html(item.type);
-        item.config.forEach(function (element) {
-            $("#configKeys").find("tr:last").after("<tr><td>" + element.name + "</td><td>" + element.type + "</td><td>" + element.defaultValue + "</td><td class='wordWrap'>" + element.description + "</td></tr>")
-        });
-    });
-</script>
-</body>
-</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/policy.html
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/policy.html b/usage/cli/src/main/resources/policy.html
deleted file mode 100644
index 481adb2..0000000
--- a/usage/cli/src/main/resources/policy.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<!--
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
--->
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
-  <head>
-    <title>Brooklyn Policy - ${name}</title>
-    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>
-    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
-    <link rel="stylesheet" href="../items.css" type="text/css" media="screen"/>
-  </head>
-  <body>
-    <div id="container">
-      <div id="header">
-        <div id="identity">
-          <a href="https://brooklyn.incubator.apache.org/" rel="home">Brooklyn</a>
-        </div>
-      </div>
-
-      <div id="summary"></div>
-
-      <ul class="nav nav-tabs">
-        <li class="active"><a href="#configKeys" data-toggle="tab">Config Keys</a></li>
-      </ul>
-
-      <div class="tab-content">
-        <div id="configKeys"></div>
-      </div>
-
-    </div>
-
-    <script src="../../../style/js/underscore-min.js" type="text/javascript"></script>
-    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script>
-    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
-    <script src="../common.js" type="text/javascript"></script>
-    <script src="../items.js" type="text/javascript"></script>
-    <script type="text/javascript">
-        $(document).ready(function () {
-            var item = brooklyn.findItemOfType(items.policies, "${type}");
-            $("#summary").html(brooklyn.typeSummary(item));
-            item.config.forEach(function (element) { $("#configKeys").append(brooklyn.configKeyCard(element)); });
-        });
-    </script>
-  </body>
-</html>


[11/23] incubator-brooklyn git commit: install a default.catalog.bom, and update docs

Posted by he...@apache.org.
install a default.catalog.bom, and update docs

and tweak poms to exclude license since these are config files the user is meant to edit;
remove the old catalog.xml, including mentions in the docs


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

Branch: refs/heads/master
Commit: 49a6ef7b7ba0e75b23c33cde434ee5e7c91bba14
Parents: fab1caf
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed May 6 15:00:39 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:22:23 2015 +0100

----------------------------------------------------------------------
 docs/guide/ops/catalog/index.md                 | 15 +++++++
 docs/guide/ops/install-on-server.md             | 23 ++++-------
 docs/guide/start/blueprints.md                  | 13 +++----
 docs/guide/start/catalog.xml                    | 22 -----------
 pom.xml                                         |  5 +--
 .../entity/brooklynnode/BrooklynNode.java       |  6 +++
 .../main/resources/brooklyn/default.catalog.bom |  4 ++
 usage/dist/pom.xml                              | 10 +++++
 .../main/dist/conf/brooklyn/default.catalog.bom | 41 ++++++++++++++++++++
 usage/dist/src/main/dist/conf/catalog.xml       | 37 ------------------
 usage/dist/src/main/dist/conf/logback.xml       | 18 ---------
 11 files changed, 91 insertions(+), 103 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/49a6ef7b/docs/guide/ops/catalog/index.md
----------------------------------------------------------------------
diff --git a/docs/guide/ops/catalog/index.md b/docs/guide/ops/catalog/index.md
index dcedcd8..7cb0e84 100644
--- a/docs/guide/ops/catalog/index.md
+++ b/docs/guide/ops/catalog/index.md
@@ -8,6 +8,8 @@ children:
 - { section: Adding to the Catalog, title: Adding and Deleting } 
 - { section: Templates and the Add-Application Wizard, title: Templates }
 - { section: Versioning } 
+- { section: CLI Options }
+ 
 ---
 
 Brooklyn provides a **catalog**, which is a persisted collection of versioned blueprints and other resources. 
@@ -289,6 +291,19 @@ When referencing a blueprint, if a version number is not specified
 the latest non-snapshot version will be loaded when an entity is instantiated.
 
 
+### CLI Options
+
+The `brooklyn` CLI includes several commands for working with the catalog.
+
+* `--catalogAdd <file.bom>` will add the catalog items in the `bom` file
+* `--catalogReset` will reset the catalog to the initial state 
+  (based on `brooklyn/default.catalog.bom` on the classpath, by default in a dist in the `conf/` directory)
+* `--catalogInitial <file.bom>` will set the catalog items to use on first run,
+  on a catalog reset, or if persistence is off
+
+If [persistence](../persistence/) is enabled, catalog additions will remain between runs.
+For more information on these commands, run `brooklyn help launch`.
+
 
 <!--
 TODO: make test cases from the code snippets here, and when building the docs assert that they match test cases

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/49a6ef7b/docs/guide/ops/install-on-server.md
----------------------------------------------------------------------
diff --git a/docs/guide/ops/install-on-server.md b/docs/guide/ops/install-on-server.md
index d4711db..6c3455f 100644
--- a/docs/guide/ops/install-on-server.md
+++ b/docs/guide/ops/install-on-server.md
@@ -11,7 +11,7 @@ Here we present two *alternatives* to install Brooklyn:
 - [Manual installation](#manual)
 
 
-## <a id="script"></a> Running the installation script
+## <a id="script"></a> Running the Installation Script
 
 There is a simple bash script available to help with the installation process. 
 
@@ -32,16 +32,16 @@ $ ./brooklyn-install.sh -s -r <your-server-ip>
 {% endhighlight %}
 
 
-## <a id="manual"></a> Manual installation
+## <a id="manual"></a> Manual Installation
 
 1. [Set up the prerequisites](#prerequisites)
 1. [Download Brooklyn](#download)
 1. [Configuring brooklyn.properties](#configuring-properties)
-1. [Configuring catalog.xml](#configuring-catalog)
+1. [Configuring default.catalog.bom](#configuring-catalog)
 1. [Test the installation](#confirm)
 
 
-### <a id="prerequisites"></a>Set up the prerequisites
+### <a id="prerequisites"></a>Set up the Prerequisites
 
 Before installing Apache Brooklyn, it is recommented to configure the host as follows. 
 
@@ -105,21 +105,14 @@ $ chmod 600 ~/.brooklyn/brooklyn.properties
 You may need to edit `~/.brooklyn/brooklyn.properties` to ensure that brooklyn can access cloud locations for application deployment.
 
 
-### <a id="configuring-catalog"></a>Configuring catalog.xml
+### <a id="configuring-catalog"></a>Configuring the Catalog
 
 By default Brooklyn loads the catalog of available application components and services from 
-`~/.brooklyn/catalog.xml`. 
-
-{% highlight bash %}
-$ wget -O ~/.brooklyn/catalog.xml {{site.url_root}}{{site.path.website}}/quickstart/catalog.xml
-{% endhighlight %}
-
-The `catalog.xml` is the application blueprint catalog. The above example file contains some blueprints which will be automatically downloaded from the web if you run them.
-
-You may need to edit `~/.brooklyn/catalog.xml` to update links to any resources for download.
+`default.catalog.bom` on the classpath. The initial catalog is in `conf/brooklyn/` in the dist.
+If you have a preferred catalog, simply replace that file.
 
 
-### <a id="confirm"></a>Confirm installation
+### <a id="confirm"></a>Confirm Installation
 
 We can do a quick test drive by launching Brooklyn:
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/49a6ef7b/docs/guide/start/blueprints.md
----------------------------------------------------------------------
diff --git a/docs/guide/start/blueprints.md b/docs/guide/start/blueprints.md
index 9986178..5966b77 100644
--- a/docs/guide/start/blueprints.md
+++ b/docs/guide/start/blueprints.md
@@ -45,17 +45,14 @@ application. Your application will be shown as "Starting" on the web console's f
 
 Instead of pasting the YAML blueprint each time,
 this blueprint can be [added to the catalog](../ops/catalog/).
-Or, even easier, you can download a sample [catalog.xml](catalog.xml).
-Install this to your `~/.brooklyn/` folder and relaunch Brooklyn
-(navigating to the "Help" tab in order to shutdown Brooklyn *and* the application you launched in the previous step).
-
-Now when the web console is re-opened, the catalog contains our blueprints.
-Select the "Demo Web Cluster with DB" and click "Next".
+With this YAML blueprint added, including the location, the Add Application dialog will offer 
+the "Demo Web Cluster with DB" as a template.
 
 [![Viewing Catalog entries in Add Application dialog.](images/add-application-catalog-web-cluster-with-db.png)](images/add-application-catalog-web-cluster-with-db-large.png)
 
-Select the location to use, give your application a name, and then click "Finish".
-
+<!-- TODO: more detail for adding to catalog? but wait for persistence to be the default, 
+     rather than extensively document default.catalog.bom.
+     also need to include instructions on stopping (currently in help, including stopping apps) -->
 
 ## Next 
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/49a6ef7b/docs/guide/start/catalog.xml
----------------------------------------------------------------------
diff --git a/docs/guide/start/catalog.xml b/docs/guide/start/catalog.xml
deleted file mode 100644
index 6cff554..0000000
--- a/docs/guide/start/catalog.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<catalog>
-    <name>Brooklyn Demos</name>
-
-
-    <template type="brooklyn.demo.WebClusterDatabaseExample" name="Demo Web Cluster with DB">
-      <description>Deploys a demonstration web application to a managed JBoss cluster with elasticity, persisting to a MySQL</description>
-      <iconUrl>http://downloads.cloudsoftcorp.com/brooklyn/catalog/logos/JBoss_by_Red_Hat.png</iconUrl>
-    </template>
-
-    <template type="brooklyn.demo.GlobalWebFabricExample" name="Demo GeoDNS Web Fabric DB">
-      <description>Deploys a demonstration web application to JBoss clusters around the world</description>
-      <iconUrl>http://downloads.cloudsoftcorp.com/brooklyn/catalog/logos/JBoss_by_Red_Hat.png</iconUrl>
-    </template>
-    
-    <classpath>
-      <entry>https://oss.sonatype.org/service/local/artifact/maven/redirect?r=releases&amp;g=io.brooklyn.example&amp;a=brooklyn-example-simple-web-cluster&amp;v=0.7.0-SNAPSHOT&amp;e=jar</entry> <!-- BROOKLYN_VERSION -->
-      <entry>https://oss.sonatype.org/service/local/artifact/maven/redirect?r=releases&amp;g=io.brooklyn.example&amp;a=brooklyn-example-global-web-fabric&amp;v=0.7.0-SNAPSHOT&amp;e=jar</entry> <!-- BROOKLYN_VERSION -->
-    </classpath>
-
-
-</catalog>
-

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/49a6ef7b/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 962bc30..e887422 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1005,10 +1005,9 @@
                     <artifactId>apache-rat-plugin</artifactId>
                     <configuration>
                         <excludes combine.children="append">
-                            <!--
-                                Excluding sandbox because not part of distribution: not in tgz, and not uploaded to maven-central.
-                            -->
+                            <!-- Exclude sandbox because not part of distribution: not in tgz, and not uploaded to maven-central -->
                             <exclude>sandbox/**</exclude>
+                            <!-- Exclude netbeans config files (not part of the project, but often on users' drives -->
                             <exclude>**/nbactions.xml</exclude>
                             <exclude>**/nb-configuration.xml</exclude>
                         </excludes>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/49a6ef7b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNode.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNode.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNode.java
index 20626f9..0a95327 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNode.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNode.java
@@ -171,14 +171,20 @@ public interface BrooklynNode extends SoftwareProcess, UsesJava {
             "brooklynnode.brooklynproperties.local.contents", "Contents for the launch-specific brooklyn properties file", null);
     
     // For use in testing primarily
+    /** @deprecated since 0.7.0; TODO this should support BOM files */
+    @Deprecated
     @SetFromFlag("brooklynCatalogRemotePath")
     public static final ConfigKey<String> BROOKLYN_CATALOG_REMOTE_PATH = ConfigKeys.newStringConfigKey(
             "brooklynnode.brooklyncatalog.remotepath", "Remote path for the brooklyn catalog.xml file to be uploaded", "${HOME}/.brooklyn/catalog.xml");
     
+    /** @deprecated since 0.7.0; TODO this should support BOM files */
+    @Deprecated
     @SetFromFlag("brooklynCatalogUri")
     public static final ConfigKey<String> BROOKLYN_CATALOG_URI = ConfigKeys.newStringConfigKey(
             "brooklynnode.brooklyncatalog.uri", "URI for the brooklyn catalog.xml file (uploaded to ~/.brooklyn/catalog.xml)", null);
 
+    /** @deprecated since 0.7.0; TODO this should support BOM files */
+    @Deprecated
     @SetFromFlag("brooklynCatalogContents")
     public static final ConfigKey<String> BROOKLYN_CATALOG_CONTENTS = ConfigKeys.newStringConfigKey(
             "brooklynnode.brooklyncatalog.contents", "Contents for the brooklyn catalog.xml file (uploaded to ~/.brooklyn/catalog.xml)", null);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/49a6ef7b/usage/cli/src/main/resources/brooklyn/default.catalog.bom
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/default.catalog.bom b/usage/cli/src/main/resources/brooklyn/default.catalog.bom
index cddb832..0db2f20 100644
--- a/usage/cli/src/main/resources/brooklyn/default.catalog.bom
+++ b/usage/cli/src/main/resources/brooklyn/default.catalog.bom
@@ -15,5 +15,9 @@
 # specific language governing permissions and limitations
 # under the License.
 #
+
+# this catalog bom simply scans.
+# in a dist a default.catalog.bom from the conf/ dir takes precedence.
+
 brooklyn.catalog:
   scanJavaAnnotations: true

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/49a6ef7b/usage/dist/pom.xml
----------------------------------------------------------------------
diff --git a/usage/dist/pom.xml b/usage/dist/pom.xml
index 3b7f1e6..ff0db95 100644
--- a/usage/dist/pom.xml
+++ b/usage/dist/pom.xml
@@ -63,6 +63,16 @@
     <build>
         <plugins>
             <plugin>
+                <groupId>org.apache.rat</groupId>
+                <artifactId>apache-rat-plugin</artifactId>
+                <configuration>
+                    <excludes combine.children="append">
+                        <!-- Exclude sample config files because they are illustrative, intended for changing -->
+                        <exclude>src/main/dist/conf/**</exclude>
+                    </excludes>
+                  </configuration>
+            </plugin>
+            <plugin>
                 <artifactId>maven-assembly-plugin</artifactId>
                 <executions>
                     <execution>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/49a6ef7b/usage/dist/src/main/dist/conf/brooklyn/default.catalog.bom
----------------------------------------------------------------------
diff --git a/usage/dist/src/main/dist/conf/brooklyn/default.catalog.bom b/usage/dist/src/main/dist/conf/brooklyn/default.catalog.bom
new file mode 100644
index 0000000..42672ae
--- /dev/null
+++ b/usage/dist/src/main/dist/conf/brooklyn/default.catalog.bom
@@ -0,0 +1,41 @@
+
+# this catalog bom is an illustration supplying a few useful sample items
+# and templates to get started using Brooklyn
+
+brooklyn.catalog:
+  version: 0.7.0-SNAPSHOT  # BROOKLYN_VERSION
+  items:
+
+  # load everything in the classpath with a @Catalog annotation
+  - scanJavaAnnotations: true
+
+  - id: server
+    description: |
+      Provision a server, with customizable provisioning.properties and credentials installed, 
+      but no other special software process or scripts executed.
+    item:
+      type: brooklyn.entity.basic.EmptySoftwareProcess
+      name: Server
+
+  - id: server-template
+    itemType: template
+    name: Server Template
+    description: |
+      Sample YAML to provision a server in a cloud with illustrative VM properties
+    item:
+      name: My App with a single VM
+      services:
+      - type: server
+        name: My VM
+      location:
+        jclouds:aws-ec2:
+          identity: <REPLACE>
+          credential: <REPLACE>
+          region: eu-west-1
+          # we want Ubuntu, with a lot of RAM
+          osFamily: ubuntu
+          minRam: 8gb
+          # set up this user and password (default is to authorize a public key)
+          user: sample
+          password: s4mpl3
+

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/49a6ef7b/usage/dist/src/main/dist/conf/catalog.xml
----------------------------------------------------------------------
diff --git a/usage/dist/src/main/dist/conf/catalog.xml b/usage/dist/src/main/dist/conf/catalog.xml
deleted file mode 100644
index 343e1da..0000000
--- a/usage/dist/src/main/dist/conf/catalog.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-    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.
--->
-<catalog>
-    <name>Brooklyn Demos</name>
-
-    <template type="brooklyn.demo.WebClusterDatabaseExample" name="Demo Web Cluster with DB">
-      <description>Deploys a demonstration web application to a managed JBoss cluster with elasticity, persisting to a MySQL</description>
-      <iconUrl>http://releng3.cloudsoftcorp.com/downloads/brooklyn/img/JBoss_by_Red_Hat.png</iconUrl>
-    </template>
-    <template type="brooklyn.demo.GlobalWebFabricExample" name="Demo GeoDNS Web Fabric DB">
-      <description>Deploys a demonstration web application to JBoss clusters around the world</description>
-      <iconUrl>http://releng3.cloudsoftcorp.com/downloads/brooklyn/img/JBoss_by_Red_Hat.png</iconUrl>
-    </template>
-    
-    <classpath>
-      <entry>https://repository.apache.org/service/local/artifact/maven/redirect?r=releases&amp;g=org.apache.brooklyn.example&amp;a=brooklyn-example-simple-web-cluster&amp;v=0.7.0-SNAPSHOT&amp;e=jar</entry> <!-- BROOKLYN_VERSION -->
-      <entry>https://repository.apache.org/service/local/artifact/maven/redirect?r=releases&amp;g=org.apache.brooklyn.example&amp;a=brooklyn-example-global-web-fabric&amp;v=0.7.0-SNAPSHOT&amp;e=jar</entry> <!-- BROOKLYN_VERSION -->
-    </classpath>
-</catalog>
-

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/49a6ef7b/usage/dist/src/main/dist/conf/logback.xml
----------------------------------------------------------------------
diff --git a/usage/dist/src/main/dist/conf/logback.xml b/usage/dist/src/main/dist/conf/logback.xml
index 0224ac1..e70862c 100644
--- a/usage/dist/src/main/dist/conf/logback.xml
+++ b/usage/dist/src/main/dist/conf/logback.xml
@@ -1,22 +1,4 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--
-    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.
--->
 <configuration>
 
     <!-- to supply custom logging, either change this file, supply your own logback-main.xml 


[09/23] incubator-brooklyn git commit: move riak.png to resources file so it ends up in the build

Posted by he...@apache.org.
move riak.png to resources file so it ends up in the build


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

Branch: refs/heads/master
Commit: 127150e5a569073d7f2dfbcd90c5763c0445189e
Parents: 461ac9f
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Apr 30 09:13:42 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:22:22 2015 +0100

----------------------------------------------------------------------
 .../main/java/brooklyn/entity/nosql/riak/riak.png | Bin 110651 -> 0 bytes
 .../resources/brooklyn/entity/nosql/riak/riak.png | Bin 0 -> 110651 bytes
 2 files changed, 0 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/127150e5/software/nosql/src/main/java/brooklyn/entity/nosql/riak/riak.png
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/brooklyn/entity/nosql/riak/riak.png b/software/nosql/src/main/java/brooklyn/entity/nosql/riak/riak.png
deleted file mode 100644
index a230b04..0000000
Binary files a/software/nosql/src/main/java/brooklyn/entity/nosql/riak/riak.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/127150e5/software/nosql/src/main/resources/brooklyn/entity/nosql/riak/riak.png
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/resources/brooklyn/entity/nosql/riak/riak.png b/software/nosql/src/main/resources/brooklyn/entity/nosql/riak/riak.png
new file mode 100644
index 0000000..a230b04
Binary files /dev/null and b/software/nosql/src/main/resources/brooklyn/entity/nosql/riak/riak.png differ


[06/23] incubator-brooklyn git commit: item lister resources put in an appropriate subdir; other related tidies

Posted by he...@apache.org.
item lister resources put in an appropriate subdir; other related tidies

code was very ad hoc, but also polluting the root of the all jar; now resources at least are in a clean subdir


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

Branch: refs/heads/master
Commit: 461ac9f1f318b31558a45c011332803379632805
Parents: 1b080fb
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed Apr 29 13:32:34 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:22:22 2015 +0100

----------------------------------------------------------------------
 usage/cli/pom.xml                               |   5 +-
 .../src/main/java/brooklyn/cli/ItemLister.java  |  77 +-
 .../java/brooklyn/cli/lister/ClassFinder.java   |   7 +-
 .../brooklyn/cli/lister/ItemDescriptors.java    |  20 +-
 .../main/resources/brooklyn-object-list.html    | 147 ----
 .../statics/brooklyn-object-list.html           | 147 ++++
 .../brooklyn/item-lister/statics/common.js      |  94 +++
 .../brooklyn/item-lister/statics/items.css      | 153 ++++
 .../statics/style/js/catalog/bloodhound.js      | 727 +++++++++++++++++++
 .../statics/style/js/underscore-min.js          |   6 +
 .../statics/style/js/underscore-min.map         |   1 +
 .../item-lister/templates/enricher.html         |  59 ++
 .../brooklyn/item-lister/templates/entity.html  |  66 ++
 .../item-lister/templates/location.html         |  62 ++
 .../brooklyn/item-lister/templates/policy.html  |  59 ++
 usage/cli/src/main/resources/common.js          |  94 ---
 usage/cli/src/main/resources/enricher.html      |  59 --
 usage/cli/src/main/resources/entity.html        |  66 --
 usage/cli/src/main/resources/items.css          | 153 ----
 .../src/main/resources/libs/js/bloodhound.js    | 727 -------------------
 usage/cli/src/main/resources/location.html      |  62 --
 usage/cli/src/main/resources/policy.html        |  59 --
 22 files changed, 1457 insertions(+), 1393 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/pom.xml
----------------------------------------------------------------------
diff --git a/usage/cli/pom.xml b/usage/cli/pom.xml
index 1042649..451b0ed 100644
--- a/usage/cli/pom.xml
+++ b/usage/cli/pom.xml
@@ -159,11 +159,12 @@
                     <configuration>
                         <excludes combine.children="append">
                             <!--
-                                bloodhound.js is copyright Twitter, Inc. It is included in NOTICE.
+                                bloodhound.js is copyright Twitter, Inc.  underscore is copyright Jeremy Ashkenas, DocumentCloud.
+                                Both are included in the LICENSE notes.
                                 It is used by the HTML generated by the CLI tool for entity descriptions,
                                 i.e. by brooklyn.cli.itemlister.ItemLister
                             -->
-                            <exclude>**/src/main/resources/libs/js/bloodhound.js</exclude>
+                            <exclude>**/src/main/resources/brooklyn/item-lister/statics/style/js/**</exclude>
                         </excludes>
                     </configuration>
                 </plugin>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/java/brooklyn/cli/ItemLister.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/brooklyn/cli/ItemLister.java b/usage/cli/src/main/java/brooklyn/cli/ItemLister.java
index 3c567dc..e616fb5 100644
--- a/usage/cli/src/main/java/brooklyn/cli/ItemLister.java
+++ b/usage/cli/src/main/java/brooklyn/cli/ItemLister.java
@@ -22,8 +22,10 @@ import io.airlift.command.Command;
 import io.airlift.command.Option;
 
 import java.io.File;
+import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.ServiceLoader;
@@ -43,6 +45,7 @@ import brooklyn.policy.Enricher;
 import brooklyn.policy.Policy;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableSet;
+import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
 import brooklyn.util.text.Strings;
 import brooklyn.util.text.TemplateProcessor;
@@ -54,7 +57,6 @@ import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
-
 import com.google.common.base.Charsets;
 import com.google.common.base.Splitter;
 import com.google.common.collect.FluentIterable;
@@ -66,6 +68,9 @@ import com.google.common.io.Files;
 public class ItemLister {
     
     private static final Logger LOG = LoggerFactory.getLogger(ItemLister.class);
+    private static final String BASE = "brooklyn/item-lister";
+    private static final String BASE_TEMPLATES = BASE+"/"+"templates";
+    private static final String BASE_STATICS = BASE+"/"+"statics";
 
     @Command(name = "list-objects", description = "List Brooklyn objects (Entities, Policies, Enrichers and Locations)")
     public static class ListAllCommand extends AbstractMain.BrooklynCommandCollectingArgs {
@@ -91,8 +96,8 @@ public class ItemLister {
         @SuppressWarnings("unchecked")
         @Override
         public Void call() throws Exception {
-            LOG.info("Retrieving objects");
             List<URL> urls = getUrls();
+            LOG.info("Retrieving objects from "+urls);
 
             // TODO Remove duplication from separate ListPolicyCommand etc
             List<Class<? extends Entity>> entityTypes = getTypes(urls, Entity.class);
@@ -113,48 +118,63 @@ public class ItemLister {
             if (outputFolder == null) {
                 System.out.println(json);
             } else {
-                LOG.info("Outputting item list to " + outputFolder);
+                LOG.info("Outputting item list (size "+itemCount+") to " + outputFolder);
                 String outputPath = Os.mergePaths(outputFolder, "index.html");
                 String parentDir = (new File(outputPath).getParentFile()).getAbsolutePath();
                 mkdir(parentDir, "entities");
                 mkdir(parentDir, "policies");
                 mkdir(parentDir, "enrichers");
                 mkdir(parentDir, "locations");
-                mkdir(parentDir, "locationResolvers");
+                mkdir(parentDir, "locationResolvers"); //TODO nothing written here yet...
+                
+                mkdir(parentDir, "style");
+                mkdir(Os.mergePaths(parentDir, "style"), "js");
+                mkdir(Os.mergePaths(parentDir, "style", "js"), "catalog");
+                
                 Files.write("var items = " + json, new File(Os.mergePaths(outputFolder, "items.js")), Charsets.UTF_8);
                 ResourceUtils resourceUtils = ResourceUtils.create(this);
-                String js = resourceUtils.getResourceAsString("common.js");
-                Files.write(js, new File(Os.mergePaths(outputFolder, "common.js")), Charsets.UTF_8);
-                String css = resourceUtils.getResourceAsString("items.css");
-                Files.write(css, new File(Os.mergePaths(outputFolder, "items.css")), Charsets.UTF_8);
-                String mainHtml = resourceUtils.getResourceAsString("brooklyn-object-list.html");
-                Files.write(mainHtml, new File(Os.mergePaths(outputFolder, "index.html")), Charsets.UTF_8);
+                
+                // root - just loads the above JSON
+                copyFromItemListerClasspathBaseStaticsToOutputDir(resourceUtils, "brooklyn-object-list.html", "index.html");
+                
+                // statics - structure mirrors docs (not for any real reason however... the json is usually enough for our docs)
+                copyFromItemListerClasspathBaseStaticsToOutputDir(resourceUtils, "common.js");
+                copyFromItemListerClasspathBaseStaticsToOutputDir(resourceUtils, "items.css");
+                copyFromItemListerClasspathBaseStaticsToOutputDir(resourceUtils, "style/js/underscore-min.js");
+                copyFromItemListerClasspathBaseStaticsToOutputDir(resourceUtils, "style/js/underscore-min.map");
+                copyFromItemListerClasspathBaseStaticsToOutputDir(resourceUtils, "style/js/catalog/bloodhound.js");
+
+                // now make pages for each item
+                
                 List<Map<String, Object>> entities = (List<Map<String, Object>>) result.get("entities");
-                String entityTemplateHtml = resourceUtils.getResourceAsString("entity.html");
+                String entityTemplateHtml = resourceUtils.getResourceAsString(Urls.mergePaths(BASE_TEMPLATES, "entity.html"));
                 for (Map<String, Object> entity : entities) {
                     String type = (String) entity.get("type");
                     String name = (String) entity.get("name");
                     String entityHtml = TemplateProcessor.processTemplateContents(entityTemplateHtml, ImmutableMap.of("type", type, "name", name));
                     Files.write(entityHtml, new File(Os.mergePaths(outputFolder, "entities", type + ".html")), Charsets.UTF_8);
                 }
+                
                 List<Map<String, Object>> policies = (List<Map<String, Object>>) result.get("policies");
-                String policyTemplateHtml = resourceUtils.getResourceAsString("policy.html");
+                String policyTemplateHtml = resourceUtils.getResourceAsString(Urls.mergePaths(BASE_TEMPLATES, "policy.html"));
                 for (Map<String, Object> policy : policies) {
                     String type = (String) policy.get("type");
                     String name = (String) policy.get("name");
                     String policyHtml = TemplateProcessor.processTemplateContents(policyTemplateHtml, ImmutableMap.of("type", type, "name", name));
                     Files.write(policyHtml, new File(Os.mergePaths(outputFolder, "policies", type + ".html")), Charsets.UTF_8);
                 }
+                
                 List<Map<String, Object>> enrichers = (List<Map<String, Object>>) result.get("enrichers");
-                String enricherTemplateHtml = resourceUtils.getResourceAsString("enricher.html");
+                String enricherTemplateHtml = resourceUtils.getResourceAsString(Urls.mergePaths(BASE_TEMPLATES, "enricher.html"));
                 for (Map<String, Object> enricher : enrichers) {
                     String type = (String) enricher.get("type");
                     String name = (String) enricher.get("name");
                     String enricherHtml = TemplateProcessor.processTemplateContents(enricherTemplateHtml, ImmutableMap.of("type", type, "name", name));
                     Files.write(enricherHtml, new File(Os.mergePaths(outputFolder, "enrichers", type + ".html")), Charsets.UTF_8);
                 }
+                
                 List<Map<String, Object>> locations = (List<Map<String, Object>>) result.get("locations");
-                String locationTemplateHtml = resourceUtils.getResourceAsString("location.html");
+                String locationTemplateHtml = resourceUtils.getResourceAsString(Urls.mergePaths(BASE_TEMPLATES, "location.html"));
                 for (Map<String, Object> location : locations) {
                     String type = (String) location.get("type");
                     String locationHtml = TemplateProcessor.processTemplateContents(locationTemplateHtml, ImmutableMap.of("type", type));
@@ -165,6 +185,14 @@ public class ItemLister {
             return null;
         }
 
+        private void copyFromItemListerClasspathBaseStaticsToOutputDir(ResourceUtils resourceUtils, String item) throws IOException {
+            copyFromItemListerClasspathBaseStaticsToOutputDir(resourceUtils, item, item);
+        }
+        private void copyFromItemListerClasspathBaseStaticsToOutputDir(ResourceUtils resourceUtils, String item, String dest) throws IOException {
+            String js = resourceUtils.getResourceAsString(Urls.mergePaths(BASE_STATICS, item));
+            Files.write(js, new File(Os.mergePaths(outputFolder, dest)), Charsets.UTF_8);
+        }
+
         private void mkdir(String rootDir, String dirName) {
             (new File(Os.mergePaths(rootDir, dirName))).mkdirs();
         }
@@ -185,7 +213,10 @@ public class ItemLister {
                 }
             } else {
                 for (String jar : jars) {
-                    urls.addAll(ClassFinder.toJarUrls(jar));
+                    List<URL> expanded = ClassFinder.toJarUrls(jar);
+                    if (expanded.isEmpty())
+                        LOG.warn("No jars found at: "+jar);
+                    urls.addAll(expanded);
                 }
             }
             return urls;
@@ -194,6 +225,8 @@ public class ItemLister {
         private <T extends BrooklynObject> List<Class<? extends T>> getTypes(List<URL> urls, Class<T> type) {
             return getTypes(urls, type, null);
         }
+
+        int itemCount = 0;
         
         private <T extends BrooklynObject> List<Class<? extends T>> getTypes(List<URL> urls, Class<T> type, Boolean catalogOnlyOverride) {
             FluentIterable<Class<? extends T>> fluent = FluentIterable.from(ClassFinder.findClasses(urls, type));
@@ -203,19 +236,21 @@ public class ItemLister {
             if (catalogOnlyOverride == null ? catalogOnly : catalogOnlyOverride) {
                 fluent = fluent.filter(ClassFinder.withAnnotation(Catalog.class));
             }
-            ImmutableList<Class<? extends T>> result = fluent.toList();
+            List<Class<? extends T>> filtered = fluent.toList();
+            Collection<Class<? extends T>> result;
             if (ignoreImpls) {
-                MutableSet<Class<? extends T>> mutableResult = MutableSet.copyOf(result);
-                for (Class<? extends T> clazz : result) {
+                result = MutableSet.copyOf(filtered);
+                for (Class<? extends T> clazz : filtered) {
                     ImplementedBy implementedBy = clazz.getAnnotation(ImplementedBy.class);
                     if (implementedBy != null) {
-                        mutableResult.remove(implementedBy.value());
+                        result.remove(implementedBy.value());
                     }
                 }
-                return ImmutableList.copyOf(mutableResult);
             } else {
-                return result;
+                result = filtered;
             }
+            itemCount += result.size();
+            return ImmutableList.copyOf(result);
         }
         
         private String toJson(Object obj) throws JsonProcessingException {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/java/brooklyn/cli/lister/ClassFinder.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/brooklyn/cli/lister/ClassFinder.java b/usage/cli/src/main/java/brooklyn/cli/lister/ClassFinder.java
index 101e019..a66fa7d 100644
--- a/usage/cli/src/main/java/brooklyn/cli/lister/ClassFinder.java
+++ b/usage/cli/src/main/java/brooklyn/cli/lister/ClassFinder.java
@@ -49,6 +49,7 @@ import brooklyn.util.javalang.UrlClassLoader;
 import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
@@ -91,7 +92,9 @@ public class ClassFinder {
             }
         };
     }
-    
+
+    /** finds a jar at a url, or for directories, jars under a path */
+    @Beta
     public static List<URL> toJarUrls(String url) throws MalformedURLException {
         if (url==null) throw new NullPointerException("Cannot read from null");
         if (url=="") throw new NullPointerException("Cannot read from empty string");
@@ -126,6 +129,8 @@ public class ClassFinder {
                         log.info("Cannot read "+file+"; not a file or directory");
                     }
                 }
+            } else {
+                result.add(new URL("file://"+tidiedFile));
             }
         }
         

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/java/brooklyn/cli/lister/ItemDescriptors.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/brooklyn/cli/lister/ItemDescriptors.java b/usage/cli/src/main/java/brooklyn/cli/lister/ItemDescriptors.java
index 3039154..989a29d 100644
--- a/usage/cli/src/main/java/brooklyn/cli/lister/ItemDescriptors.java
+++ b/usage/cli/src/main/java/brooklyn/cli/lister/ItemDescriptors.java
@@ -24,6 +24,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import brooklyn.basic.BrooklynDynamicType;
 import brooklyn.basic.BrooklynObject;
 import brooklyn.basic.BrooklynType;
@@ -42,6 +45,8 @@ import brooklyn.rest.domain.SummaryComparators;
 import brooklyn.rest.transform.EffectorTransformer;
 import brooklyn.rest.transform.EntityTransformer;
 import brooklyn.rest.transform.SensorTransformer;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.exceptions.RuntimeInterruptedException;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
@@ -50,6 +55,8 @@ import com.google.common.collect.Sets;
 
 public class ItemDescriptors {
 
+    private static final Logger LOG = LoggerFactory.getLogger(ItemDescriptors.class);
+    
     public static List<Map<String, Object>> toItemDescriptors(Iterable<? extends Class<? extends BrooklynObject>> types, boolean headingsOnly) {
         return toItemDescriptors(types, headingsOnly, null);
     }
@@ -58,8 +65,17 @@ public class ItemDescriptors {
         List<Map<String, Object>> itemDescriptors = Lists.newArrayList();
         
         for (Class<? extends BrooklynObject> type : types) {
-            Map<String, Object> itemDescriptor = toItemDescriptor(type, headingsOnly);
-            itemDescriptors.add(itemDescriptor);
+            try {
+                Map<String, Object> itemDescriptor = toItemDescriptor(type, headingsOnly);
+                itemDescriptors.add(itemDescriptor);
+            } catch (Throwable throwable) {
+                if (throwable instanceof InterruptedException)
+                    throw new RuntimeInterruptedException((InterruptedException) throwable);
+                if (throwable instanceof RuntimeInterruptedException)
+                    throw (RuntimeInterruptedException) throwable;
+
+                LOG.warn("Could not load "+type+": "+throwable);
+            }
         }
         
         if (!Strings.isNullOrEmpty(sortField)) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn-object-list.html
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn-object-list.html b/usage/cli/src/main/resources/brooklyn-object-list.html
deleted file mode 100644
index 4487346..0000000
--- a/usage/cli/src/main/resources/brooklyn-object-list.html
+++ /dev/null
@@ -1,147 +0,0 @@
-<!DOCTYPE html>
-<!--
-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.
--->
-<html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
-    <link rel="stylesheet" href="items.css" type="text/css" media="screen"/>
-    <title>Brooklyn Objects</title>
-  </head>
-
-  <body>
-    <div id="container">
-      <div id="header">
-        <div id="identity">
-          <a href="https://brooklyn.incubator.apache.org/" rel="home">Brooklyn</a>
-        </div>
-      </div>
-
-      <ul class="nav nav-tabs">
-        <li class="active"><a href="#entities" data-toggle="tab">Entities</a></li>
-        <li><a href="#policies" data-toggle="tab">Policies</a></li>
-        <li><a href="#enrichers" data-toggle="tab">Enrichers</a></li>
-      </ul>
-
-      <div class="tab-content">
-        <div class="tab-pane active" id="entities">
-          <input class="filter form-control" type="text" placeholder="Filter by type, e.g. webapp or nosql">
-        </div>
-        <div class="tab-pane" id="policies">
-          <input class="filter form-control" type="text" placeholder="Filter by type, e.g. ha">
-        </div>
-        <div class="tab-pane" id="enrichers">
-          <input class="filter form-control" type="text" placeholder="Filter by type, e.g. http">
-        </div>
-        <div class="tab-pane" id="locations"></div>
-        <div class="tab-pane" id="locationResolvers"></div>
-      </div>
-    </div>
-
-    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script>
-    <script src="../../style/js/underscore-min.js" type="text/javascript"></script>
-    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
-    <script src="libs/js/bloodhound.js" type="text/javascript"></script>
-    <script src="common.js" type="text/javascript"></script>
-    <script src="items.js" type="text/javascript"></script>
-    <script type="text/javascript">
-        if (!String.prototype.trim) {
-            String.prototype.trim = function () {
-                return this.replace(/^\s+|\s+$/g, '');
-            };
-        }
-        var card = function (collection, cardFunction, target) {
-            var cards = _.map(collection, cardFunction);
-            $(target).append(cards.join(""));
-        };
-        var ESCAPE_KEY = 27;
-
-        var filter = function (element) {
-            var $element = $(element),
-                $tab = $element.parent(),
-                kind = $tab.attr("id"),
-                collection = items[kind];
-            if (!collection) {
-                console.warn("Unable to determine type for input", element);
-                return;
-            }
-
-            // Number.MAX_VALUE configures Bloodhound to return all matches.
-            var bloodhound = new Bloodhound({
-                name: kind,
-                local: collection,
-                limit: Number.MAX_VALUE,
-                datumTokenizer: function (d) {
-                    return Bloodhound.tokenizers.nonword(d.type);
-                },
-                queryTokenizer: Bloodhound.tokenizers.nonword
-            });
-            bloodhound.initialize();
-
-            // Filter items as input changes
-            var allAnchors = $tab.find("a").map(function (index, a) { return $(a); });
-            var hideAnchorsNotMatchingQuery = function () {
-                var query = $element.val();
-                query = query.trim();
-                if (!query) {
-                    $tab.find("a").removeClass("hide");
-                } else {
-                    var matchedTypes = {};
-                    bloodhound.get(query, function (suggestions) {
-                        _.each(suggestions, function (s) {
-                            // approximate a set!
-                            matchedTypes[s.type] = true;
-                        });
-                    });
-                    _.each(allAnchors, function (a) {
-                        if (_.has(matchedTypes, a.data("type"))) {
-                            a.removeClass("hide");
-                        } else {
-                            a.addClass("hide");
-                        }
-                    });
-                }
-            };
-            $element.on("input", hideAnchorsNotMatchingQuery);
-            // In case page is loaded with text in input, e.g. from back button.
-            hideAnchorsNotMatchingQuery();
-
-            $element.on("keydown", function (e) {
-                if (e.keyCode == ESCAPE_KEY) {
-                    $element.val("");
-                    hideAnchorsNotMatchingQuery();
-                }
-            });
-        };
-
-        $(document).ready(function () {
-            card(items.entities, brooklyn.entityCard, "#entities");
-            card(items.policies, brooklyn.policyCard, "#policies");
-            card(items.enrichers, brooklyn.enricherCard, "#enrichers");
-            //transformItemAndAddToElement(items.locations, brooklyn.locationCard, "#locations");
-            //items.locationResolvers.forEach(function (element) { $("#locationResolvers").append("<tr><td>" + element + "</td></tr>"); });
-            $("input.filter").each(function (index, element) {
-                filter(element);
-            });
-        });
-    </script>
-  </body>
-</html>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn/item-lister/statics/brooklyn-object-list.html
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/item-lister/statics/brooklyn-object-list.html b/usage/cli/src/main/resources/brooklyn/item-lister/statics/brooklyn-object-list.html
new file mode 100644
index 0000000..c30d3db
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/item-lister/statics/brooklyn-object-list.html
@@ -0,0 +1,147 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+    <link rel="stylesheet" href="items.css" type="text/css" media="screen"/>
+    <title>Brooklyn Objects</title>
+  </head>
+
+  <body>
+    <div id="container">
+      <div id="header">
+        <div id="identity">
+          <a href="https://brooklyn.incubator.apache.org/" rel="home">Brooklyn</a>
+        </div>
+      </div>
+
+      <ul class="nav nav-tabs">
+        <li class="active"><a href="#entities" data-toggle="tab">Entities</a></li>
+        <li><a href="#policies" data-toggle="tab">Policies</a></li>
+        <li><a href="#enrichers" data-toggle="tab">Enrichers</a></li>
+      </ul>
+
+      <div class="tab-content">
+        <div class="tab-pane active" id="entities">
+          <input class="filter form-control" type="text" placeholder="Filter by type, e.g. webapp or nosql">
+        </div>
+        <div class="tab-pane" id="policies">
+          <input class="filter form-control" type="text" placeholder="Filter by type, e.g. ha">
+        </div>
+        <div class="tab-pane" id="enrichers">
+          <input class="filter form-control" type="text" placeholder="Filter by type, e.g. http">
+        </div>
+        <div class="tab-pane" id="locations"></div>
+        <div class="tab-pane" id="locationResolvers"></div>
+      </div>
+    </div>
+
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script>
+    <script src="style/js/underscore-min.js" type="text/javascript"></script>
+    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
+    <script src="style/js/catalog/bloodhound.js" type="text/javascript"></script>
+    <script src="common.js" type="text/javascript"></script>
+    <script src="items.js" type="text/javascript"></script>
+    <script type="text/javascript">
+        if (!String.prototype.trim) {
+            String.prototype.trim = function () {
+                return this.replace(/^\s+|\s+$/g, '');
+            };
+        }
+        var card = function (collection, cardFunction, target) {
+            var cards = _.map(collection, cardFunction);
+            $(target).append(cards.join(""));
+        };
+        var ESCAPE_KEY = 27;
+
+        var filter = function (element) {
+            var $element = $(element),
+                $tab = $element.parent(),
+                kind = $tab.attr("id"),
+                collection = items[kind];
+            if (!collection) {
+                console.warn("Unable to determine type for input", element);
+                return;
+            }
+
+            // Number.MAX_VALUE configures Bloodhound to return all matches.
+            var bloodhound = new Bloodhound({
+                name: kind,
+                local: collection,
+                limit: Number.MAX_VALUE,
+                datumTokenizer: function (d) {
+                    return Bloodhound.tokenizers.nonword(d.type);
+                },
+                queryTokenizer: Bloodhound.tokenizers.nonword
+            });
+            bloodhound.initialize();
+
+            // Filter items as input changes
+            var allAnchors = $tab.find("a").map(function (index, a) { return $(a); });
+            var hideAnchorsNotMatchingQuery = function () {
+                var query = $element.val();
+                query = query.trim();
+                if (!query) {
+                    $tab.find("a").removeClass("hide");
+                } else {
+                    var matchedTypes = {};
+                    bloodhound.get(query, function (suggestions) {
+                        _.each(suggestions, function (s) {
+                            // approximate a set!
+                            matchedTypes[s.type] = true;
+                        });
+                    });
+                    _.each(allAnchors, function (a) {
+                        if (_.has(matchedTypes, a.data("type"))) {
+                            a.removeClass("hide");
+                        } else {
+                            a.addClass("hide");
+                        }
+                    });
+                }
+            };
+            $element.on("input", hideAnchorsNotMatchingQuery);
+            // In case page is loaded with text in input, e.g. from back button.
+            hideAnchorsNotMatchingQuery();
+
+            $element.on("keydown", function (e) {
+                if (e.keyCode == ESCAPE_KEY) {
+                    $element.val("");
+                    hideAnchorsNotMatchingQuery();
+                }
+            });
+        };
+
+        $(document).ready(function () {
+            card(items.entities, brooklyn.entityCard, "#entities");
+            card(items.policies, brooklyn.policyCard, "#policies");
+            card(items.enrichers, brooklyn.enricherCard, "#enrichers");
+            //transformItemAndAddToElement(items.locations, brooklyn.locationCard, "#locations");
+            //items.locationResolvers.forEach(function (element) { $("#locationResolvers").append("<tr><td>" + element + "</td></tr>"); });
+            $("input.filter").each(function (index, element) {
+                filter(element);
+            });
+        });
+    </script>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn/item-lister/statics/common.js
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/item-lister/statics/common.js b/usage/cli/src/main/resources/brooklyn/item-lister/statics/common.js
new file mode 100644
index 0000000..8e8a45f
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/item-lister/statics/common.js
@@ -0,0 +1,94 @@
+/**
+ * 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.
+ */
+
+var brooklyn = (function ($, _) {
+
+    return {
+        findItemOfType: function(items, type) {
+            return _.findWhere(items, { type: type });
+        },
+
+        entityCard: _.template(
+            "<a class='plain' data-type='<%= type %>' href='entities/<%= type %>.html'>" +
+            "<div class='card'>" +
+            "<span class='glyphicon glyphicon-chevron-right'/>" +
+            "<div class='name'><%=name%></div>" +
+            "<div class='type'><%=type%></div>" +
+            "<div class='description'><%=description%></div>" +
+            "</div>" +
+            "</a>"
+        ),
+        policyCard: _.template(
+            "<a class='plain' data-type='<%= type %>' href='policies/<%= type %>.html'>" +
+            "<div class='card'>" +
+            "<span class='glyphicon glyphicon-chevron-right'/>" +
+            "<div class='name'><%=name%></div>" +
+            "<div class='type'><%=type%></div>" +
+            "<div class='description'><%=description%></div>" +
+            "</div>" +
+            "</a>"
+        ),
+        enricherCard: _.template(
+            "<a class='plain' data-type='<%= type %>' href='enrichers/<%= type %>.html'>" +
+            "<div class='card'>" +
+            "<span class='glyphicon glyphicon-chevron-right'/>" +
+            "<div class='name'><%=name%></div>" +
+            "<div class='type'><%=type%></div>" +
+            "<div class='description'><%=description%></div>" +
+            "</div>" +
+            "</a>"
+        ),
+
+        typeSummary: _.template(
+            "<div class='summaryLabel'><%=name%></div>" +
+            "<div class='summaryType'><%=type%></div>" +
+            "<div class='description'><%=description%></div>"
+        ),
+
+        configKeyCard: _.template(
+            "<div class='card configKey'>" +
+            "<div class='name'><%=name%></div>" +
+            "<dl>" +
+            "<dt>description</dt><dd><%=(description||'&nbsp;')%></dd>" +
+            "<dt>value type</dt><dd class='java'><%=(type||'&nbsp;')%></dd>" +
+            "<dt>default value</dt><dd><%=(defaultValue||'&nbsp;')%></dd>" +
+            "</dl>" +
+            "</div>"
+        ),
+        sensorCard: _.template(
+            "<div class='card sensor'>" +
+            "<div class='name'><%=name%></div>" +
+            "<dl>" +
+            "<dt>description</dt><dd><%=(description||'&nbsp;')%></dd>" +
+            "<dt>value type</dt><dd class='java'><%=(type||'&nbsp;')%></dd>" +
+            "</dl>" +
+            "</div>"
+        ),
+        effectorCard: _.template(
+            "<div class='card effector'>" +
+            "<div class='name'><%=name%></div>" +
+            "<dl>" +
+            "<dt>description</dt><dd><%=(description||'&nbsp;')%></dd>" +
+            "<dt>return type</dt><dd class='java'><%=(returnType||'&nbsp;')%></dd>" +
+            "</dl>" +
+            "</div>"
+        )
+    };
+
+}(jQuery, _));

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn/item-lister/statics/items.css
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/item-lister/statics/items.css b/usage/cli/src/main/resources/brooklyn/item-lister/statics/items.css
new file mode 100644
index 0000000..a0bf0f0
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/item-lister/statics/items.css
@@ -0,0 +1,153 @@
+/*
+ * 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.
+*/
+/* landing page */
+body {
+  margin: 0px;
+  padding: 10px 0px 20px 0px;
+  font-family: arial, helvetica, sans-serif;
+  background-color: #ffffff;
+  color: #393939;
+  font-size: 15px;
+}
+
+.nav-tabs {
+  clear: both;
+  font-weight: bold;
+  font-size: 12pt;
+}
+.nav-tabs a {
+  color: #4d9d3a;
+}
+.nav-tabs a:hover {
+  color: #4d9d3a;
+}
+
+.tab-content {
+  padding: 20px;
+  padding-bottom: 10px;
+  border: 1px solid #ddd;
+  border-top: none;
+}
+
+a:hover > .card {
+  top: -2px;
+  background-color: #f4f4f4;
+  box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.2);
+}
+.card {
+  position: relative;
+  padding: 12px;
+  background-color: #f8f8f8;
+  color: #333;
+  border: 1px solid #E1E1E8;
+  border-radius: 6px;
+  font-size: 11pt;
+}
+.card,
+.filter {
+    margin-bottom: 10px;
+}
+a .glyphicon {
+  display: block;
+  position: absolute;
+  right: 0;
+  top: 0;
+  padding: 8px;
+  font-size: 16pt;
+  color: #aaa;
+}
+a:hover .glyphicon {
+  color: #888;
+}
+a.plain {
+  text-decoration: none !important;
+}
+.name {
+  font-size: 12pt;
+  font-weight: bold;
+}
+.type {
+  font-family: monospace;
+  color: #888;
+  margin-top: 2px;
+}
+#summary .description {
+  margin: 15px 0 25px 0;
+}
+.card .description {
+  margin: 10px 40px 0 20px;
+}
+
+#summary {
+  clear: both;
+  margin: 10px 0 20px 0;
+}
+.summaryLabel {
+  font-size: 20px;
+  font-weight: bold;
+}
+.summaryType {
+  font-family: monospace;
+  font-size: 12pt;
+  color: #888;
+}
+.java {
+  font-family: monospace;
+}
+
+.card dl {
+  margin-bottom: 0;
+  margin-top: 5px;
+}
+dt {
+  clear: both;
+  float: left;
+  width: 8em;
+  text-align: right;
+  font-weight: normal;
+}
+dd {
+  margin-left: 9em;
+}
+
+#container {
+  width: 980px;
+  padding: 0;
+  margin: 0 auto;
+}
+
+#identity {
+  float: left;
+  margin: 0;
+  padding: 30px 0 15px 10px;
+}
+
+#identity a {
+  text-decoration: none;
+  display: block;
+  margin: 0;
+  color: #4d9d3a;
+  font-size: 2.5em;
+  padding: 0;
+  background: transparent url(images/brooklyn.gif) no-repeat 0 0;
+  width: 206px;
+  height: 44px;
+  text-indent: -1000px;
+  overflow: hidden;
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/catalog/bloodhound.js
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/catalog/bloodhound.js b/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/catalog/bloodhound.js
new file mode 100644
index 0000000..96a4c43
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/catalog/bloodhound.js
@@ -0,0 +1,727 @@
+/*!
+ * typeahead.js 0.10.5
+ * https://github.com/twitter/typeahead.js
+ * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT
+ */
+
+(function($) {
+    var _ = function() {
+        "use strict";
+        return {
+            isMsie: function() {
+                return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
+            },
+            isBlankString: function(str) {
+                return !str || /^\s*$/.test(str);
+            },
+            escapeRegExChars: function(str) {
+                return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
+            },
+            isString: function(obj) {
+                return typeof obj === "string";
+            },
+            isNumber: function(obj) {
+                return typeof obj === "number";
+            },
+            isArray: $.isArray,
+            isFunction: $.isFunction,
+            isObject: $.isPlainObject,
+            isUndefined: function(obj) {
+                return typeof obj === "undefined";
+            },
+            toStr: function toStr(s) {
+                return _.isUndefined(s) || s === null ? "" : s + "";
+            },
+            bind: $.proxy,
+            each: function(collection, cb) {
+                $.each(collection, reverseArgs);
+                function reverseArgs(index, value) {
+                    return cb(value, index);
+                }
+            },
+            map: $.map,
+            filter: $.grep,
+            every: function(obj, test) {
+                var result = true;
+                if (!obj) {
+                    return result;
+                }
+                $.each(obj, function(key, val) {
+                    if (!(result = test.call(null, val, key, obj))) {
+                        return false;
+                    }
+                });
+                return !!result;
+            },
+            some: function(obj, test) {
+                var result = false;
+                if (!obj) {
+                    return result;
+                }
+                $.each(obj, function(key, val) {
+                    if (result = test.call(null, val, key, obj)) {
+                        return false;
+                    }
+                });
+                return !!result;
+            },
+            mixin: $.extend,
+            getUniqueId: function() {
+                var counter = 0;
+                return function() {
+                    return counter++;
+                };
+            }(),
+            templatify: function templatify(obj) {
+                return $.isFunction(obj) ? obj : template;
+                function template() {
+                    return String(obj);
+                }
+            },
+            defer: function(fn) {
+                setTimeout(fn, 0);
+            },
+            debounce: function(func, wait, immediate) {
+                var timeout, result;
+                return function() {
+                    var context = this, args = arguments, later, callNow;
+                    later = function() {
+                        timeout = null;
+                        if (!immediate) {
+                            result = func.apply(context, args);
+                        }
+                    };
+                    callNow = immediate && !timeout;
+                    clearTimeout(timeout);
+                    timeout = setTimeout(later, wait);
+                    if (callNow) {
+                        result = func.apply(context, args);
+                    }
+                    return result;
+                };
+            },
+            throttle: function(func, wait) {
+                var context, args, timeout, result, previous, later;
+                previous = 0;
+                later = function() {
+                    previous = new Date();
+                    timeout = null;
+                    result = func.apply(context, args);
+                };
+                return function() {
+                    var now = new Date(), remaining = wait - (now - previous);
+                    context = this;
+                    args = arguments;
+                    if (remaining <= 0) {
+                        clearTimeout(timeout);
+                        timeout = null;
+                        previous = now;
+                        result = func.apply(context, args);
+                    } else if (!timeout) {
+                        timeout = setTimeout(later, remaining);
+                    }
+                    return result;
+                };
+            },
+            noop: function() {}
+        };
+    }();
+    var VERSION = "0.10.5";
+    var tokenizers = function() {
+        "use strict";
+        return {
+            nonword: nonword,
+            whitespace: whitespace,
+            obj: {
+                nonword: getObjTokenizer(nonword),
+                whitespace: getObjTokenizer(whitespace)
+            }
+        };
+        function whitespace(str) {
+            str = _.toStr(str);
+            return str ? str.split(/\s+/) : [];
+        }
+        function nonword(str) {
+            str = _.toStr(str);
+            return str ? str.split(/\W+/) : [];
+        }
+        function getObjTokenizer(tokenizer) {
+            return function setKey() {
+                var args = [].slice.call(arguments, 0);
+                return function tokenize(o) {
+                    var tokens = [];
+                    _.each(args, function(k) {
+                        tokens = tokens.concat(tokenizer(_.toStr(o[k])));
+                    });
+                    return tokens;
+                };
+            };
+        }
+    }();
+    var LruCache = function() {
+        "use strict";
+        function LruCache(maxSize) {
+            this.maxSize = _.isNumber(maxSize) ? maxSize : 100;
+            this.reset();
+            if (this.maxSize <= 0) {
+                this.set = this.get = $.noop;
+            }
+        }
+        _.mixin(LruCache.prototype, {
+            set: function set(key, val) {
+                var tailItem = this.list.tail, node;
+                if (this.size >= this.maxSize) {
+                    this.list.remove(tailItem);
+                    delete this.hash[tailItem.key];
+                }
+                if (node = this.hash[key]) {
+                    node.val = val;
+                    this.list.moveToFront(node);
+                } else {
+                    node = new Node(key, val);
+                    this.list.add(node);
+                    this.hash[key] = node;
+                    this.size++;
+                }
+            },
+            get: function get(key) {
+                var node = this.hash[key];
+                if (node) {
+                    this.list.moveToFront(node);
+                    return node.val;
+                }
+            },
+            reset: function reset() {
+                this.size = 0;
+                this.hash = {};
+                this.list = new List();
+            }
+        });
+        function List() {
+            this.head = this.tail = null;
+        }
+        _.mixin(List.prototype, {
+            add: function add(node) {
+                if (this.head) {
+                    node.next = this.head;
+                    this.head.prev = node;
+                }
+                this.head = node;
+                this.tail = this.tail || node;
+            },
+            remove: function remove(node) {
+                node.prev ? node.prev.next = node.next : this.head = node.next;
+                node.next ? node.next.prev = node.prev : this.tail = node.prev;
+            },
+            moveToFront: function(node) {
+                this.remove(node);
+                this.add(node);
+            }
+        });
+        function Node(key, val) {
+            this.key = key;
+            this.val = val;
+            this.prev = this.next = null;
+        }
+        return LruCache;
+    }();
+    var PersistentStorage = function() {
+        "use strict";
+        var ls, methods;
+        try {
+            ls = window.localStorage;
+            ls.setItem("~~~", "!");
+            ls.removeItem("~~~");
+        } catch (err) {
+            ls = null;
+        }
+        function PersistentStorage(namespace) {
+            this.prefix = [ "__", namespace, "__" ].join("");
+            this.ttlKey = "__ttl__";
+            this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix));
+        }
+        if (ls && window.JSON) {
+            methods = {
+                _prefix: function(key) {
+                    return this.prefix + key;
+                },
+                _ttlKey: function(key) {
+                    return this._prefix(key) + this.ttlKey;
+                },
+                get: function(key) {
+                    if (this.isExpired(key)) {
+                        this.remove(key);
+                    }
+                    return decode(ls.getItem(this._prefix(key)));
+                },
+                set: function(key, val, ttl) {
+                    if (_.isNumber(ttl)) {
+                        ls.setItem(this._ttlKey(key), encode(now() + ttl));
+                    } else {
+                        ls.removeItem(this._ttlKey(key));
+                    }
+                    return ls.setItem(this._prefix(key), encode(val));
+                },
+                remove: function(key) {
+                    ls.removeItem(this._ttlKey(key));
+                    ls.removeItem(this._prefix(key));
+                    return this;
+                },
+                clear: function() {
+                    var i, key, keys = [], len = ls.length;
+                    for (i = 0; i < len; i++) {
+                        if ((key = ls.key(i)).match(this.keyMatcher)) {
+                            keys.push(key.replace(this.keyMatcher, ""));
+                        }
+                    }
+                    for (i = keys.length; i--; ) {
+                        this.remove(keys[i]);
+                    }
+                    return this;
+                },
+                isExpired: function(key) {
+                    var ttl = decode(ls.getItem(this._ttlKey(key)));
+                    return _.isNumber(ttl) && now() > ttl ? true : false;
+                }
+            };
+        } else {
+            methods = {
+                get: _.noop,
+                set: _.noop,
+                remove: _.noop,
+                clear: _.noop,
+                isExpired: _.noop
+            };
+        }
+        _.mixin(PersistentStorage.prototype, methods);
+        return PersistentStorage;
+        function now() {
+            return new Date().getTime();
+        }
+        function encode(val) {
+            return JSON.stringify(_.isUndefined(val) ? null : val);
+        }
+        function decode(val) {
+            return JSON.parse(val);
+        }
+    }();
+    var Transport = function() {
+        "use strict";
+        var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10);
+        function Transport(o) {
+            o = o || {};
+            this.cancelled = false;
+            this.lastUrl = null;
+            this._send = o.transport ? callbackToDeferred(o.transport) : $.ajax;
+            this._get = o.rateLimiter ? o.rateLimiter(this._get) : this._get;
+            this._cache = o.cache === false ? new LruCache(0) : sharedCache;
+        }
+        Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
+            maxPendingRequests = num;
+        };
+        Transport.resetCache = function resetCache() {
+            sharedCache.reset();
+        };
+        _.mixin(Transport.prototype, {
+            _get: function(url, o, cb) {
+                var that = this, jqXhr;
+                if (this.cancelled || url !== this.lastUrl) {
+                    return;
+                }
+                if (jqXhr = pendingRequests[url]) {
+                    jqXhr.done(done).fail(fail);
+                } else if (pendingRequestsCount < maxPendingRequests) {
+                    pendingRequestsCount++;
+                    pendingRequests[url] = this._send(url, o).done(done).fail(fail).always(always);
+                } else {
+                    this.onDeckRequestArgs = [].slice.call(arguments, 0);
+                }
+                function done(resp) {
+                    cb && cb(null, resp);
+                    that._cache.set(url, resp);
+                }
+                function fail() {
+                    cb && cb(true);
+                }
+                function always() {
+                    pendingRequestsCount--;
+                    delete pendingRequests[url];
+                    if (that.onDeckRequestArgs) {
+                        that._get.apply(that, that.onDeckRequestArgs);
+                        that.onDeckRequestArgs = null;
+                    }
+                }
+            },
+            get: function(url, o, cb) {
+                var resp;
+                if (_.isFunction(o)) {
+                    cb = o;
+                    o = {};
+                }
+                this.cancelled = false;
+                this.lastUrl = url;
+                if (resp = this._cache.get(url)) {
+                    _.defer(function() {
+                        cb && cb(null, resp);
+                    });
+                } else {
+                    this._get(url, o, cb);
+                }
+                return !!resp;
+            },
+            cancel: function() {
+                this.cancelled = true;
+            }
+        });
+        return Transport;
+        function callbackToDeferred(fn) {
+            return function customSendWrapper(url, o) {
+                var deferred = $.Deferred();
+                fn(url, o, onSuccess, onError);
+                return deferred;
+                function onSuccess(resp) {
+                    _.defer(function() {
+                        deferred.resolve(resp);
+                    });
+                }
+                function onError(err) {
+                    _.defer(function() {
+                        deferred.reject(err);
+                    });
+                }
+            };
+        }
+    }();
+    var SearchIndex = function() {
+        "use strict";
+        function SearchIndex(o) {
+            o = o || {};
+            if (!o.datumTokenizer || !o.queryTokenizer) {
+                $.error("datumTokenizer and queryTokenizer are both required");
+            }
+            this.datumTokenizer = o.datumTokenizer;
+            this.queryTokenizer = o.queryTokenizer;
+            this.reset();
+        }
+        _.mixin(SearchIndex.prototype, {
+            bootstrap: function bootstrap(o) {
+                this.datums = o.datums;
+                this.trie = o.trie;
+            },
+            add: function(data) {
+                var that = this;
+                data = _.isArray(data) ? data : [ data ];
+                _.each(data, function(datum) {
+                    var id, tokens;
+                    id = that.datums.push(datum) - 1;
+                    tokens = normalizeTokens(that.datumTokenizer(datum));
+                    _.each(tokens, function(token) {
+                        var node, chars, ch;
+                        node = that.trie;
+                        chars = token.split("");
+                        while (ch = chars.shift()) {
+                            node = node.children[ch] || (node.children[ch] = newNode());
+                            node.ids.push(id);
+                        }
+                    });
+                });
+            },
+            get: function get(query) {
+                var that = this, tokens, matches;
+                tokens = normalizeTokens(this.queryTokenizer(query));
+                _.each(tokens, function(token) {
+                    var node, chars, ch, ids;
+                    if (matches && matches.length === 0) {
+                        return false;
+                    }
+                    node = that.trie;
+                    chars = token.split("");
+                    while (node && (ch = chars.shift())) {
+                        node = node.children[ch];
+                    }
+                    if (node && chars.length === 0) {
+                        ids = node.ids.slice(0);
+                        matches = matches ? getIntersection(matches, ids) : ids;
+                    } else {
+                        matches = [];
+                        return false;
+                    }
+                });
+                return matches ? _.map(unique(matches), function(id) {
+                    return that.datums[id];
+                }) : [];
+            },
+            reset: function reset() {
+                this.datums = [];
+                this.trie = newNode();
+            },
+            serialize: function serialize() {
+                return {
+                    datums: this.datums,
+                    trie: this.trie
+                };
+            }
+        });
+        return SearchIndex;
+        function normalizeTokens(tokens) {
+            tokens = _.filter(tokens, function(token) {
+                return !!token;
+            });
+            tokens = _.map(tokens, function(token) {
+                return token.toLowerCase();
+            });
+            return tokens;
+        }
+        function newNode() {
+            return {
+                ids: [],
+                children: {}
+            };
+        }
+        function unique(array) {
+            var seen = {}, uniques = [];
+            for (var i = 0, len = array.length; i < len; i++) {
+                if (!seen[array[i]]) {
+                    seen[array[i]] = true;
+                    uniques.push(array[i]);
+                }
+            }
+            return uniques;
+        }
+        function getIntersection(arrayA, arrayB) {
+            var ai = 0, bi = 0, intersection = [];
+            arrayA = arrayA.sort(compare);
+            arrayB = arrayB.sort(compare);
+            var lenArrayA = arrayA.length, lenArrayB = arrayB.length;
+            while (ai < lenArrayA && bi < lenArrayB) {
+                if (arrayA[ai] < arrayB[bi]) {
+                    ai++;
+                } else if (arrayA[ai] > arrayB[bi]) {
+                    bi++;
+                } else {
+                    intersection.push(arrayA[ai]);
+                    ai++;
+                    bi++;
+                }
+            }
+            return intersection;
+            function compare(a, b) {
+                return a - b;
+            }
+        }
+    }();
+    var oParser = function() {
+        "use strict";
+        return {
+            local: getLocal,
+            prefetch: getPrefetch,
+            remote: getRemote
+        };
+        function getLocal(o) {
+            return o.local || null;
+        }
+        function getPrefetch(o) {
+            var prefetch, defaults;
+            defaults = {
+                url: null,
+                thumbprint: "",
+                ttl: 24 * 60 * 60 * 1e3,
+                filter: null,
+                ajax: {}
+            };
+            if (prefetch = o.prefetch || null) {
+                prefetch = _.isString(prefetch) ? {
+                    url: prefetch
+                } : prefetch;
+                prefetch = _.mixin(defaults, prefetch);
+                prefetch.thumbprint = VERSION + prefetch.thumbprint;
+                prefetch.ajax.type = prefetch.ajax.type || "GET";
+                prefetch.ajax.dataType = prefetch.ajax.dataType || "json";
+                !prefetch.url && $.error("prefetch requires url to be set");
+            }
+            return prefetch;
+        }
+        function getRemote(o) {
+            var remote, defaults;
+            defaults = {
+                url: null,
+                cache: true,
+                wildcard: "%QUERY",
+                replace: null,
+                rateLimitBy: "debounce",
+                rateLimitWait: 300,
+                send: null,
+                filter: null,
+                ajax: {}
+            };
+            if (remote = o.remote || null) {
+                remote = _.isString(remote) ? {
+                    url: remote
+                } : remote;
+                remote = _.mixin(defaults, remote);
+                remote.rateLimiter = /^throttle$/i.test(remote.rateLimitBy) ? byThrottle(remote.rateLimitWait) : byDebounce(remote.rateLimitWait);
+                remote.ajax.type = remote.ajax.type || "GET";
+                remote.ajax.dataType = remote.ajax.dataType || "json";
+                delete remote.rateLimitBy;
+                delete remote.rateLimitWait;
+                !remote.url && $.error("remote requires url to be set");
+            }
+            return remote;
+            function byDebounce(wait) {
+                return function(fn) {
+                    return _.debounce(fn, wait);
+                };
+            }
+            function byThrottle(wait) {
+                return function(fn) {
+                    return _.throttle(fn, wait);
+                };
+            }
+        }
+    }();
+    (function(root) {
+        "use strict";
+        var old, keys;
+        old = root.Bloodhound;
+        keys = {
+            data: "data",
+            protocol: "protocol",
+            thumbprint: "thumbprint"
+        };
+        root.Bloodhound = Bloodhound;
+        function Bloodhound(o) {
+            if (!o || !o.local && !o.prefetch && !o.remote) {
+                $.error("one of local, prefetch, or remote is required");
+            }
+            this.limit = o.limit || 5;
+            this.sorter = getSorter(o.sorter);
+            this.dupDetector = o.dupDetector || ignoreDuplicates;
+            this.local = oParser.local(o);
+            this.prefetch = oParser.prefetch(o);
+            this.remote = oParser.remote(o);
+            this.cacheKey = this.prefetch ? this.prefetch.cacheKey || this.prefetch.url : null;
+            this.index = new SearchIndex({
+                datumTokenizer: o.datumTokenizer,
+                queryTokenizer: o.queryTokenizer
+            });
+            this.storage = this.cacheKey ? new PersistentStorage(this.cacheKey) : null;
+        }
+        Bloodhound.noConflict = function noConflict() {
+            root.Bloodhound = old;
+            return Bloodhound;
+        };
+        Bloodhound.tokenizers = tokenizers;
+        _.mixin(Bloodhound.prototype, {
+            _loadPrefetch: function loadPrefetch(o) {
+                var that = this, serialized, deferred;
+                if (serialized = this._readFromStorage(o.thumbprint)) {
+                    this.index.bootstrap(serialized);
+                    deferred = $.Deferred().resolve();
+                } else {
+                    deferred = $.ajax(o.url, o.ajax).done(handlePrefetchResponse);
+                }
+                return deferred;
+                function handlePrefetchResponse(resp) {
+                    that.clear();
+                    that.add(o.filter ? o.filter(resp) : resp);
+                    that._saveToStorage(that.index.serialize(), o.thumbprint, o.ttl);
+                }
+            },
+            _getFromRemote: function getFromRemote(query, cb) {
+                var that = this, url, uriEncodedQuery;
+                if (!this.transport) {
+                    return;
+                }
+                query = query || "";
+                uriEncodedQuery = encodeURIComponent(query);
+                url = this.remote.replace ? this.remote.replace(this.remote.url, query) : this.remote.url.replace(this.remote.wildcard, uriEncodedQuery);
+                return this.transport.get(url, this.remote.ajax, handleRemoteResponse);
+                function handleRemoteResponse(err, resp) {
+                    err ? cb([]) : cb(that.remote.filter ? that.remote.filter(resp) : resp);
+                }
+            },
+            _cancelLastRemoteRequest: function cancelLastRemoteRequest() {
+                this.transport && this.transport.cancel();
+            },
+            _saveToStorage: function saveToStorage(data, thumbprint, ttl) {
+                if (this.storage) {
+                    this.storage.set(keys.data, data, ttl);
+                    this.storage.set(keys.protocol, location.protocol, ttl);
+                    this.storage.set(keys.thumbprint, thumbprint, ttl);
+                }
+            },
+            _readFromStorage: function readFromStorage(thumbprint) {
+                var stored = {}, isExpired;
+                if (this.storage) {
+                    stored.data = this.storage.get(keys.data);
+                    stored.protocol = this.storage.get(keys.protocol);
+                    stored.thumbprint = this.storage.get(keys.thumbprint);
+                }
+                isExpired = stored.thumbprint !== thumbprint || stored.protocol !== location.protocol;
+                return stored.data && !isExpired ? stored.data : null;
+            },
+            _initialize: function initialize() {
+                var that = this, local = this.local, deferred;
+                deferred = this.prefetch ? this._loadPrefetch(this.prefetch) : $.Deferred().resolve();
+                local && deferred.done(addLocalToIndex);
+                this.transport = this.remote ? new Transport(this.remote) : null;
+                return this.initPromise = deferred.promise();
+                function addLocalToIndex() {
+                    that.add(_.isFunction(local) ? local() : local);
+                }
+            },
+            initialize: function initialize(force) {
+                return !this.initPromise || force ? this._initialize() : this.initPromise;
+            },
+            add: function add(data) {
+                this.index.add(data);
+            },
+            get: function get(query, cb) {
+                var that = this, matches = [], cacheHit = false;
+                matches = this.index.get(query);
+                matches = this.sorter(matches).slice(0, this.limit);
+                matches.length < this.limit ? cacheHit = this._getFromRemote(query, returnRemoteMatches) : this._cancelLastRemoteRequest();
+                if (!cacheHit) {
+                    (matches.length > 0 || !this.transport) && cb && cb(matches);
+                }
+                function returnRemoteMatches(remoteMatches) {
+                    var matchesWithBackfill = matches.slice(0);
+                    _.each(remoteMatches, function(remoteMatch) {
+                        var isDuplicate;
+                        isDuplicate = _.some(matchesWithBackfill, function(match) {
+                            return that.dupDetector(remoteMatch, match);
+                        });
+                        !isDuplicate && matchesWithBackfill.push(remoteMatch);
+                        return matchesWithBackfill.length < that.limit;
+                    });
+                    cb && cb(that.sorter(matchesWithBackfill));
+                }
+            },
+            clear: function clear() {
+                this.index.reset();
+            },
+            clearPrefetchCache: function clearPrefetchCache() {
+                this.storage && this.storage.clear();
+            },
+            clearRemoteCache: function clearRemoteCache() {
+                this.transport && Transport.resetCache();
+            },
+            ttAdapter: function ttAdapter() {
+                return _.bind(this.get, this);
+            }
+        });
+        return Bloodhound;
+        function getSorter(sortFn) {
+            return _.isFunction(sortFn) ? sort : noSort;
+            function sort(array) {
+                return array.sort(sortFn);
+            }
+            function noSort(array) {
+                return array;
+            }
+        }
+        function ignoreDuplicates() {
+            return false;
+        }
+    })(this);
+})(window.jQuery);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/461ac9f1/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/underscore-min.js
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/underscore-min.js b/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/underscore-min.js
new file mode 100644
index 0000000..11f1d96
--- /dev/null
+++ b/usage/cli/src/main/resources/brooklyn/item-lister/statics/style/js/underscore-min.js
@@ -0,0 +1,6 @@
+//     Underscore.js 1.7.0
+//     http://underscorejs.org
+//     (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+//     Underscore may be freely distributed under the MIT license.
+(function(){var n=this,t=n._,r=Array.prototype,e=Object.prototype,u=Function.prototype,i=r.push,a=r.slice,o=r.concat,l=e.toString,c=e.hasOwnProperty,f=Array.isArray,s=Object.keys,p=u.bind,h=function(n){return n instanceof h?n:this instanceof h?void(this._wrapped=n):new h(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=h),exports._=h):n._=h,h.VERSION="1.7.0";var g=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}};h.iteratee=function(n,t,r){return null==n?h.identity:h.isFunction(n)?g(n,t,r):h.isObject(n)?h.matches(n):h.property(n)},h.each=h.forEach=function(n,t,r){if(null==n)return n;t=g(t,r);var e,u=n.length;if(u===+u)for(e=0;u>e;e++)t(n[e],e,n);else{var i=h.keys(n);for(e
 =0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},h.map=h.collect=function(n,t,r){if(null==n)return[];t=h.iteratee(t,r);for(var e,u=n.length!==+n.length&&h.keys(n),i=(u||n).length,a=Array(i),o=0;i>o;o++)e=u?u[o]:o,a[o]=t(n[e],e,n);return a};var v="Reduce of empty array with no initial value";h.reduce=h.foldl=h.inject=function(n,t,r,e){null==n&&(n=[]),t=g(t,e,4);var u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length,o=0;if(arguments.length<3){if(!a)throw new TypeError(v);r=n[i?i[o++]:o++]}for(;a>o;o++)u=i?i[o]:o,r=t(r,n[u],u,n);return r},h.reduceRight=h.foldr=function(n,t,r,e){null==n&&(n=[]),t=g(t,e,4);var u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;if(arguments.length<3){if(!a)throw new TypeError(v);r=n[i?i[--a]:--a]}for(;a--;)u=i?i[a]:a,r=t(r,n[u],u,n);return r},h.find=h.detect=function(n,t,r){var e;return t=h.iteratee(t,r),h.some(n,function(n,r,u){return t(n,r,u)?(e=n,!0):void 0}),e},h.filter=h.select=function(n,t,r){var e=[];return null==n?e:(t=h.iteratee(t,r),h.each(n,
 function(n,r,u){t(n,r,u)&&e.push(n)}),e)},h.reject=function(n,t,r){return h.filter(n,h.negate(h.iteratee(t)),r)},h.every=h.all=function(n,t,r){if(null==n)return!0;t=h.iteratee(t,r);var e,u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;for(e=0;a>e;e++)if(u=i?i[e]:e,!t(n[u],u,n))return!1;return!0},h.some=h.any=function(n,t,r){if(null==n)return!1;t=h.iteratee(t,r);var e,u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;for(e=0;a>e;e++)if(u=i?i[e]:e,t(n[u],u,n))return!0;return!1},h.contains=h.include=function(n,t){return null==n?!1:(n.length!==+n.length&&(n=h.values(n)),h.indexOf(n,t)>=0)},h.invoke=function(n,t){var r=a.call(arguments,2),e=h.isFunction(t);return h.map(n,function(n){return(e?t:n[t]).apply(n,r)})},h.pluck=function(n,t){return h.map(n,h.property(t))},h.where=function(n,t){return h.filter(n,h.matches(t))},h.findWhere=function(n,t){return h.find(n,h.matches(t))},h.max=function(n,t,r){var e,u,i=-1/0,a=-1/0;if(null==t&&null!=n){n=n.length===+n.length?n:h.values(n);for(va
 r o=0,l=n.length;l>o;o++)e=n[o],e>i&&(i=e)}else t=h.iteratee(t,r),h.each(n,function(n,r,e){u=t(n,r,e),(u>a||u===-1/0&&i===-1/0)&&(i=n,a=u)});return i},h.min=function(n,t,r){var e,u,i=1/0,a=1/0;if(null==t&&null!=n){n=n.length===+n.length?n:h.values(n);for(var o=0,l=n.length;l>o;o++)e=n[o],i>e&&(i=e)}else t=h.iteratee(t,r),h.each(n,function(n,r,e){u=t(n,r,e),(a>u||1/0===u&&1/0===i)&&(i=n,a=u)});return i},h.shuffle=function(n){for(var t,r=n&&n.length===+n.length?n:h.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=h.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},h.sample=function(n,t,r){return null==t||r?(n.length!==+n.length&&(n=h.values(n)),n[h.random(n.length-1)]):h.shuffle(n).slice(0,Math.max(0,t))},h.sortBy=function(n,t,r){return t=h.iteratee(t,r),h.pluck(h.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var m=func
 tion(n){return function(t,r,e){var u={};return r=h.iteratee(r,e),h.each(t,function(e,i){var a=r(e,i,t);n(u,e,a)}),u}};h.groupBy=m(function(n,t,r){h.has(n,r)?n[r].push(t):n[r]=[t]}),h.indexBy=m(function(n,t,r){n[r]=t}),h.countBy=m(function(n,t,r){h.has(n,r)?n[r]++:n[r]=1}),h.sortedIndex=function(n,t,r,e){r=h.iteratee(r,e,1);for(var u=r(t),i=0,a=n.length;a>i;){var o=i+a>>>1;r(n[o])<u?i=o+1:a=o}return i},h.toArray=function(n){return n?h.isArray(n)?a.call(n):n.length===+n.length?h.map(n,h.identity):h.values(n):[]},h.size=function(n){return null==n?0:n.length===+n.length?n.length:h.keys(n).length},h.partition=function(n,t,r){t=h.iteratee(t,r);var e=[],u=[];return h.each(n,function(n,r,i){(t(n,r,i)?e:u).push(n)}),[e,u]},h.first=h.head=h.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:0>t?[]:a.call(n,0,t)},h.initial=function(n,t,r){return a.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},h.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:a.call(n,Math.max(n.l
 ength-t,0))},h.rest=h.tail=h.drop=function(n,t,r){return a.call(n,null==t||r?1:t)},h.compact=function(n){return h.filter(n,h.identity)};var y=function(n,t,r,e){if(t&&h.every(n,h.isArray))return o.apply(e,n);for(var u=0,a=n.length;a>u;u++){var l=n[u];h.isArray(l)||h.isArguments(l)?t?i.apply(e,l):y(l,t,r,e):r||e.push(l)}return e};h.flatten=function(n,t){return y(n,t,!1,[])},h.without=function(n){return h.difference(n,a.call(arguments,1))},h.uniq=h.unique=function(n,t,r,e){if(null==n)return[];h.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=h.iteratee(r,e));for(var u=[],i=[],a=0,o=n.length;o>a;a++){var l=n[a];if(t)a&&i===l||u.push(l),i=l;else if(r){var c=r(l,a,n);h.indexOf(i,c)<0&&(i.push(c),u.push(l))}else h.indexOf(u,l)<0&&u.push(l)}return u},h.union=function(){return h.uniq(y(arguments,!0,!0,[]))},h.intersection=function(n){if(null==n)return[];for(var t=[],r=arguments.length,e=0,u=n.length;u>e;e++){var i=n[e];if(!h.contains(t,i)){for(var a=1;r>a&&h.contains(arguments[a],i);a++);a===r&&t.p
 ush(i)}}return t},h.difference=function(n){var t=y(a.call(arguments,1),!0,!0,[]);return h.filter(n,function(n){return!h.contains(t,n)})},h.zip=function(n){if(null==n)return[];for(var t=h.max(arguments,"length").length,r=Array(t),e=0;t>e;e++)r[e]=h.pluck(arguments,e);return r},h.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},h.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=h.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}for(;u>e;e++)if(n[e]===t)return e;return-1},h.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=n.length;for("number"==typeof r&&(e=0>r?e+r+1:Math.min(e,r+1));--e>=0;)if(n[e]===t)return e;return-1},h.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=r||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=Array(e),i=0;e>i;i++,n+=r)u[i]=n;return u};var d=function(){};h.bind=function(n,t){var r,e;if(p&&n.bind===p)return p.apply(n,a.c
 all(arguments,1));if(!h.isFunction(n))throw new TypeError("Bind must be called on a function");return r=a.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(a.call(arguments)));d.prototype=n.prototype;var u=new d;d.prototype=null;var i=n.apply(u,r.concat(a.call(arguments)));return h.isObject(i)?i:u}},h.partial=function(n){var t=a.call(arguments,1);return function(){for(var r=0,e=t.slice(),u=0,i=e.length;i>u;u++)e[u]===h&&(e[u]=arguments[r++]);for(;r<arguments.length;)e.push(arguments[r++]);return n.apply(this,e)}},h.bindAll=function(n){var t,r,e=arguments.length;if(1>=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=h.bind(n[r],n);return n},h.memoize=function(n,t){var r=function(e){var u=r.cache,i=t?t.apply(this,arguments):e;return h.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},h.delay=function(n,t){var r=a.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},h.de
 fer=function(n){return h.delay.apply(h,[n,1].concat(a.call(arguments,1)))},h.throttle=function(n,t,r){var e,u,i,a=null,o=0;r||(r={});var l=function(){o=r.leading===!1?0:h.now(),a=null,i=n.apply(e,u),a||(e=u=null)};return function(){var c=h.now();o||r.leading!==!1||(o=c);var f=t-(c-o);return e=this,u=arguments,0>=f||f>t?(clearTimeout(a),a=null,o=c,i=n.apply(e,u),a||(e=u=null)):a||r.trailing===!1||(a=setTimeout(l,f)),i}},h.debounce=function(n,t,r){var e,u,i,a,o,l=function(){var c=h.now()-a;t>c&&c>0?e=setTimeout(l,t-c):(e=null,r||(o=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,a=h.now();var c=r&&!e;return e||(e=setTimeout(l,t)),c&&(o=n.apply(i,u),i=u=null),o}},h.wrap=function(n,t){return h.partial(t,n)},h.negate=function(n){return function(){return!n.apply(this,arguments)}},h.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},h.after=function(n,t){return function(){return--
 n<1?t.apply(this,arguments):void 0}},h.before=function(n,t){var r;return function(){return--n>0?r=t.apply(this,arguments):t=null,r}},h.once=h.partial(h.before,2),h.keys=function(n){if(!h.isObject(n))return[];if(s)return s(n);var t=[];for(var r in n)h.has(n,r)&&t.push(r);return t},h.values=function(n){for(var t=h.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},h.pairs=function(n){for(var t=h.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},h.invert=function(n){for(var t={},r=h.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},h.functions=h.methods=function(n){var t=[];for(var r in n)h.isFunction(n[r])&&t.push(r);return t.sort()},h.extend=function(n){if(!h.isObject(n))return n;for(var t,r,e=1,u=arguments.length;u>e;e++){t=arguments[e];for(r in t)c.call(t,r)&&(n[r]=t[r])}return n},h.pick=function(n,t,r){var e,u={};if(null==n)return u;if(h.isFunction(t)){t=g(t,r);for(e in n){var i=n[e];t(i,e,n)&&(u[e]=i)}}else{var l=o.apply([],a.call(argume
 nts,1));n=new Object(n);for(var c=0,f=l.length;f>c;c++)e=l[c],e in n&&(u[e]=n[e])}return u},h.omit=function(n,t,r){if(h.isFunction(t))t=h.negate(t);else{var e=h.map(o.apply([],a.call(arguments,1)),String);t=function(n,t){return!h.contains(e,t)}}return h.pick(n,t,r)},h.defaults=function(n){if(!h.isObject(n))return n;for(var t=1,r=arguments.length;r>t;t++){var e=arguments[t];for(var u in e)n[u]===void 0&&(n[u]=e[u])}return n},h.clone=function(n){return h.isObject(n)?h.isArray(n)?n.slice():h.extend({},n):n},h.tap=function(n,t){return t(n),n};var b=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof h&&(n=n._wrapped),t instanceof h&&(t=t._wrapped);var u=l.call(n);if(u!==l.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i
 =r.length;i--;)if(r[i]===n)return e[i]===t;var a=n.constructor,o=t.constructor;if(a!==o&&"constructor"in n&&"constructor"in t&&!(h.isFunction(a)&&a instanceof a&&h.isFunction(o)&&o instanceof o))return!1;r.push(n),e.push(t);var c,f;if("[object Array]"===u){if(c=n.length,f=c===t.length)for(;c--&&(f=b(n[c],t[c],r,e)););}else{var s,p=h.keys(n);if(c=p.length,f=h.keys(t).length===c)for(;c--&&(s=p[c],f=h.has(t,s)&&b(n[s],t[s],r,e)););}return r.pop(),e.pop(),f};h.isEqual=function(n,t){return b(n,t,[],[])},h.isEmpty=function(n){if(null==n)return!0;if(h.isArray(n)||h.isString(n)||h.isArguments(n))return 0===n.length;for(var t in n)if(h.has(n,t))return!1;return!0},h.isElement=function(n){return!(!n||1!==n.nodeType)},h.isArray=f||function(n){return"[object Array]"===l.call(n)},h.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},h.each(["Arguments","Function","String","Number","Date","RegExp"],function(n){h["is"+n]=function(t){return l.call(t)==="[object "+n+"]"}}),h.
 isArguments(arguments)||(h.isArguments=function(n){return h.has(n,"callee")}),"function"!=typeof/./&&(h.isFunction=function(n){return"function"==typeof n||!1}),h.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},h.isNaN=function(n){return h.isNumber(n)&&n!==+n},h.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===l.call(n)},h.isNull=function(n){return null===n},h.isUndefined=function(n){return n===void 0},h.has=function(n,t){return null!=n&&c.call(n,t)},h.noConflict=function(){return n._=t,this},h.identity=function(n){return n},h.constant=function(n){return function(){return n}},h.noop=function(){},h.property=function(n){return function(t){return t[n]}},h.matches=function(n){var t=h.pairs(n),r=t.length;return function(n){if(null==n)return!r;n=new Object(n);for(var e=0;r>e;e++){var u=t[e],i=u[0];if(u[1]!==n[i]||!(i in n))return!1}return!0}},h.times=function(n,t,r){var e=Array(Math.max(0,n));t=g(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},h.random=funct
 ion(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},h.now=Date.now||function(){return(new Date).getTime()};var _={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},w=h.invert(_),j=function(n){var t=function(t){return n[t]},r="(?:"+h.keys(n).join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};h.escape=j(_),h.unescape=j(w),h.result=function(n,t){if(null==n)return void 0;var r=n[t];return h.isFunction(r)?n[t]():r};var x=0;h.uniqueId=function(n){var t=++x+"";return n?n+t:t},h.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var A=/(.)^/,k={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},O=/\\|'|\r|\n|\u2028|\u2029/g,F=function(n){return"\\"+k[n]};h.template=function(n,t,r){!t&&r&&(t=r),t=h.defaults({},t,h.templateSettings);var e=RegExp([(t.escape||A).source,(t.interpolate||A).source,(t.evaluate||A).source]
 .join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,a,o){return i+=n.slice(u,o).replace(O,F),u=o+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":a&&(i+="';\n"+a+"\n__p+='"),t}),i+="';\n",t.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var a=new Function(t.variable||"obj","_",i)}catch(o){throw o.source=i,o}var l=function(n){return a.call(this,n,h)},c=t.variable||"obj";return l.source="function("+c+"){\n"+i+"}",l},h.chain=function(n){var t=h(n);return t._chain=!0,t};var E=function(n){return this._chain?h(n).chain():n};h.mixin=function(n){h.each(h.functions(n),function(t){var r=h[t]=n[t];h.prototype[t]=function(){var n=[this._wrapped];return i.apply(n,arguments),E.call(this,r.apply(h,n))}})},h.mixin(h),h.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=r[n];h.prototype[n]=
 function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],E.call(this,r)}}),h.each(["concat","join","slice"],function(n){var t=r[n];h.prototype[n]=function(){return E.call(this,t.apply(this._wrapped,arguments))}}),h.prototype.value=function(){return this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return h})}).call(this);
+//# sourceMappingURL=underscore-min.map
\ No newline at end of file


[20/23] incubator-brooklyn git commit: add comment about mkdir, as per code review

Posted by he...@apache.org.
add comment about mkdir, as per code review


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

Branch: refs/heads/master
Commit: 17497370f58fe156f5ba2ed5829e9fc019a25117
Parents: 5236675
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri May 22 10:02:14 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 22 10:02:14 2015 +0100

----------------------------------------------------------------------
 .../java/brooklyn/entity/basic/EmptySoftwareProcessSshDriver.java  | 2 ++
 1 file changed, 2 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/17497370/software/base/src/main/java/brooklyn/entity/basic/EmptySoftwareProcessSshDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/EmptySoftwareProcessSshDriver.java b/software/base/src/main/java/brooklyn/entity/basic/EmptySoftwareProcessSshDriver.java
index f7d1d3d..dbafa65 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/EmptySoftwareProcessSshDriver.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/EmptySoftwareProcessSshDriver.java
@@ -48,6 +48,7 @@ public class EmptySoftwareProcessSshDriver extends AbstractSoftwareProcessSshDri
         Map<String, String> installFiles = entity.getConfig(SoftwareProcess.INSTALL_FILES);
         Map<String, String> installTemplates = entity.getConfig(SoftwareProcess.INSTALL_TEMPLATES);
         if ((installFiles!=null && !installFiles.isEmpty()) || (installTemplates!=null && !installTemplates.isEmpty())) {
+            // only do this if there are files, to prevent unnecessary `mkdir`
             super.copyInstallResources();
         }
     }
@@ -57,6 +58,7 @@ public class EmptySoftwareProcessSshDriver extends AbstractSoftwareProcessSshDri
         Map<String, String> runtimeFiles = entity.getConfig(SoftwareProcess.RUNTIME_FILES);
         Map<String, String> runtimeTemplates = entity.getConfig(SoftwareProcess.RUNTIME_TEMPLATES);
         if ((runtimeFiles!=null && !runtimeFiles.isEmpty()) || (runtimeTemplates!=null && !runtimeTemplates.isEmpty())) {
+            // only do this if there are files, to prevent unnecessary `mkdir`
             super.copyRuntimeResources();
         }        
     }


[13/23] incubator-brooklyn git commit: support `domainName` as a location config property (softlayer only)

Posted by he...@apache.org.
support `domainName` as a location config property (softlayer only)


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

Branch: refs/heads/master
Commit: b4518dd5c1e6075a2f243e0f790f0be6f0a55773
Parents: 3ed4459
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri May 8 17:19:19 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:51:50 2015 +0100

----------------------------------------------------------------------
 .../java/brooklyn/location/cloud/CloudLocationConfig.java    | 3 +++
 docs/guide/ops/locations/index.md                            | 3 +++
 .../main/java/brooklyn/location/jclouds/JcloudsLocation.java | 8 ++++++++
 3 files changed, 14 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4518dd5/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java b/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
index 39ef6fa..6831726 100644
--- a/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
+++ b/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
@@ -98,4 +98,7 @@ public interface CloudLocationConfig {
     public static final ConfigKey<Object> MIN_DISK = new BasicConfigKey<Object>(Object.class, "minDisk",
         "Minimum size of disk, either as string (100gb) or number of GB (100), for use in selecting the machine/hardware profile", null);
 
+    public static final ConfigKey<String> DOMAIN_NAME = new BasicConfigKey<String>(String.class, "domainName",
+        "DNS domain where the host should be created, e.g. yourdomain.com (selected clouds only)", null);
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4518dd5/docs/guide/ops/locations/index.md
----------------------------------------------------------------------
diff --git a/docs/guide/ops/locations/index.md b/docs/guide/ops/locations/index.md
index 3c1f6b8..02e82bb 100644
--- a/docs/guide/ops/locations/index.md
+++ b/docs/guide/ops/locations/index.md
@@ -139,6 +139,9 @@ For more keys and more detail on the keys below, see
   For all names, a random suffix will be appended to help guarantee uniqueness;
   this can be removed by setting `vmNameSaltLength: 0`.
   
+- A DNS domain name where this host should be placed can be specified with `domainName`
+  (in selected clouds only)
+
 - User metadata can be attached using the syntax `userMetadata: { key: value, key2: "value 2" }` 
   (or `userMetadata=key=value,key2="value 2"` in a properties file)
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b4518dd5/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
index 1118409..c74cfe2 100644
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
+++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
@@ -1065,6 +1065,14 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
                     public void apply(TemplateOptions t, ConfigBag props, Object v) {
                         t.networks((String)v);
                     }})
+              .put(DOMAIN_NAME, new CustomizeTemplateOptions() {
+                    public void apply(TemplateOptions t, ConfigBag props, Object v) {
+                        if (t instanceof SoftLayerTemplateOptions) {
+                            ((SoftLayerTemplateOptions)t).domainName((String)v);
+                        } else {
+                            LOG.info("ignoring domain-name({}) in VM creation because not supported for cloud/type ({})", v, t);                            
+                        }
+                    }})
               .put(TEMPLATE_OPTIONS, new CustomizeTemplateOptions() {
                   @Override
                   public void apply(TemplateOptions options, ConfigBag config, Object v) {


[19/23] incubator-brooklyn git commit: ssh command sensor allows configuring the dir where it runs

Posted by he...@apache.org.
ssh command sensor allows configuring the dir where it runs

fixes bug if ssh sensor used with empty software process entity, where run dir isn't yet created


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

Branch: refs/heads/master
Commit: 44273ad5042dc11a9d1dee07df264f7c03a88144
Parents: 776ad43
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu May 21 23:42:12 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu May 21 23:50:34 2015 +0100

----------------------------------------------------------------------
 .../entity/software/ssh/SshCommandEffector.java |  6 ++--
 .../entity/software/ssh/SshCommandSensor.java   | 37 ++++++++++++++++----
 2 files changed, 35 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44273ad5/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java
index 4cc1b5c..200d0ce 100644
--- a/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java
+++ b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java
@@ -40,6 +40,7 @@ import com.google.common.base.Preconditions;
 public final class SshCommandEffector extends AddEffector {
     
     public static final ConfigKey<String> EFFECTOR_COMMAND = ConfigKeys.newStringConfigKey("command");
+    public static final ConfigKey<String> EFFECTOR_EXECUTION_DIR = SshCommandSensor.SENSOR_EXECUTION_DIR;
     
     public SshCommandEffector(ConfigBag params) {
         super(newEffectorBuilder(params).build());
@@ -59,10 +60,12 @@ public final class SshCommandEffector extends AddEffector {
     protected static class Body extends EffectorBody<String> {
         private final Effector<?> effector;
         private final String command;
+        private final String executionDir;
 
         public Body(Effector<?> eff, ConfigBag params) {
             this.effector = eff;
             this.command = Preconditions.checkNotNull(params.get(EFFECTOR_COMMAND), "command must be supplied when defining this effector");
+            this.executionDir = params.get(EFFECTOR_EXECUTION_DIR);
             // TODO could take a custom "env" aka effectorShellEnv
         }
 
@@ -70,8 +73,7 @@ public final class SshCommandEffector extends AddEffector {
         public String call(ConfigBag params) {
             String command = this.command;
             
-            String runDir = entity().getAttribute(SoftwareProcess.RUN_DIR);
-            if (runDir!=null) command = "cd '"+runDir+"'\n"+command;
+            command = SshCommandSensor.makeCommandExecutingInDirectory(command, executionDir, entity());
             
             MutableMap<String, String> env = MutableMap.of();
             // first set all declared parameters, including default values

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/44273ad5/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java
index 51e2917..2c918f9 100644
--- a/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java
+++ b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java
@@ -37,6 +37,7 @@ import brooklyn.event.feed.ssh.SshValueFunctions;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.flags.TypeCoercions;
+import brooklyn.util.os.Os;
 import brooklyn.util.text.Strings;
 
 import com.google.common.annotations.Beta;
@@ -59,14 +60,20 @@ public final class SshCommandSensor<T> extends AddSensor<T> {
     private static final Logger LOG = LoggerFactory.getLogger(SshCommandSensor.class);
 
     public static final ConfigKey<String> SENSOR_COMMAND = ConfigKeys.newStringConfigKey("command", "SSH command to execute for sensor");
+    public static final ConfigKey<String> SENSOR_EXECUTION_DIR = ConfigKeys.newStringConfigKey("executionDir", "Directory where the command should run; "
+        + "if not supplied, executes in the entity's run dir (or home dir if no run dir is defined); "
+        + "use '~' to always execute in the home dir, or 'custom-feed/' to execute in a custom-feed dir relative to the run dir");
 
     protected final String command;
+    protected final String executionDir;
 
     public SshCommandSensor(final ConfigBag params) {
         super(params);
 
         // TODO create a supplier for the command string to support attribute embedding
         command = Preconditions.checkNotNull(params.get(SENSOR_COMMAND), "command");
+        
+        executionDir = params.get(SENSOR_EXECUTION_DIR);
     }
 
     @Override
@@ -87,12 +94,7 @@ public final class SshCommandSensor<T> extends AddSensor<T> {
         Supplier<String> commandSupplier = new Supplier<String>() {
             @Override
             public String get() {
-                String finalCommand = command;
-                String runDir = entity.getAttribute(SoftwareProcess.RUN_DIR);
-                if (runDir != null) {
-                    finalCommand = "cd '"+runDir+"' && "+finalCommand;
-                }
-                return finalCommand;
+                return makeCommandExecutingInDirectory(command, executionDir, entity);
             }
         };
 
@@ -115,4 +117,27 @@ public final class SshCommandSensor<T> extends AddSensor<T> {
                 .build();
     }
 
+    static String makeCommandExecutingInDirectory(String command, String executionDir, EntityLocal entity) {
+        String finalCommand = command;
+        String execDir = executionDir;
+        if (Strings.isBlank(execDir)) {
+            // default to run dir
+            execDir = entity.getAttribute(SoftwareProcess.RUN_DIR);
+            // if no run dir, default to home
+            if (Strings.isBlank(execDir)) {
+                execDir = "~";
+            }
+        } else if (!Os.isAbsolutish(execDir)) {
+            // relative paths taken wrt run dir
+            String runDir = entity.getAttribute(SoftwareProcess.RUN_DIR);
+            if (!Strings.isBlank(runDir)) {
+                execDir = Os.mergePaths(runDir, execDir);
+            }
+        }
+        if (!"~".equals(execDir)) {
+            finalCommand = "mkdir -p '"+execDir+"' && cd '"+execDir+"' && "+finalCommand;
+        }
+        return finalCommand;
+    }
+
 }


[02/23] incubator-brooklyn git commit: introduce CatalogInitialization to cleanly init catalog at the right times

Posted by he...@apache.org.
introduce CatalogInitialization to cleanly init catalog at the right times

invoked during persistence cycles, and at startup, holding the new CLI catalog options.
this does not yet properly initialize things for real-world use, and tests fixed in next commit.


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

Branch: refs/heads/master
Commit: 0b9bc3b3966543f09eb7fb4c7be7df5abd4e6d4d
Parents: addf30e
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Apr 27 14:38:05 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:22:21 2015 +0100

----------------------------------------------------------------------
 .../java/brooklyn/catalog/CatalogLoadMode.java  |   3 +
 .../brooklyn/catalog/CatalogPredicates.java     |   1 +
 .../catalog/internal/BasicBrooklynCatalog.java  |  38 +-
 .../brooklyn/catalog/internal/CatalogDto.java   |   9 +
 .../catalog/internal/CatalogInitialization.java | 346 +++++++++++++++++++
 .../brooklyn/config/BrooklynServerConfig.java   |  14 +-
 .../brooklyn/entity/rebind/RebindIteration.java |  74 ++--
 .../internal/AbstractManagementContext.java     |  57 ++-
 .../internal/ManagementContextInternal.java     |  19 +-
 .../NonDeploymentManagementContext.java         |  28 ++
 .../main/resources/brooklyn/empty.catalog.bom   |  18 +
 .../catalog/internal/CatalogScanTest.java       |  11 +-
 .../entity/rebind/RebindCatalogEntityTest.java  |  33 +-
 .../entity/rebind/RebindCatalogItemTest.java    |   4 +-
 .../brooklyn/entity/rebind/RebindTestUtils.java |   2 +-
 .../entity/LocalManagementContextForTests.java  |   3 +-
 usage/cli/src/main/java/brooklyn/cli/Main.java  |  29 +-
 .../brooklyn/launcher/BrooklynLauncher.java     |  41 +--
 .../brooklyn/launcher/BrooklynLauncherTest.java |   9 +-
 .../brooklyn/rest/BrooklynRestApiLauncher.java  |   3 +-
 .../BrooklynRestApiLauncherTestFixture.java     |   3 +-
 21 files changed, 594 insertions(+), 151 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/main/java/brooklyn/catalog/CatalogLoadMode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/CatalogLoadMode.java b/core/src/main/java/brooklyn/catalog/CatalogLoadMode.java
index 259f545..6341a10 100644
--- a/core/src/main/java/brooklyn/catalog/CatalogLoadMode.java
+++ b/core/src/main/java/brooklyn/catalog/CatalogLoadMode.java
@@ -20,8 +20,11 @@ package brooklyn.catalog;
 
 import org.slf4j.LoggerFactory;
 
+import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.entity.rebind.persister.PersistMode;
 
+/** @deprecated since 0.7.0 replaced by {@link CatalogInitialization} */
+@Deprecated
 public enum CatalogLoadMode {
     /**
      * The server will load its initial catalog from the URL configured in

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/main/java/brooklyn/catalog/CatalogPredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/CatalogPredicates.java b/core/src/main/java/brooklyn/catalog/CatalogPredicates.java
index 2ffebdf..756ec80 100644
--- a/core/src/main/java/brooklyn/catalog/CatalogPredicates.java
+++ b/core/src/main/java/brooklyn/catalog/CatalogPredicates.java
@@ -72,6 +72,7 @@ public class CatalogPredicates {
         }
     };
 
+    /** @deprecated since 0.7.0 use {@link #displayName(Predicate)} */
     @Deprecated
     public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> name(final Predicate<? super String> filter) {
         return displayName(filter);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
index b49a240..9d68465 100644
--- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
@@ -25,7 +25,6 @@ import io.brooklyn.camp.spi.AssemblyTemplate;
 import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator;
 import io.brooklyn.camp.spi.pdp.DeploymentPlan;
 
-import java.io.FileNotFoundException;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -75,7 +74,6 @@ import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
-import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSortedSet;
@@ -140,6 +138,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         catalog.load(mgmt, null);
         CatalogUtils.logDebugOrTraceIfRebinding(log, "Reloaded catalog for "+this+", now switching");
         this.catalog = catalog;
+        this.manualAdditionsCatalog = null;
 
         // Inject management context into and persist all the new entries.
         for (CatalogItem<?, ?> entry : getCatalogItems()) {
@@ -1102,41 +1101,6 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
             serializer = new CatalogXmlSerializer();
     }
 
-    public void resetCatalogToContentsAtConfiguredUrl() {
-        CatalogDto dto = null;
-        String catalogUrl = mgmt.getConfig().getConfig(BrooklynServerConfig.BROOKLYN_CATALOG_URL);
-        try {
-            if (!Strings.isEmpty(catalogUrl)) {
-                dto = CatalogDto.newDtoFromUrl(catalogUrl);
-                if (log.isDebugEnabled()) {
-                    log.debug("Loading catalog from {}: {}", catalogUrl, catalog);
-                }
-            }
-        } catch (Exception e) {
-            if (Throwables.getRootCause(e) instanceof FileNotFoundException) {
-                Maybe<Object> nonDefaultUrl = mgmt.getConfig().getConfigRaw(BrooklynServerConfig.BROOKLYN_CATALOG_URL, true);
-                if (nonDefaultUrl.isPresentAndNonNull() && !"".equals(nonDefaultUrl.get())) {
-                    log.warn("Could not find catalog XML specified at {}; using default (local classpath) catalog. Error was: {}", nonDefaultUrl, e);
-                } else {
-                    if (log.isDebugEnabled()) {
-                        log.debug("No default catalog file available at {}; trying again using local classpath to populate catalog. Error was: {}", catalogUrl, e);
-                    }
-                }
-            } else {
-                log.warn("Error importing catalog XML at " + catalogUrl + "; using default (local classpath) catalog. Error was: " + e, e);
-            }
-        }
-        if (dto == null) {
-            // retry, either an error, or was blank
-            dto = CatalogDto.newDefaultLocalScanningDto(CatalogClasspathDo.CatalogScanningModes.ANNOTATIONS);
-            if (log.isDebugEnabled()) {
-                log.debug("Loaded default (local classpath) catalog: " + catalog);
-            }
-        }
-
-        reset(dto);
-    }
-
     @Deprecated
     public CatalogItem<?,?> getCatalogItemForType(String typeName) {
         final CatalogItem<?,?> resultI;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java
index 0d98c82..407cb7c 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java
@@ -65,7 +65,15 @@ public class CatalogDto {
         return result.dto;
     }
 
+    /** @deprecated since 0.7.0 use {@link #newDtoFromXmlUrl(String)} if you must, but note the xml format itself is deprecated */
+    @Deprecated
     public static CatalogDto newDtoFromUrl(String url) {
+        return newDtoFromXmlUrl(url);
+    }
+    
+    /** @deprecated since 0.7.0 the xml format is deprecated; use YAML parse routines on BasicBrooklynCatalog */
+    @Deprecated
+    public static CatalogDto newDtoFromXmlUrl(String url) {
         if (LOG.isDebugEnabled()) LOG.debug("Retrieving catalog from: {}", url);
         try {
             InputStream source = ResourceUtils.create().getResourceFromUrl(url);
@@ -77,6 +85,7 @@ public class CatalogDto {
         }
     }
 
+    /** @deprecated since 0.7.0 the xml format is deprecated; use YAML parse routines on BasicBrooklynCatalog */
     public static CatalogDto newDtoFromXmlContents(String xmlContents, String originDescription) {
         CatalogDto result = (CatalogDto) new CatalogXmlSerializer().deserialize(new StringReader(xmlContents));
         result.contentsDescription = originDescription;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java b/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
new file mode 100644
index 0000000..db7343a
--- /dev/null
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
@@ -0,0 +1,346 @@
+/*
+ * 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.catalog.internal;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.catalog.BrooklynCatalog;
+import brooklyn.catalog.CatalogItem;
+import brooklyn.config.BrooklynServerConfig;
+import brooklyn.management.ManagementContext;
+import brooklyn.management.internal.ManagementContextInternal;
+import brooklyn.util.ResourceUtils;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.flags.TypeCoercions;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.net.Urls;
+import brooklyn.util.text.Strings;
+import brooklyn.util.yaml.Yamls;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+
+@Beta
+public class CatalogInitialization {
+
+    /*
+
+    A1) if not persisting, go to B1
+    A2) if --catalog-reset, delete persisted catalog items
+    A3) read persisted catalog items (possibly deleted in A2), go to C1
+    A4) go to B1
+
+    B1) look for --catalog-initial, if so read it, then go to C1
+    B2) look for BrooklynServerConfig.BROOKLYN_CATALOG_URL, if so, read it, supporting YAML or XML (warning if XML), then go to C1
+    B3) look for ~/.brooklyn/catalog.bom, if exists, read it then go to C1
+    B4) look for ~/.brooklyn/brooklyn.xml, if exists, warn, read it then go to C1
+    B5) read all classpath://brooklyn/default.catalog.bom items, if they exist (and for now they will)
+    B6) go to C1
+
+    C1) if --catalog-add, read and add those items
+
+    D1) if persisting, read the rest of the persisted items (entities etc)
+
+     */
+
+    private static final Logger log = LoggerFactory.getLogger(CatalogInitialization.class);
+    
+    String initialUri;
+    boolean reset;
+    String additionsUri;
+    boolean force;
+
+    boolean disallowLocal = false;
+    List<Function<ManagementContext, Void>> callbacks = MutableList.of();
+    AtomicInteger runCount = new AtomicInteger();
+    
+    public CatalogInitialization(String initialUri, boolean reset, String additionUri, boolean force) {
+        this.initialUri = initialUri;
+        this.reset = reset;
+        this.additionsUri = additionUri;
+        this.force = force;
+    }
+    
+    public CatalogInitialization() {
+        this(null, false, null, false);
+    }
+
+    public CatalogInitialization addPopulationCallback(Function<ManagementContext, Void> callback) {
+        callbacks.add(callback);
+        return this;
+    }
+
+    public boolean isInitialResetRequested() {
+        return reset;
+    }
+
+    public int getRunCount() {
+        return runCount.get();
+    }
+    
+    public boolean hasRun() {
+        return getRunCount()>0;
+    }
+
+    /** makes or updates the mgmt catalog, based on the settings in this class */
+    public void populateCatalog(ManagementContext managementContext, boolean needsInitial, Collection<CatalogItem<?, ?>> optionalItemsForResettingCatalog) {
+        try {
+            BasicBrooklynCatalog catalog;
+            Maybe<BrooklynCatalog> cm = ((ManagementContextInternal)managementContext).getCatalogIfSet();
+            if (cm.isAbsent()) {
+                if (hasRun()) {
+                    log.warn("Odd: catalog initialization has run but management context has no catalog; re-creating");
+                }
+                catalog = new BasicBrooklynCatalog(managementContext);
+                setCatalog(managementContext, catalog, "Replacing catalog with newly populated catalog", true);
+            } else {
+                if (!hasRun()) {
+                    log.warn("Odd: catalog initialization has not run but management context has a catalog; re-populating");
+                }
+                catalog = (BasicBrooklynCatalog) cm.get();
+            }
+
+            populateCatalog(managementContext, catalog, needsInitial, true, optionalItemsForResettingCatalog);
+            
+        } finally {
+            runCount.incrementAndGet();
+        }
+    }
+
+    private void populateCatalog(ManagementContext managementContext, BasicBrooklynCatalog catalog, boolean needsInitial, boolean runCallbacks, Collection<CatalogItem<?, ?>> optionalItemsForResettingCatalog) {
+        applyCatalogLoadMode(managementContext);
+        
+        if (optionalItemsForResettingCatalog!=null) {
+            catalog.reset(optionalItemsForResettingCatalog);
+        }
+        
+        if (needsInitial) {
+            populateInitial(catalog, managementContext);
+        }
+        
+        populateAdditions(catalog, managementContext);
+
+        if (runCallbacks) {
+            populateViaCallbacks(catalog, managementContext);
+        }
+    }
+
+    private enum PopulateMode { YAML, XML, AUTODETECT }
+    
+    protected void populateInitial(BasicBrooklynCatalog catalog, ManagementContext managementContext) {
+        if (disallowLocal) {
+            if (!hasRun()) {
+                log.debug("CLI initial catalog not being read with disallow-local mode set.");
+            }
+            return;
+        }
+
+//        B1) look for --catalog-initial, if so read it, then go to C1
+//        B2) look for BrooklynServerConfig.BROOKLYN_CATALOG_URL, if so, read it, supporting YAML or XML (warning if XML), then go to C1
+//        B3) look for ~/.brooklyn/catalog.bom, if exists, read it then go to C1
+//        B4) look for ~/.brooklyn/brooklyn.xml, if exists, warn, read it then go to C1
+//        B5) read all classpath://brooklyn/default.catalog.bom items, if they exist (and for now they will)
+//        B6) go to C1
+
+        if (initialUri!=null) {
+            populateInitialFromUri(catalog, managementContext, initialUri, PopulateMode.AUTODETECT);
+            return;
+        }
+        
+        String catalogUrl = managementContext.getConfig().getConfig(BrooklynServerConfig.BROOKLYN_CATALOG_URL);
+        if (Strings.isNonBlank(catalogUrl)) {
+            populateInitialFromUri(catalog, managementContext, catalogUrl, PopulateMode.AUTODETECT);
+            return;
+        }
+        
+        catalogUrl = Urls.mergePaths(BrooklynServerConfig.getMgmtBaseDir( managementContext.getConfig() ), "catalog.bom");
+        if (new File(catalogUrl).exists()) {
+            populateInitialFromUri(catalog, managementContext, "file:"+catalogUrl, PopulateMode.YAML);
+            return;
+        }
+        
+        catalogUrl = Urls.mergePaths(BrooklynServerConfig.getMgmtBaseDir( managementContext.getConfig() ), "catalog.xml");
+        if (new File(catalogUrl).exists()) {
+            populateInitialFromUri(catalog, managementContext, "file:"+catalogUrl, PopulateMode.XML);
+            return;
+        }
+
+        // TODO scan for default.catalog.bom files and add all of them
+        
+//        // TODO optionally scan for classpath items
+//        // retry, either an error, or was blank
+//        dto = CatalogDto.newDefaultLocalScanningDto(CatalogClasspathDo.CatalogScanningModes.ANNOTATIONS);
+//        if (log.isDebugEnabled()) {
+//            log.debug("Loaded default (local classpath) catalog: " + catalogDo);
+//        }
+        
+        return;
+    }
+    
+    private void populateInitialFromUri(BasicBrooklynCatalog catalog, ManagementContext managementContext, String catalogUrl, PopulateMode mode) {
+        log.debug("Loading initial catalog from {}", catalogUrl);
+
+        Exception problem = null;
+        Object result = null;
+        
+        String contents = null;
+        try {
+            contents = new ResourceUtils(this).getResourceAsString(catalogUrl);
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            if (problem==null) problem = e;
+        }
+
+        if (contents!=null && (mode==PopulateMode.YAML || mode==PopulateMode.AUTODETECT)) {
+            // try YAML first
+            try {
+                catalog.reset(MutableList.<CatalogItem<?,?>>of());
+                result = catalog.addItems(contents);
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                if (problem==null) problem = e;
+            }
+        }
+        
+        if (result==null && contents!=null && (mode==PopulateMode.XML || mode==PopulateMode.AUTODETECT)) {
+            // then try XML
+            CatalogDto dto = null;
+            try {
+                dto = CatalogDto.newDtoFromXmlContents(contents, catalogUrl);
+                problem = null;
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                if (problem==null) problem = e;
+            }
+            if (dto!=null) {
+                catalog.reset(dto);
+            }
+        }
+        
+        if (result!=null) {
+            log.debug("Loaded initial catalog from {}: {}", catalogUrl, result);
+        }
+        if (problem!=null) {
+            log.warn("Error importing catalog from " + catalogUrl + ": " + problem, problem);
+            // TODO inform mgmt of error
+        }
+
+    }
+
+    protected void populateAdditions(BasicBrooklynCatalog catalog, ManagementContext mgmt) {
+        if (Strings.isNonBlank(additionsUri)) {
+            if (disallowLocal) {
+                if (!hasRun()) {
+                    log.warn("CLI additions supplied but not supported in disallow-local mode; ignoring.");
+                }
+                return;
+            }   
+            if (!hasRun()) {
+                log.debug("Adding to catalog from CLI: "+additionsUri+" (force: "+force+")");
+            }
+            Iterable<? extends CatalogItem<?, ?>> items = catalog.addItems(
+                new ResourceUtils(this).getResourceAsString(additionsUri), force);
+            
+            if (!hasRun())
+                log.debug("Added to catalog from CLI: "+items);
+            else
+                log.debug("Added to catalog from CLI: count "+Iterables.size(items));
+        }
+    }
+
+    protected void populateViaCallbacks(BasicBrooklynCatalog catalog, ManagementContext managementContext) {
+        for (Function<ManagementContext, Void> callback: callbacks)
+            callback.apply(managementContext);
+    }
+
+    private boolean setFromCatalogLoadMode = false;
+    /** @deprecated since introduced in 0.7.0, only for legacy compatibility with 
+     * {@link CatalogLoadMode} {@link BrooklynServerConfig#CATALOG_LOAD_MODE},
+     * allowing control of catalog loading from a brooklyn property */
+    @Deprecated
+    public void applyCatalogLoadMode(ManagementContext managementContext) {
+        if (setFromCatalogLoadMode) return;
+        setFromCatalogLoadMode = true;
+        Maybe<Object> clmm = ((ManagementContextInternal)managementContext).getConfig().getConfigRaw(BrooklynServerConfig.CATALOG_LOAD_MODE, false);
+        if (clmm.isAbsent()) return;
+        brooklyn.catalog.CatalogLoadMode clm = TypeCoercions.coerce(clmm.get(), brooklyn.catalog.CatalogLoadMode.class);
+        log.warn("Legacy CatalogLoadMode "+clm+" set: applying, but this should be changed to use new CLI --catalogXxx commands");
+        switch (clm) {
+        case LOAD_BROOKLYN_CATALOG_URL:
+            reset = true;
+            break;
+        case LOAD_BROOKLYN_CATALOG_URL_IF_NO_PERSISTED_STATE:
+            // now the default
+            break;
+        case LOAD_PERSISTED_STATE:
+            disallowLocal = true;
+            break;
+        }
+    }
+
+    /** makes the catalog, warning if persistence is on and hasn't run yet 
+     * (as the catalog will be subsequently replaced) */
+    @Beta
+    public BrooklynCatalog getCatalogPopulatingBestEffort(ManagementContext managementContext) {
+        Maybe<BrooklynCatalog> cm = ((ManagementContextInternal)managementContext).getCatalogIfSet();
+        if (cm.isPresent()) return cm.get();
+
+        BrooklynCatalog oldC = setCatalog(managementContext, new BasicBrooklynCatalog(managementContext),
+            "Request to make local catalog early, but someone else has created it, reverting to that", false);
+        if (oldC==null) {
+            // our catalog was added, so run population
+            // NB: we need the catalog to be saved already so that we can run callbacks
+            populateCatalog(managementContext, (BasicBrooklynCatalog) managementContext.getCatalog(), true, true, null);
+        }
+        
+        return managementContext.getCatalog();
+    }
+
+    /** Sets the catalog in the given management context, warning and choosing appropriately if one already exists. 
+     * Returns any previously existing catalog (whether or not changed). */
+    @Beta
+    public static BrooklynCatalog setCatalog(ManagementContext managementContext, BrooklynCatalog catalog, String messageIfAlready, boolean preferNew) {
+        Maybe<BrooklynCatalog> cm;
+        synchronized (managementContext) {
+            cm = ((ManagementContextInternal)managementContext).getCatalogIfSet();
+            if (cm.isAbsent()) {
+                ((ManagementContextInternal)managementContext).setCatalog(catalog);
+                return null;
+            }
+            if (preferNew) {
+                // set to null first to prevent errors
+                ((ManagementContextInternal)managementContext).setCatalog(null);
+                ((ManagementContextInternal)managementContext).setCatalog(catalog);
+            }
+        }
+        if (Strings.isNonBlank(messageIfAlready)) {
+            log.warn(messageIfAlready);
+        }
+        return cm.get();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/config/BrooklynServerConfig.java b/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
index c5ff294..a1fdd32 100644
--- a/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
+++ b/core/src/main/java/brooklyn/config/BrooklynServerConfig.java
@@ -28,7 +28,7 @@ import java.util.Map;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.catalog.CatalogLoadMode;
+import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.management.ManagementContext;
 import brooklyn.util.guava.Maybe;
@@ -98,14 +98,14 @@ public class BrooklynServerConfig {
             + "this key is DEPRECATED in favor of promotion and demotion specific flags now defaulting to true");
 
     public static final ConfigKey<String> BROOKLYN_CATALOG_URL = ConfigKeys.newStringConfigKey("brooklyn.catalog.url",
-        "The URL of a catalog.xml descriptor; absent for default (~/.brooklyn/catalog.xml), " +
-        "or empty for no URL (use default scanner)",
-        new File(Os.fromHome(".brooklyn/catalog.xml")).toURI().toString());
+        "The URL of a custom catalog.bom or catalog.xml descriptor to load");
 
-    public static final ConfigKey<CatalogLoadMode> CATALOG_LOAD_MODE = ConfigKeys.newConfigKey(CatalogLoadMode.class,
+    /** @deprecated since 0.7.0 replaced by {@link CatalogInitialization}; also note, default removed 
+     * (it was overridden anyway, and in almost all cases the new behaviour is still the default behaviour) */
+    @Deprecated
+    public static final ConfigKey<brooklyn.catalog.CatalogLoadMode> CATALOG_LOAD_MODE = ConfigKeys.newConfigKey(brooklyn.catalog.CatalogLoadMode.class,
             "brooklyn.catalog.mode",
-            "The mode the management context should use to load the catalog when first starting",
-            CatalogLoadMode.LOAD_BROOKLYN_CATALOG_URL);
+            "The mode the management context should use to load the catalog when first starting");
 
     /** string used in places where the management node ID is needed to resolve a path */
     public static final String MANAGEMENT_NODE_ID_PROPERTY = "brooklyn.mgmt.node.id";

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
index 75f36f8..b5af10c 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
@@ -38,12 +38,10 @@ import brooklyn.basic.BrooklynObject;
 import brooklyn.basic.BrooklynObjectInternal;
 import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
-import brooklyn.catalog.CatalogLoadMode;
-import brooklyn.catalog.internal.BasicBrooklynCatalog;
+import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.BrooklynLogging;
 import brooklyn.config.BrooklynLogging.LoggingLevel;
-import brooklyn.config.BrooklynServerConfig;
 import brooklyn.enricher.basic.AbstractEnricher;
 import brooklyn.entity.Application;
 import brooklyn.entity.Entity;
@@ -300,6 +298,7 @@ public abstract class RebindIteration {
         isEmpty = mementoManifest.isEmpty();
     }
 
+    @SuppressWarnings("deprecation")
     protected void rebuildCatalog() {
         
         // build catalog early so we can load other things
@@ -341,38 +340,57 @@ public abstract class RebindIteration {
                 }
             }
         }
+
+        // see notes in CatalogInitialization
         
-        // Register catalogue items with the management context. Loads the bundles in the OSGi framework.
-        CatalogLoadMode catalogLoadMode = managementContext.getConfig().getConfig(BrooklynServerConfig.CATALOG_LOAD_MODE);
+        Collection<CatalogItem<?, ?>> catalogItems = rebindContext.getCatalogItems();
+        CatalogInitialization catInit = ((ManagementContextInternal)managementContext).getCatalogInitialization();
+        catInit.applyCatalogLoadMode(managementContext);
+        Collection<CatalogItem<?,?>> itemsForResettingCatalog = null;
+        boolean needsInitialCatalog;
         if (rebindManager.persistCatalogItemsEnabled) {
-            boolean shouldResetCatalog = catalogLoadMode == CatalogLoadMode.LOAD_PERSISTED_STATE
-                    || (!isEmpty && catalogLoadMode == CatalogLoadMode.LOAD_BROOKLYN_CATALOG_URL_IF_NO_PERSISTED_STATE);
-            boolean shouldLoadDefaultCatalog = catalogLoadMode == CatalogLoadMode.LOAD_BROOKLYN_CATALOG_URL
-                    || (isEmpty && catalogLoadMode == CatalogLoadMode.LOAD_BROOKLYN_CATALOG_URL_IF_NO_PERSISTED_STATE);
-            if (shouldResetCatalog) {
-                // Reset catalog with previously persisted state
-                logRebindingDebug("RebindManager resetting management context catalog to previously persisted state");
-                managementContext.getCatalog().reset(rebindContext.getCatalogItems());
-            } else if (shouldLoadDefaultCatalog) {
-                // Load catalogue as normal
-                // TODO in read-only mode, should do this less frequently than entities etc
-                logRebindingDebug("RebindManager loading default catalog");
-                ((BasicBrooklynCatalog) managementContext.getCatalog()).resetCatalogToContentsAtConfiguredUrl();
+            if (!catInit.hasRun() && catInit.isInitialResetRequested()) {
+                String message = "RebindManager resetting catalog on first run (catalog persistence enabled, but reset explicitly specified). ";
+                if (catalogItems.isEmpty()) {
+                    message += "Catalog was empty anyway.";
+                } else {
+                    message += "Deleting "+catalogItems.size()+" persisted catalog item"+Strings.s(catalogItems)+": "+catalogItems;
+                    if (shouldLogRebinding()) {
+                        LOG.info(message);
+                    }
+                }
+                logRebindingDebug(message);
+
+                itemsForResettingCatalog = MutableList.<CatalogItem<?,?>>of();
+                // TODO destroy persisted items
+                needsInitialCatalog = true;
             } else {
-                // Management context should have taken care of loading the catalogue
-                Collection<CatalogItem<?, ?>> catalogItems = rebindContext.getCatalogItems();
-                String message = "RebindManager not resetting catalog to persisted state. Catalog load mode is {}.";
-                if (!catalogItems.isEmpty() && shouldLogRebinding()) {
-                    LOG.info(message + " There {} {} item{} persisted.", new Object[]{
-                            catalogLoadMode, catalogItems.size() == 1 ? "was" : "were", catalogItems.size(), Strings.s(catalogItems)});
-                } else if (LOG.isDebugEnabled()) {
-                    logRebindingDebug(message, catalogLoadMode);
+                if (!isEmpty) {
+                    logRebindingDebug("RebindManager clearing local catalog and loading from persisted state");
+                    itemsForResettingCatalog = rebindContext.getCatalogItems();
+                    needsInitialCatalog = false;
+                } else {
+                    if (catInit.hasRun()) {
+                        logRebindingDebug("RebindManager will re-add any new items (persisted state empty)");
+                        needsInitialCatalog = false;
+                    } else {
+                        logRebindingDebug("RebindManager loading initial catalog locally because persisted state empty");
+                        needsInitialCatalog = true;
+                    }
                 }
             }
-            // TODO destroy old (as above)
         } else {
-            logRebindingDebug("RebindManager not resetting catalog because catalog persistence is disabled");
+            if (catInit.hasRun()) {
+                logRebindingDebug("RebindManager skipping catalog init because it has already run (catalog persistence disabled)");
+                needsInitialCatalog = false;
+            } else {
+                logRebindingDebug("RebindManager will initialize catalog locally because catalog persistence is disabled");
+                needsInitialCatalog = true;
+            }
         }
+
+        // TODO in read-only mode, perhaps do this less frequently than entities etc ?
+        catInit.populateCatalog(managementContext, needsInitialCatalog, itemsForResettingCatalog);
     }
 
     protected void instantiateLocationsAndEntities() {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
index 0f20f6b..de3460b 100644
--- a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
+++ b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
@@ -38,11 +38,10 @@ import org.slf4j.LoggerFactory;
 import brooklyn.basic.BrooklynObject;
 import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
-import brooklyn.catalog.CatalogLoadMode;
 import brooklyn.catalog.internal.BasicBrooklynCatalog;
+import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.BrooklynProperties;
-import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.StringConfigMap;
 import brooklyn.entity.Effector;
 import brooklyn.entity.Entity;
@@ -170,6 +169,7 @@ public abstract class AbstractManagementContext implements ManagementContextInte
     protected final List<Throwable> errors = Collections.synchronizedList(MutableList.<Throwable>of()); 
 
     protected Maybe<URI> uri = Maybe.absent();
+    protected CatalogInitialization catalogInitialization;
 
     public AbstractManagementContext(BrooklynProperties brooklynProperties){
         this(brooklynProperties, null);
@@ -359,24 +359,28 @@ public abstract class AbstractManagementContext implements ManagementContextInte
     }
 
     @Override
-    public BrooklynCatalog getCatalog() {
-        if (catalog==null) {
-            loadCatalog();
-        }
-        return catalog;
-    }
-
-    protected synchronized void loadCatalog() {
-        if (catalog != null) return;
-        BasicBrooklynCatalog catalog = new BasicBrooklynCatalog(this);
-        CatalogLoadMode loadMode = getConfig().getConfig(BrooklynServerConfig.CATALOG_LOAD_MODE);
-        if (CatalogLoadMode.LOAD_BROOKLYN_CATALOG_URL.equals(loadMode)) {
-            log.debug("Resetting catalog to configured URL. Catalog mode is: {}", loadMode.name());
-            catalog.resetCatalogToContentsAtConfiguredUrl();
-        } else if (log.isDebugEnabled()) {
-            log.debug("Deferring catalog load to rebind manager. Catalog mode is: {}", loadMode.name());
+    public Maybe<BrooklynCatalog> getCatalogIfSet() {
+        return Maybe.<BrooklynCatalog>fromNullable(catalog);
+    }
+
+    @Override
+    public void setCatalog(BrooklynCatalog catalog) {
+        if (this.catalog!=null && catalog!=null) {
+            // should only happen if process has accessed catalog before rebind/startup populated it
+            log.warn("Replacing catalog in management context; new catalog is: "+catalog);
         }
-        this.catalog = catalog;
+        this.catalog = (BasicBrooklynCatalog) catalog;
+    }
+
+    @Override
+    public BrooklynCatalog getCatalog() {
+        if (catalog!=null) 
+            return catalog;
+        
+        // catalog init is needed; normally this will be done from start sequence,
+        // but if accessed early -- and in tests -- we will load it here
+        // TODO log if in launcher mode
+        return getCatalogInitialization().getCatalogPopulatingBestEffort(this);
     }
 
     /**
@@ -443,6 +447,21 @@ public abstract class AbstractManagementContext implements ManagementContextInte
         return uri;
     }
     
+    @Override
+    public CatalogInitialization getCatalogInitialization() {
+        if (catalogInitialization!=null) return catalogInitialization;
+        synchronized (this) {
+            if (catalogInitialization!=null) return catalogInitialization;
+            CatalogInitialization ci = new CatalogInitialization();
+            setCatalogInitialization(ci);
+            return ci;
+        }
+    }
+    
+    @Override
+    public synchronized void setCatalogInitialization(CatalogInitialization catalogInitialization) {
+        this.catalogInitialization = catalogInitialization;
+    }
     
     public BrooklynObject lookup(String id) {
         return lookup(id, BrooklynObject.class);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java b/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java
index 87bcc98..d5123a6 100644
--- a/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java
+++ b/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java
@@ -24,9 +24,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 
+import brooklyn.catalog.BrooklynCatalog;
+import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.config.BrooklynProperties;
-import brooklyn.config.BrooklynServerConfig;
-import brooklyn.config.ConfigKey;
 import brooklyn.entity.Effector;
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.BrooklynTaskTags;
@@ -51,7 +51,7 @@ public interface ManagementContextInternal extends ManagementContext {
     public static final String NON_TRANSIENT_TASK_TAG = BrooklynTaskTags.NON_TRANSIENT_TASK_TAG;
     public static final String TRANSIENT_TASK_TAG = BrooklynTaskTags.TRANSIENT_TASK_TAG;
 
-    public static final ConfigKey<String> BROOKLYN_CATALOG_URL = BrooklynServerConfig.BROOKLYN_CATALOG_URL;
+    public static final String EMPTY_CATALOG_URL = "classpath://brooklyn/empty.catalog.bom";
     
     ClassLoader getBaseClassLoader();
 
@@ -112,5 +112,18 @@ public interface ManagementContextInternal extends ManagementContext {
      * TODO In future this will change to a custom interface with a unique identifier for each error. */
     @Beta
     List<Throwable> errors();
+
+    @Beta
+    CatalogInitialization getCatalogInitialization();
+
+    @Beta
+    void setCatalogInitialization(CatalogInitialization catalogInitialization);
+
+    @Beta
+    public Maybe<BrooklynCatalog> getCatalogIfSet();
     
+    /** For use from {@link CatalogInitialization} to set the catalog */
+    @Beta
+    public void setCatalog(BrooklynCatalog catalog);
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java b/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
index d5fcf75..77eb4de 100644
--- a/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
+++ b/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
@@ -35,6 +35,8 @@ import org.slf4j.LoggerFactory;
 
 import brooklyn.basic.BrooklynObject;
 import brooklyn.catalog.BrooklynCatalog;
+import brooklyn.catalog.internal.BasicBrooklynCatalog;
+import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.StringConfigMap;
 import brooklyn.entity.Application;
@@ -73,6 +75,7 @@ import brooklyn.mementos.BrooklynMementoRawData;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.time.Duration;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 
 public class NonDeploymentManagementContext implements ManagementContextInternal {
@@ -447,6 +450,31 @@ public class NonDeploymentManagementContext implements ManagementContextInternal
         return initialManagementContext.errors();
     }
 
+    @Override
+    public CatalogInitialization getCatalogInitialization() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getCatalogInitialization();
+    }
+    
+    @Override
+    public void setCatalogInitialization(CatalogInitialization catalogInitialization) {
+        checkInitialManagementContextReal();
+        initialManagementContext.setCatalogInitialization(catalogInitialization);
+    }
+
+    @Override
+    public Maybe<BrooklynCatalog> getCatalogIfSet() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.getCatalogIfSet();
+    }
+    
+    /** For use from {@link CatalogInitialization} to set the catalog */
+    @Beta @Override
+    public void setCatalog(BrooklynCatalog catalog) {
+        checkInitialManagementContextReal();
+        initialManagementContext.setCatalog(catalog);
+    }
+    
     /**
      * For when the initial management context is not "real"; the changeListener is a no-op, but everything else forbidden.
      * 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/main/resources/brooklyn/empty.catalog.bom
----------------------------------------------------------------------
diff --git a/core/src/main/resources/brooklyn/empty.catalog.bom b/core/src/main/resources/brooklyn/empty.catalog.bom
new file mode 100644
index 0000000..401d2fa
--- /dev/null
+++ b/core/src/main/resources/brooklyn/empty.catalog.bom
@@ -0,0 +1,18 @@
+# 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.
+#
+brooklyn.catalog: {}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
index 91372f0..949877f 100644
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
+++ b/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
@@ -32,6 +32,7 @@ import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.CatalogPredicates;
 import brooklyn.catalog.internal.MyCatalogItems.MySillyAppTemplate;
 import brooklyn.config.BrooklynProperties;
+import brooklyn.config.BrooklynServerConfig;
 import brooklyn.entity.Application;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.proxying.EntitySpec;
@@ -68,7 +69,7 @@ public class CatalogScanTest {
     private synchronized void loadFullCatalog() {
         if (fullCatalog!=null) return;
         BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
-        props.put(LocalManagementContext.BROOKLYN_CATALOG_URL.getName(), 
+        props.put(BrooklynServerConfig.BROOKLYN_CATALOG_URL.getName(), 
                 "data:,"+Urls.encode("<catalog><classpath scan=\"types\"/></catalog>"));
         fullCatalog = newManagementContext(props).getCatalog();        
         log.info("ENTITIES loaded for FULL: "+fullCatalog.getCatalogItems(Predicates.alwaysTrue()));
@@ -77,7 +78,7 @@ public class CatalogScanTest {
     private synchronized void loadTheDefaultCatalog() {
         if (defaultCatalog!=null) return;
         BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
-        props.put(LocalManagementContext.BROOKLYN_CATALOG_URL.getName(), "");
+        props.put(BrooklynServerConfig.BROOKLYN_CATALOG_URL.getName(), "");
         LocalManagementContext managementContext = newManagementContext(props);
         defaultCatalog = managementContext.getCatalog();        
         log.info("ENTITIES loaded for DEFAULT: "+defaultCatalog.getCatalogItems(Predicates.alwaysTrue()));
@@ -87,7 +88,7 @@ public class CatalogScanTest {
     private synchronized void loadAnnotationsOnlyCatalog() {
         if (annotsCatalog!=null) return;
         BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
-        props.put(LocalManagementContext.BROOKLYN_CATALOG_URL.getName(),
+        props.put(BrooklynServerConfig.BROOKLYN_CATALOG_URL.getName(),
                 "data:,"+URLEncoder.encode("<catalog><classpath scan=\"annotations\"/></catalog>"));
         LocalManagementContext managementContext = newManagementContext(props);
         annotsCatalog = managementContext.getCatalog();
@@ -126,14 +127,14 @@ public class CatalogScanTest {
     @Test
     public void testAnnotationLoadsSomeApps() {
         loadAnnotationsOnlyCatalog();
-        Iterable<CatalogItem<Object,Object>> silly1 = annotsCatalog.getCatalogItems(CatalogPredicates.name(Predicates.equalTo("MySillyAppTemplate")));
+        Iterable<CatalogItem<Object,Object>> silly1 = annotsCatalog.getCatalogItems(CatalogPredicates.displayName(Predicates.equalTo("MySillyAppTemplate")));
         Assert.assertEquals(Iterables.getOnlyElement(silly1).getDescription(), "Some silly app test");
     }
     
     @Test
     public void testAnnotationLoadsSomeAppBuilders() {
         loadAnnotationsOnlyCatalog();
-        Iterable<CatalogItem<Object,Object>> silly1 = annotsCatalog.getCatalogItems(CatalogPredicates.name(Predicates.equalTo("MySillyAppBuilderTemplate")));
+        Iterable<CatalogItem<Object,Object>> silly1 = annotsCatalog.getCatalogItems(CatalogPredicates.displayName(Predicates.equalTo("MySillyAppBuilderTemplate")));
         Assert.assertEquals(Iterables.getOnlyElement(silly1).getDescription(), "Some silly app builder test");
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
index cf9e5fe..9c94697 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
@@ -22,10 +22,10 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNotSame;
 
+import java.io.Closeable;
 import java.net.URL;
 import java.util.List;
 
-import brooklyn.test.TestResourceUnavailableException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.BeforeMethod;
@@ -40,10 +40,13 @@ import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.basic.StartableApplication;
 import brooklyn.entity.proxying.EntitySpec;
 import brooklyn.event.basic.Sensors;
+import brooklyn.management.ManagementContext;
 import brooklyn.management.ha.ManagementNodeState;
 import brooklyn.management.internal.LocalManagementContext;
+import brooklyn.test.TestResourceUnavailableException;
 import brooklyn.util.javalang.UrlClassLoader;
-import brooklyn.util.os.Os;
+
+import com.google.common.base.Function;
 
 public class RebindCatalogEntityTest extends RebindTestFixture<StartableApplication> {
 
@@ -89,6 +92,8 @@ public class RebindCatalogEntityTest extends RebindTestFixture<StartableApplicat
     //
     // Note: to test before/after behaviour (i.e. that we're really fixing what we think we are) then comment out the body of:
     //       AbstractMemento.injectTypeClass(Class)
+    //
+    // NB: this behaviour is generally deprecated in favour of OSGi now.
     @Test
     public void testRestoresAppFromCatalogClassloader() throws Exception {
         @SuppressWarnings("unchecked")
@@ -100,7 +105,7 @@ public class RebindCatalogEntityTest extends RebindTestFixture<StartableApplicat
         origApp = ApplicationBuilder.newManagedApp(appSpec, origManagementContext);
         ((EntityInternal)origApp).setAttribute(Sensors.newStringSensor("mysensor"), "mysensorval");
         
-        newApp = rebind();
+        newApp = rebindWithAppClass();
         Entities.dumpInfo(newApp);
         assertNotSame(newApp, origApp);
         assertEquals(newApp.getId(), origApp.getId());
@@ -118,17 +123,27 @@ public class RebindCatalogEntityTest extends RebindTestFixture<StartableApplicat
     // TODO Not using RebindTestUtils.rebind(mementoDir, getClass().getClassLoader());
     //      because that won't have right catalog classpath.
     //      How to reuse that code cleanly?
-    @Override
-    protected StartableApplication rebind() throws Exception {
+    protected StartableApplication rebindWithAppClass() throws Exception {
         RebindTestUtils.waitForPersisted(origApp);
-
         LocalManagementContext newManagementContext = RebindTestUtils.newPersistingManagementContextUnstarted(mementoDir, classLoader);
-        
+
+        UrlClassLoader ucl = new UrlClassLoader(url);
         @SuppressWarnings("unchecked")
-        Class<? extends AbstractApplication> appClazz = (Class<? extends AbstractApplication>) new UrlClassLoader(url).loadClass(APP_CLASSNAME);
-        newManagementContext.getCatalog().addItem(appClazz);
+        final Class<? extends AbstractApplication> appClazz = (Class<? extends AbstractApplication>) ucl.loadClass(APP_CLASSNAME);
+        // ucl.close is only introduced in java 1.7
+        if (ucl instanceof Closeable) ((Closeable)ucl).close();
+
+        newManagementContext.getCatalogInitialization().addPopulationCallback(new Function<ManagementContext, Void>() {
+            @Override
+            public Void apply(ManagementContext input) {
+                input.getCatalog().addItem(appClazz);
+                return null;
+            }
+        });
         
         ClassLoader classLoader = newManagementContext.getCatalog().getRootClassLoader();
+        
+        classLoader.loadClass(appClazz.getName());
         List<Application> newApps = newManagementContext.getRebindManager().rebind(classLoader, null, ManagementNodeState.MASTER);
         newManagementContext.getRebindManager().startPersistence();
         return (StartableApplication) newApps.get(0);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
index e0260c3..10bcc68 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
@@ -37,7 +37,6 @@ import brooklyn.camp.lite.CampPlatformWithJustBrooklynMgmt;
 import brooklyn.camp.lite.TestAppAssemblyInstantiator;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.CatalogItem.CatalogItemType;
-import brooklyn.catalog.CatalogLoadMode;
 import brooklyn.catalog.internal.BasicBrooklynCatalog;
 import brooklyn.catalog.internal.CatalogDto;
 import brooklyn.config.BrooklynProperties;
@@ -85,7 +84,7 @@ public class RebindCatalogItemTest extends RebindTestFixtureWithApp {
     protected LocalManagementContext createOrigManagementContext() {
         BrooklynProperties properties = BrooklynProperties.Factory.newDefault();
         properties.put(BrooklynServerConfig.BROOKLYN_CATALOG_URL, "classpath://brooklyn/entity/rebind/rebind-catalog-item-test-catalog.xml");
-        properties.put(BrooklynServerConfig.CATALOG_LOAD_MODE, CatalogLoadMode.LOAD_BROOKLYN_CATALOG_URL);
+        properties.put(BrooklynServerConfig.CATALOG_LOAD_MODE, brooklyn.catalog.CatalogLoadMode.LOAD_BROOKLYN_CATALOG_URL);
         return RebindTestUtils.managementContextBuilder(mementoDir, classLoader)
                 .properties(properties)
                 .persistPeriodMillis(getPersistPeriodMillis())
@@ -97,7 +96,6 @@ public class RebindCatalogItemTest extends RebindTestFixtureWithApp {
     protected LocalManagementContext createNewManagementContext(File mementoDir) {
         BrooklynProperties properties = BrooklynProperties.Factory.newDefault();
         properties.put(BrooklynServerConfig.BROOKLYN_CATALOG_URL, "classpath://brooklyn/entity/rebind/rebind-catalog-item-test-catalog.xml");
-        properties.put(BrooklynServerConfig.CATALOG_LOAD_MODE, CatalogLoadMode.LOAD_BROOKLYN_CATALOG_URL_IF_NO_PERSISTED_STATE);
         return RebindTestUtils.managementContextBuilder(mementoDir, classLoader)
                 .properties(properties)
                 .forLive(useLiveManagementContext())

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/test/java/brooklyn/entity/rebind/RebindTestUtils.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindTestUtils.java b/core/src/test/java/brooklyn/entity/rebind/RebindTestUtils.java
index ea901af..939d42e 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindTestUtils.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindTestUtils.java
@@ -200,7 +200,7 @@ public class RebindTestUtils {
                     ? this.properties
                     : BrooklynProperties.Factory.newDefault();
             if (this.emptyCatalog) {
-                properties.putIfAbsent(BrooklynServerConfig.BROOKLYN_CATALOG_URL, "classpath://brooklyn-catalog-empty.xml");
+                properties.putIfAbsent(BrooklynServerConfig.BROOKLYN_CATALOG_URL, ManagementContextInternal.EMPTY_CATALOG_URL);
             }
             if (forLive) {
                 unstarted = new LocalManagementContext(properties);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/core/src/test/java/brooklyn/test/entity/LocalManagementContextForTests.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/test/entity/LocalManagementContextForTests.java b/core/src/test/java/brooklyn/test/entity/LocalManagementContextForTests.java
index 38d163a..28b6914 100644
--- a/core/src/test/java/brooklyn/test/entity/LocalManagementContextForTests.java
+++ b/core/src/test/java/brooklyn/test/entity/LocalManagementContextForTests.java
@@ -22,6 +22,7 @@ import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.ConfigKey;
 import brooklyn.management.internal.LocalManagementContext;
+import brooklyn.management.internal.ManagementContextInternal;
 
 /** management context which allows disabling common time-consuming tasks.
  * most instances have:
@@ -55,7 +56,7 @@ public class LocalManagementContextForTests extends LocalManagementContext {
 
     public static BrooklynProperties setEmptyCatalogAsDefault(BrooklynProperties brooklynProperties) {
         if (brooklynProperties==null) return null;
-        brooklynProperties.putIfAbsent(BrooklynServerConfig.BROOKLYN_CATALOG_URL, "classpath://brooklyn-catalog-empty.xml");
+        brooklynProperties.putIfAbsent(BrooklynServerConfig.BROOKLYN_CATALOG_URL, ManagementContextInternal.EMPTY_CATALOG_URL);
         return brooklynProperties;
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/usage/cli/src/main/java/brooklyn/cli/Main.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/brooklyn/cli/Main.java b/usage/cli/src/main/java/brooklyn/cli/Main.java
index d89703e..7dab5aa 100644
--- a/usage/cli/src/main/java/brooklyn/cli/Main.java
+++ b/usage/cli/src/main/java/brooklyn/cli/Main.java
@@ -41,6 +41,7 @@ import brooklyn.BrooklynVersion;
 import brooklyn.basic.BrooklynTypes;
 import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.cli.CloudExplorer.BlobstoreGetBlobCommand;
 import brooklyn.cli.CloudExplorer.BlobstoreListContainerCommand;
 import brooklyn.cli.CloudExplorer.BlobstoreListContainersCommand;
@@ -217,6 +218,24 @@ public class Main extends AbstractMain {
                         "(or as a JSON array, if the values are complex)")
         public String locations;
 
+        @Option(name = { "--catalogInitial" }, title = "catalog initial bom URI",
+            description = "Specifies a catalog.bom URI to be used to populate the initial catalog, "
+                + "if nothing is yet persisted in the catalog (or if it is reset)")
+        public String catalogInitial;
+
+        @Option(name = { "--catalogReset" }, title = "clear catalog",
+            description = "Specifies that any catalog items which have been persisted should be cleared")
+        public boolean catalogReset;
+
+        @Option(name = { "--catalogAdd" }, title = "catalog bom URI to add",
+            description = "Specifies a catalog.bom to be added to the catalog")
+        public String catalogAdd;
+
+        @Option(name = { "--catalogForce" }, title = "force catalog addition",
+            description = "Specifies that catalog items added via the CLI should be forcibly added, "
+                + "replacing any identical versions already registered (use with care!)")
+        public boolean catalogForce;
+
         @Option(name = { "-p", "--port" }, title = "port number",
                 description = "Specifies the port to be used by the Brooklyn Management Console; "
                     + "default is 8081+ for http, 8443+ for https.")
@@ -386,13 +405,15 @@ public class Main extends AbstractMain {
     
                 launcher = createLauncher();
 
-                launcher.customizeInitialCatalog(new Function<BrooklynLauncher,Void>() {
+                CatalogInitialization catInit = new CatalogInitialization(catalogInitial, catalogReset, catalogAdd, catalogForce);
+                catInit.addPopulationCallback(new Function<ManagementContext,Void>() {
                     @Override
-                    public Void apply(BrooklynLauncher launcher) {
-                        populateCatalog(launcher.getServerDetails().getManagementContext().getCatalog());
+                    public Void apply(ManagementContext mgmt) {
+                        populateCatalog(mgmt.getCatalog());
                         return null;
                     }
                 });
+                launcher.catalogInitialization(catInit);
                 
                 launcher.persistMode(persistMode);
                 launcher.persistenceDir(persistenceDir);
@@ -652,7 +673,7 @@ public class Main extends AbstractMain {
                 stopAllApps(ctx.getApplications());
             } else {
                 // Block forever so that Brooklyn doesn't exit (until someone does cntrl-c or kill)
-                log.info("Launched Brooklyn; will now block until shutdown issued. Shutdown via GUI or API or process interrupt.");
+                log.info("Launched Brooklyn; will now block until shutdown command received via GUI/API (recommended) or process interrupt.");
                 waitUntilInterrupted();
             }
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
index 16f4f76..4d3b1be 100644
--- a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
+++ b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
@@ -41,7 +41,7 @@ import javax.annotation.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.catalog.CatalogLoadMode;
+import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.BrooklynServerPaths;
@@ -66,7 +66,6 @@ import brooklyn.entity.rebind.persister.PersistMode;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore;
 import brooklyn.entity.rebind.transformer.CompoundTransformer;
 import brooklyn.entity.trait.Startable;
-import brooklyn.internal.BrooklynFeatureEnablement;
 import brooklyn.launcher.config.StopWhichAppsOnShutdown;
 import brooklyn.location.Location;
 import brooklyn.location.LocationSpec;
@@ -158,7 +157,7 @@ public class BrooklynLauncher {
     private StopWhichAppsOnShutdown stopWhichAppsOnShutdown = StopWhichAppsOnShutdown.THESE_IF_NOT_PERSISTED;
     
     private Function<ManagementContext,Void> customizeManagement = null;
-    private Function<BrooklynLauncher,Void> customizeInitialCatalog = null;
+    private CatalogInitialization catalogInitialization = null;
     
     private PersistMode persistMode = PersistMode.DISABLED;
     private HighAvailabilityMode highAvailabilityMode = HighAvailabilityMode.DISABLED;
@@ -423,10 +422,10 @@ public class BrooklynLauncher {
     }
 
     @Beta
-    public BrooklynLauncher customizeInitialCatalog(Function<BrooklynLauncher, Void> customizeInitialCatalog) {
-        if (this.customizeInitialCatalog!=null)
+    public BrooklynLauncher catalogInitialization(CatalogInitialization catInit) {
+        if (this.catalogInitialization!=null)
             throw new IllegalStateException("Initial catalog customization already set.");
-        this.customizeInitialCatalog = customizeInitialCatalog;
+        this.catalogInitialization = catInit;
         return this;
     }
 
@@ -560,8 +559,6 @@ public class BrooklynLauncher {
         if (started) throw new IllegalStateException("Cannot start() or launch() multiple times");
         started = true;
 
-        setCatalogLoadMode();
-
         // Create the management context
         initManagementContext();
 
@@ -589,10 +586,12 @@ public class BrooklynLauncher {
         }
 
         try {
-            // TODO currently done *after* above to mirror existing usage, 
-            // but where this runs will likely change
-            if (customizeInitialCatalog!=null)
-                customizeInitialCatalog.apply(this);
+            // run cat init now if it hasn't yet been run
+            CatalogInitialization catInit = ((ManagementContextInternal)managementContext).getCatalogInitialization();
+            if (catInit!=null && !catInit.hasRun()) {
+                LOG.debug("Loading catalog as part of launcher (persistence did not run it)");
+                catInit.populateCatalog(managementContext, true, null);
+            }
         } catch (Exception e) {
             handleSubsystemStartupError(true, "initial catalog", e);
         }
@@ -622,22 +621,6 @@ public class BrooklynLauncher {
         return this;
     }
 
-    /**
-     * Sets {@link BrooklynServerConfig#CATALOG_LOAD_MODE} in {@link #brooklynAdditionalProperties}.
-     * <p>
-     * Checks {@link brooklyn.internal.BrooklynFeatureEnablement#FEATURE_CATALOG_PERSISTENCE_PROPERTY}
-     * and the {@link #persistMode persistence mode}.
-     */
-    private void setCatalogLoadMode() {
-        CatalogLoadMode catalogLoadMode;
-        if (!BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_CATALOG_PERSISTENCE_PROPERTY)) {
-            catalogLoadMode = CatalogLoadMode.LOAD_BROOKLYN_CATALOG_URL;
-        } else {
-            catalogLoadMode = CatalogLoadMode.forPersistMode(persistMode);
-        }
-        brooklynProperties(BrooklynServerConfig.CATALOG_LOAD_MODE, catalogLoadMode);
-    }
-
     private void initManagementContext() {
         // Create the management context
         if (managementContext == null) {
@@ -682,6 +665,8 @@ public class BrooklynLauncher {
             brooklynProperties.addFromMap(brooklynAdditionalProperties);
         }
         
+        ((ManagementContextInternal)managementContext).setCatalogInitialization(catalogInitialization);
+        
         if (customizeManagement!=null) {
             customizeManagement.apply(managementContext);
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java
index 0eba3d7..b4f963e 100644
--- a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java
+++ b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java
@@ -36,6 +36,7 @@ import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.Test;
 
+import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.entity.Application;
@@ -313,15 +314,15 @@ public class BrooklynLauncherTest {
         }
     }
     
-    @Test  // takes a few seconds because starts webapp, but also tests rest api so useful
+    @Test  // takes a bit of time because starts webapp, but also tests rest api so useful
     public void testErrorsCaughtByApiAndRestApiWorks() throws Exception {
         launcher = newLauncherForTests(true)
-                .customizeInitialCatalog(new Function<BrooklynLauncher, Void>() {
+                .catalogInitialization(new CatalogInitialization(null, false, null, false).addPopulationCallback(new Function<ManagementContext, Void>() {
                     @Override
-                    public Void apply(BrooklynLauncher input) {
+                    public Void apply(ManagementContext input) {
                         throw new RuntimeException("deliberate-exception-for-testing");
                     }
-                })
+                }))
                 .start();
         // such an error should be thrown, then caught in this calling thread
         ManagementContext mgmt = launcher.getServerDetails().getManagementContext();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java b/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java
index 9941466..f189815 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java
@@ -43,6 +43,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.BrooklynProperties;
+import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.BrooklynServiceAttributes;
 import brooklyn.management.ManagementContext;
 import brooklyn.management.internal.LocalManagementContext;
@@ -200,7 +201,7 @@ public class BrooklynRestApiLauncher {
 
         if (forceUseOfDefaultCatalogWithJavaClassPath) {
             // don't use any catalog.xml which is set
-            ((BrooklynProperties) mgmt.getConfig()).put(ManagementContextInternal.BROOKLYN_CATALOG_URL, "");
+            ((BrooklynProperties) mgmt.getConfig()).put(BrooklynServerConfig.BROOKLYN_CATALOG_URL, ManagementContextInternal.EMPTY_CATALOG_URL);
             // sets URLs for a surefire
             ((LocalManagementContext) mgmt).setBaseClassPathForScanning(ClasspathHelper.forJavaClassPath());
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0b9bc3b3/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncherTestFixture.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncherTestFixture.java b/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncherTestFixture.java
index 4ce519f..86be4c7 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncherTestFixture.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncherTestFixture.java
@@ -25,6 +25,7 @@ import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 
 import brooklyn.config.BrooklynProperties;
+import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.BrooklynServiceAttributes;
 import brooklyn.entity.basic.Entities;
 import brooklyn.management.ManagementContext;
@@ -82,7 +83,7 @@ public abstract class BrooklynRestApiLauncherTestFixture {
 
     public static void forceUseOfDefaultCatalogWithJavaClassPath(ManagementContext manager) {
         // don't use any catalog.xml which is set
-        ((BrooklynProperties)manager.getConfig()).put(ManagementContextInternal.BROOKLYN_CATALOG_URL, "");
+        ((BrooklynProperties)manager.getConfig()).put(BrooklynServerConfig.BROOKLYN_CATALOG_URL, ManagementContextInternal.EMPTY_CATALOG_URL);
         // sets URLs for a surefire
         ((LocalManagementContext)manager).setBaseClassPathForScanning(ClasspathHelper.forJavaClassPath());
         // this also works


[18/23] incubator-brooklyn git commit: if pre- and post- install and launch commands are set for EmptySoftwareProcess, run them

Posted by he...@apache.org.
if pre- and post- install and launch commands are set for EmptySoftwareProcess, run them


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

Branch: refs/heads/master
Commit: 5236675560765a18c0117647e4545f5180fd4e3a
Parents: 776ad43
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu May 21 08:46:01 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu May 21 08:46:37 2015 +0100

----------------------------------------------------------------------
 .../basic/EmptySoftwareProcessSshDriver.java    | 32 +++++++++-----------
 1 file changed, 15 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/52366755/software/base/src/main/java/brooklyn/entity/basic/EmptySoftwareProcessSshDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/EmptySoftwareProcessSshDriver.java b/software/base/src/main/java/brooklyn/entity/basic/EmptySoftwareProcessSshDriver.java
index 5c320c3..f7d1d3d 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/EmptySoftwareProcessSshDriver.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/EmptySoftwareProcessSshDriver.java
@@ -18,6 +18,7 @@
  */
 package brooklyn.entity.basic;
 
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import brooklyn.location.basic.SshMachineLocation;
@@ -43,25 +44,22 @@ public class EmptySoftwareProcessSshDriver extends AbstractSoftwareProcessSshDri
     public void customize() { }
 
     @Override
-    public void setup() { }
-
-    @Override
-    public void copyInstallResources() { }
-
-    @Override
-    public void copyRuntimeResources() { }
-
-    @Override
-    public void runPreInstallCommand(String command) { }
-
-    @Override
-    public void runPostInstallCommand(String command) { }
-
-    @Override
-    public void runPreLaunchCommand(String command) { }
+    public void copyInstallResources() { 
+        Map<String, String> installFiles = entity.getConfig(SoftwareProcess.INSTALL_FILES);
+        Map<String, String> installTemplates = entity.getConfig(SoftwareProcess.INSTALL_TEMPLATES);
+        if ((installFiles!=null && !installFiles.isEmpty()) || (installTemplates!=null && !installTemplates.isEmpty())) {
+            super.copyInstallResources();
+        }
+    }
 
     @Override
-    public void runPostLaunchCommand(String command) { }
+    public void copyRuntimeResources() { 
+        Map<String, String> runtimeFiles = entity.getConfig(SoftwareProcess.RUNTIME_FILES);
+        Map<String, String> runtimeTemplates = entity.getConfig(SoftwareProcess.RUNTIME_TEMPLATES);
+        if ((runtimeFiles!=null && !runtimeFiles.isEmpty()) || (runtimeTemplates!=null && !runtimeTemplates.isEmpty())) {
+            super.copyRuntimeResources();
+        }        
+    }
 
     @Override
     public void launch() {


[10/23] incubator-brooklyn git commit: address code review comments (minor)

Posted by he...@apache.org.
address code review comments (minor)


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

Branch: refs/heads/master
Commit: fa09efce0d7f6b1429923810d3f65ef9bd776ffb
Parents: 49a6ef7
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu May 7 18:58:43 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:22:23 2015 +0100

----------------------------------------------------------------------
 core/src/main/java/brooklyn/catalog/internal/CatalogDto.java | 1 +
 .../entity/brooklynnode/BrooklynNodeIntegrationTest.java     | 8 ++++++--
 2 files changed, 7 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/fa09efce/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java
index 407cb7c..8888542 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogDto.java
@@ -86,6 +86,7 @@ public class CatalogDto {
     }
 
     /** @deprecated since 0.7.0 the xml format is deprecated; use YAML parse routines on BasicBrooklynCatalog */
+    @Deprecated
     public static CatalogDto newDtoFromXmlContents(String xmlContents, String originDescription) {
         CatalogDto result = (CatalogDto) new CatalogXmlSerializer().deserialize(new StringReader(xmlContents));
         result.contentsDescription = originDescription;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/fa09efce/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java b/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
index ff7b5fc..944f286 100644
--- a/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
+++ b/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
@@ -54,6 +54,7 @@ import brooklyn.entity.brooklynnode.BrooklynNode.StopNodeAndKillAppsEffector;
 import brooklyn.entity.proxying.EntityProxyImpl;
 import brooklyn.entity.proxying.EntitySpec;
 import brooklyn.event.feed.http.JsonFunctions;
+import brooklyn.location.MachineLocation;
 import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import brooklyn.location.basic.Locations;
 import brooklyn.location.basic.PortRanges;
@@ -64,6 +65,7 @@ import brooklyn.util.collections.MutableMap;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Functionals;
+import brooklyn.util.guava.Maybe;
 import brooklyn.util.http.HttpTool;
 import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.javalang.JavaClassNames;
@@ -519,13 +521,15 @@ services:
         File pidFile = new File(getDriver(brooklynNode).getPidFile());
         assertTrue(isPidRunning(pidFile));
         
+        Maybe<MachineLocation> l = Locations.findUniqueMachineLocation(brooklynNode.getLocations());
         brooklynNode.invoke(eff, Collections.<String, Object>emptyMap()).getUnchecked();
 
         // Note can't use driver.isRunning to check shutdown; can't invoke scripts on an unmanaged entity
         EntityTestUtils.assertAttributeEquals(brooklynNode, BrooklynNode.SERVICE_UP, false);
-        // previously we unmanaged the node on stop, but that behaviour has been removed (noticed May 2015)
-        // TODO remove this after a couple of months, for awareness/confirmation
+        
+        // unmanaged if the machine is destroyed - ie false on localhost (this test by default), but true in the cloud 
 //        assertFalse(Entities.isManaged(brooklynNode));
+        
         assertFalse(isPidRunning(pidFile), "pid in "+pidFile+" still running");
     }
 


[08/23] incubator-brooklyn git commit: tidy CLI options, renaming startup-ignore-error flags; and other tidy

Posted by he...@apache.org.
tidy CLI options, renaming startup-ignore-error flags; and other tidy

breaks backwards compatibility in CLI: previously we had `ignoreXxxOnStartup` fields, but now many of these default to true, and airlift offers no way to make them false, so they are called `--startupFailOn...` or `--startupContinueOn...`.

changes the nascent CatalogInitialization so that callbacks get it, and can query settings such as whether to throw on errors.


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

Branch: refs/heads/master
Commit: 21707da8e476f4ee530e02f93bfdefd981a9e1e1
Parents: 127150e
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Apr 30 11:07:00 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:22:22 2015 +0100

----------------------------------------------------------------------
 .../catalog/internal/BasicBrooklynCatalog.java  | 12 ++-
 .../catalog/internal/CatalogInitialization.java | 96 ++++++++++++++------
 .../brooklyn/entity/rebind/RebindIteration.java |  5 +-
 .../internal/AbstractManagementContext.java     |  3 +-
 .../entity/rebind/RebindCatalogEntityTest.java  |  7 +-
 usage/cli/src/main/java/brooklyn/cli/Main.java  | 94 ++++++++++---------
 .../brooklyn/launcher/BrooklynLauncher.java     | 21 ++++-
 .../brooklyn/launcher/BrooklynLauncherTest.java |  4 +-
 8 files changed, 157 insertions(+), 85 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
index f1d5a3c..375b29e 100644
--- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
@@ -25,6 +25,7 @@ import io.brooklyn.camp.spi.AssemblyTemplate;
 import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator;
 import io.brooklyn.camp.spi.pdp.DeploymentPlan;
 
+import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -346,14 +347,21 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
 
         // revert to legacy mechanism
         SpecT spec = null;
+        Method method;
+        try {
+            method = Reflections.findMethod(specType, "create", Class.class);
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            throw new IllegalStateException("Unsupported creation of spec type "+specType+"; it must have a public static create(Class) method", e);            
+        }
         try {
             if (loadedItem.getJavaType()!=null) {
-                SpecT specT = (SpecT) Reflections.findMethod(specType, "create", Class.class).invoke(null, loadedItem.loadJavaClass(mgmt));
+                SpecT specT = (SpecT) method.invoke(null, loadedItem.loadJavaClass(mgmt));
                 spec = specT;
             }
         } catch (Exception e) {
             Exceptions.propagateIfFatal(e);
-            throw new IllegalStateException("Unsupported creation of spec type "+specType+"; it must have a public static create(Class) method", e);
+            throw new IllegalStateException("Error creating "+specType+" "+loadedItem.getJavaType()+": "+e, e);
         }
 
         if (spec==null) 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java b/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
index 15d5d76..69dc877 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java
@@ -30,10 +30,13 @@ import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.management.ManagementContext;
+import brooklyn.management.ManagementContextInjectable;
 import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.exceptions.FatalRuntimeException;
+import brooklyn.util.exceptions.RuntimeInterruptedException;
 import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.net.Urls;
@@ -41,10 +44,11 @@ import brooklyn.util.text.Strings;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
 import com.google.common.collect.Iterables;
 
 @Beta
-public class CatalogInitialization {
+public class CatalogInitialization implements ManagementContextInjectable {
 
     /*
 
@@ -74,9 +78,13 @@ public class CatalogInitialization {
     boolean force;
 
     boolean disallowLocal = false;
-    List<Function<ManagementContext, Void>> callbacks = MutableList.of();
+    List<Function<CatalogInitialization, Void>> callbacks = MutableList.of();
     AtomicInteger runCount = new AtomicInteger();
     
+    ManagementContext managementContext;
+    boolean isStartingUp = false;
+    boolean failOnStartupErrors = false;
+    
     public CatalogInitialization(String initialUri, boolean reset, String additionUri, boolean force) {
         this.initialUri = initialUri;
         this.reset = reset;
@@ -88,7 +96,17 @@ public class CatalogInitialization {
         this(null, false, null, false);
     }
 
-    public CatalogInitialization addPopulationCallback(Function<ManagementContext, Void> callback) {
+    public void injectManagementContext(ManagementContext managementContext) {
+        if (this.managementContext!=null && managementContext!=null && !this.managementContext.equals(managementContext))
+            throw new IllegalStateException("Cannot switch management context of "+this+"; from "+this.managementContext+" to "+managementContext);
+        this.managementContext = managementContext;
+    }
+    
+    public ManagementContext getManagementContext() {
+        return Preconditions.checkNotNull(managementContext, "management context has not been injected into "+this);
+    }
+
+    public CatalogInitialization addPopulationCallback(Function<CatalogInitialization, Void> callback) {
         callbacks.add(callback);
         return this;
     }
@@ -106,51 +124,51 @@ public class CatalogInitialization {
     }
 
     /** makes or updates the mgmt catalog, based on the settings in this class */
-    public void populateCatalog(ManagementContext managementContext, boolean needsInitial, Collection<CatalogItem<?, ?>> optionalItemsForResettingCatalog) {
+    public void populateCatalog(boolean needsInitial, Collection<CatalogItem<?, ?>> optionalItemsForResettingCatalog) {
         try {
             BasicBrooklynCatalog catalog;
             Maybe<BrooklynCatalog> cm = ((ManagementContextInternal)managementContext).getCatalogIfSet();
             if (cm.isAbsent()) {
                 if (hasRun()) {
-                    log.warn("Odd: catalog initialization has run but management context has no catalog; re-creating");
+                    log.warn("Catalog initialization has already run but management context has no catalog; re-creating");
                 }
                 catalog = new BasicBrooklynCatalog(managementContext);
                 setCatalog(managementContext, catalog, "Replacing catalog with newly populated catalog", true);
             } else {
                 if (!hasRun()) {
-                    log.warn("Odd: catalog initialization has not run but management context has a catalog; re-populating");
+                    log.warn("Catalog initialization has not properly run but management context has a catalog; re-populating, possibly overwriting items installed during earlier access (it may have been an early web request)");
                 }
                 catalog = (BasicBrooklynCatalog) cm.get();
             }
 
-            populateCatalog(managementContext, catalog, needsInitial, true, optionalItemsForResettingCatalog);
+            populateCatalog(catalog, needsInitial, true, optionalItemsForResettingCatalog);
             
         } finally {
             runCount.incrementAndGet();
         }
     }
 
-    private void populateCatalog(ManagementContext managementContext, BasicBrooklynCatalog catalog, boolean needsInitial, boolean runCallbacks, Collection<CatalogItem<?, ?>> optionalItemsForResettingCatalog) {
-        applyCatalogLoadMode(managementContext);
+    private void populateCatalog(BasicBrooklynCatalog catalog, boolean needsInitial, boolean runCallbacks, Collection<CatalogItem<?, ?>> optionalItemsForResettingCatalog) {
+        applyCatalogLoadMode();
         
         if (optionalItemsForResettingCatalog!=null) {
             catalog.reset(optionalItemsForResettingCatalog);
         }
         
         if (needsInitial) {
-            populateInitial(catalog, managementContext);
+            populateInitial(catalog);
         }
         
-        populateAdditions(catalog, managementContext);
+        populateAdditions(catalog);
 
         if (runCallbacks) {
-            populateViaCallbacks(catalog, managementContext);
+            populateViaCallbacks(catalog);
         }
     }
 
     private enum PopulateMode { YAML, XML, AUTODETECT }
     
-    protected void populateInitial(BasicBrooklynCatalog catalog, ManagementContext managementContext) {
+    protected void populateInitial(BasicBrooklynCatalog catalog) {
         if (disallowLocal) {
             if (!hasRun()) {
                 log.debug("CLI initial catalog not being read with disallow-local mode set.");
@@ -166,25 +184,25 @@ public class CatalogInitialization {
 //        B6) go to C1
 
         if (initialUri!=null) {
-            populateInitialFromUri(catalog, managementContext, initialUri, PopulateMode.AUTODETECT);
+            populateInitialFromUri(catalog, initialUri, PopulateMode.AUTODETECT);
             return;
         }
         
         String catalogUrl = managementContext.getConfig().getConfig(BrooklynServerConfig.BROOKLYN_CATALOG_URL);
         if (Strings.isNonBlank(catalogUrl)) {
-            populateInitialFromUri(catalog, managementContext, catalogUrl, PopulateMode.AUTODETECT);
+            populateInitialFromUri(catalog, catalogUrl, PopulateMode.AUTODETECT);
             return;
         }
         
         catalogUrl = Urls.mergePaths(BrooklynServerConfig.getMgmtBaseDir( managementContext.getConfig() ), "catalog.bom");
         if (new File(catalogUrl).exists()) {
-            populateInitialFromUri(catalog, managementContext, "file:"+catalogUrl, PopulateMode.YAML);
+            populateInitialFromUri(catalog, "file:"+catalogUrl, PopulateMode.YAML);
             return;
         }
         
         catalogUrl = Urls.mergePaths(BrooklynServerConfig.getMgmtBaseDir( managementContext.getConfig() ), "catalog.xml");
         if (new File(catalogUrl).exists()) {
-            populateInitialFromUri(catalog, managementContext, "file:"+catalogUrl, PopulateMode.XML);
+            populateInitialFromUri(catalog, "file:"+catalogUrl, PopulateMode.XML);
             return;
         }
 
@@ -195,7 +213,7 @@ public class CatalogInitialization {
         
         catalogUrl = "classpath:/brooklyn/default.catalog.bom";
         if (new ResourceUtils(this).doesUrlExist(catalogUrl)) {
-            populateInitialFromUri(catalog, managementContext, catalogUrl, PopulateMode.YAML);
+            populateInitialFromUri(catalog, catalogUrl, PopulateMode.YAML);
             return;
         }
         
@@ -203,7 +221,7 @@ public class CatalogInitialization {
         return;
     }
     
-    private void populateInitialFromUri(BasicBrooklynCatalog catalog, ManagementContext managementContext, String catalogUrl, PopulateMode mode) {
+    private void populateInitialFromUri(BasicBrooklynCatalog catalog, String catalogUrl, PopulateMode mode) {
         log.debug("Loading initial catalog from {}", catalogUrl);
 
         Exception problem = null;
@@ -260,7 +278,7 @@ public class CatalogInitialization {
         return problem;
     }
 
-    protected void populateAdditions(BasicBrooklynCatalog catalog, ManagementContext mgmt) {
+    protected void populateAdditions(BasicBrooklynCatalog catalog) {
         if (Strings.isNonBlank(additionsUri)) {
             if (disallowLocal) {
                 if (!hasRun()) {
@@ -281,17 +299,18 @@ public class CatalogInitialization {
         }
     }
 
-    protected void populateViaCallbacks(BasicBrooklynCatalog catalog, ManagementContext managementContext) {
-        for (Function<ManagementContext, Void> callback: callbacks)
-            callback.apply(managementContext);
+    protected void populateViaCallbacks(BasicBrooklynCatalog catalog) {
+        for (Function<CatalogInitialization, Void> callback: callbacks)
+            callback.apply(this);
     }
 
     private boolean setFromCatalogLoadMode = false;
+
     /** @deprecated since introduced in 0.7.0, only for legacy compatibility with 
      * {@link CatalogLoadMode} {@link BrooklynServerConfig#CATALOG_LOAD_MODE},
      * allowing control of catalog loading from a brooklyn property */
     @Deprecated
-    public void applyCatalogLoadMode(ManagementContext managementContext) {
+    public void applyCatalogLoadMode() {
         if (setFromCatalogLoadMode) return;
         setFromCatalogLoadMode = true;
         Maybe<Object> clmm = ((ManagementContextInternal)managementContext).getConfig().getConfigRaw(BrooklynServerConfig.CATALOG_LOAD_MODE, false);
@@ -314,7 +333,7 @@ public class CatalogInitialization {
     /** makes the catalog, warning if persistence is on and hasn't run yet 
      * (as the catalog will be subsequently replaced) */
     @Beta
-    public BrooklynCatalog getCatalogPopulatingBestEffort(ManagementContext managementContext) {
+    public BrooklynCatalog getCatalogPopulatingBestEffort() {
         Maybe<BrooklynCatalog> cm = ((ManagementContextInternal)managementContext).getCatalogIfSet();
         if (cm.isPresent()) return cm.get();
 
@@ -323,7 +342,7 @@ public class CatalogInitialization {
         if (oldC==null) {
             // our catalog was added, so run population
             // NB: we need the catalog to be saved already so that we can run callbacks
-            populateCatalog(managementContext, (BasicBrooklynCatalog) managementContext.getCatalog(), true, true, null);
+            populateCatalog((BasicBrooklynCatalog) managementContext.getCatalog(), true, true, null);
         }
         
         return managementContext.getCatalog();
@@ -351,5 +370,30 @@ public class CatalogInitialization {
         }
         return cm.get();
     }
+
+    public void setStartingUp(boolean isStartingUp) {
+        this.isStartingUp = isStartingUp;
+    }
+
+    public void setFailOnStartupErrors(boolean startupFailOnCatalogErrors) {
+        this.failOnStartupErrors = startupFailOnCatalogErrors;
+    }
+
+    public void handleException(Throwable throwable, Object details) {
+        if (throwable instanceof InterruptedException)
+            throw new RuntimeInterruptedException((InterruptedException) throwable);
+        if (throwable instanceof RuntimeInterruptedException)
+            throw (RuntimeInterruptedException) throwable;
+
+        log.error("Error loading catalog item '"+details+"': "+throwable);
+        log.debug("Trace for error loading catalog item '"+details+"': "+throwable, throwable);
+
+        // TODO give more detail when adding
+        ((ManagementContextInternal)getManagementContext()).errors().add(throwable);
+        
+        if (isStartingUp && failOnStartupErrors) {
+            throw new FatalRuntimeException("Unable to load catalog item '"+details+"': "+throwable, throwable);
+        }
+    }
     
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
index b5af10c..13d02e8 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java
@@ -345,7 +345,8 @@ public abstract class RebindIteration {
         
         Collection<CatalogItem<?, ?>> catalogItems = rebindContext.getCatalogItems();
         CatalogInitialization catInit = ((ManagementContextInternal)managementContext).getCatalogInitialization();
-        catInit.applyCatalogLoadMode(managementContext);
+        catInit.injectManagementContext(managementContext);
+        catInit.applyCatalogLoadMode();
         Collection<CatalogItem<?,?>> itemsForResettingCatalog = null;
         boolean needsInitialCatalog;
         if (rebindManager.persistCatalogItemsEnabled) {
@@ -390,7 +391,7 @@ public abstract class RebindIteration {
         }
 
         // TODO in read-only mode, perhaps do this less frequently than entities etc ?
-        catInit.populateCatalog(managementContext, needsInitialCatalog, itemsForResettingCatalog);
+        catInit.populateCatalog(needsInitialCatalog, itemsForResettingCatalog);
     }
 
     protected void instantiateLocationsAndEntities() {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
index de3460b..8ff6dfc 100644
--- a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
+++ b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
@@ -380,7 +380,7 @@ public abstract class AbstractManagementContext implements ManagementContextInte
         // catalog init is needed; normally this will be done from start sequence,
         // but if accessed early -- and in tests -- we will load it here
         // TODO log if in launcher mode
-        return getCatalogInitialization().getCatalogPopulatingBestEffort(this);
+        return getCatalogInitialization().getCatalogPopulatingBestEffort();
     }
 
     /**
@@ -460,6 +460,7 @@ public abstract class AbstractManagementContext implements ManagementContextInte
     
     @Override
     public synchronized void setCatalogInitialization(CatalogInitialization catalogInitialization) {
+        if (catalogInitialization!=null) catalogInitialization.injectManagementContext(this);
         this.catalogInitialization = catalogInitialization;
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
index 9c94697..212212f 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java
@@ -31,6 +31,7 @@ import org.slf4j.LoggerFactory;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import brooklyn.catalog.internal.CatalogInitialization;
 import brooklyn.entity.Application;
 import brooklyn.entity.basic.AbstractApplication;
 import brooklyn.entity.basic.ApplicationBuilder;
@@ -133,10 +134,10 @@ public class RebindCatalogEntityTest extends RebindTestFixture<StartableApplicat
         // ucl.close is only introduced in java 1.7
         if (ucl instanceof Closeable) ((Closeable)ucl).close();
 
-        newManagementContext.getCatalogInitialization().addPopulationCallback(new Function<ManagementContext, Void>() {
+        newManagementContext.getCatalogInitialization().addPopulationCallback(new Function<CatalogInitialization, Void>() {
             @Override
-            public Void apply(ManagementContext input) {
-                input.getCatalog().addItem(appClazz);
+            public Void apply(CatalogInitialization input) {
+                input.getManagementContext().getCatalog().addItem(appClazz);
                 return null;
             }
         });

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/usage/cli/src/main/java/brooklyn/cli/Main.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/brooklyn/cli/Main.java b/usage/cli/src/main/java/brooklyn/cli/Main.java
index 7dab5aa..ad6aeb1 100644
--- a/usage/cli/src/main/java/brooklyn/cli/Main.java
+++ b/usage/cli/src/main/java/brooklyn/cli/Main.java
@@ -75,7 +75,6 @@ import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.FatalConfigurationRuntimeException;
 import brooklyn.util.exceptions.FatalRuntimeException;
-import brooklyn.util.exceptions.RuntimeInterruptedException;
 import brooklyn.util.exceptions.UserFacingException;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.javalang.Enums;
@@ -192,11 +191,11 @@ public class Main extends AbstractMain {
     public static class LaunchCommand extends BrooklynCommandCollectingArgs {
 
         @Option(name = { "--localBrooklynProperties" }, title = "local brooklyn.properties file",
-                description = "local brooklyn.properties file, specific to this launch (appending to and overriding global properties)")
+                description = "Load the given properties file, specific to this launch (appending to and overriding global properties)")
         public String localBrooklynProperties;
 
         @Option(name = { "--noGlobalBrooklynProperties" }, title = "do not use any global brooklyn.properties file found",
-            description = "do not use the default global brooklyn.properties file found")
+            description = "Do not use the default global brooklyn.properties file found")
         public boolean noGlobalBrooklynProperties = false;
 
         @Option(name = { "-a", "--app" }, title = "application class or file",
@@ -223,7 +222,7 @@ public class Main extends AbstractMain {
                 + "if nothing is yet persisted in the catalog (or if it is reset)")
         public String catalogInitial;
 
-        @Option(name = { "--catalogReset" }, title = "clear catalog",
+        @Option(name = { "--catalogReset" }, 
             description = "Specifies that any catalog items which have been persisted should be cleared")
         public boolean catalogReset;
 
@@ -231,22 +230,22 @@ public class Main extends AbstractMain {
             description = "Specifies a catalog.bom to be added to the catalog")
         public String catalogAdd;
 
-        @Option(name = { "--catalogForce" }, title = "force catalog addition",
+        @Option(name = { "--catalogForce" }, 
             description = "Specifies that catalog items added via the CLI should be forcibly added, "
                 + "replacing any identical versions already registered (use with care!)")
         public boolean catalogForce;
 
         @Option(name = { "-p", "--port" }, title = "port number",
-                description = "Specifies the port to be used by the Brooklyn Management Console; "
+                description = "Use this port for the brooklyn management web console and REST API; "
                     + "default is 8081+ for http, 8443+ for https.")
         public String port;
 
         @Option(name = { "--https" },
-            description = "Specifies that the server should start on https.")
+            description = "Launch the web console on https")
         public boolean useHttps = false;
         
         @Option(name = { "-nc", "--noConsole" },
-                description = "Whether to start the web console")
+                description = "Do not start the web console or REST API")
         public boolean noConsole = false;
 
         @Option(name = { "-b", "--bindAddress" },
@@ -258,26 +257,32 @@ public class Main extends AbstractMain {
         public String publicAddress = null;
 
         @Option(name = { "--noConsoleSecurity" },
-                description = "Whether to disable security for the web console with no security (i.e. no authentication required)")
+                description = "Whether to disable authentication and security filters for the web console (for use when debugging on a secure network or bound to localhost)")
         public Boolean noConsoleSecurity = false;
 
-        @Option(name = { "--ignoreWebStartupErrors" },
-            description = "Ignore web subsystem failures on startup (default is to abort if the web API fails to start, as management is not possible)")
-        public boolean ignoreWebErrors = false;
+        @Option(name = { "--startupContinueOnWebErrors" },
+            description = "Continue on web subsystem failures during startup "
+                + "(default is to abort if the web API fails to start, as management access is not normally possible)")
+        public boolean startupContinueOnWebErrors = false;
+
+        @Option(name = { "--startupFailOnPersistenceErrors" },
+            description = "Fail on persistence/HA subsystem failures during startup "
+                + "(default is to continue, so errors can be viewed via the API)")
+        public boolean startupFailOnPersistenceErrors = false;
 
-        @Option(name = { "--ignorePersistenceStartupErrors" },
-            description = "Ignore persistence/HA subsystem failures on startup "
-                + "(default is true, so errors can be viewed via the API)")
-        public boolean ignorePersistenceErrors = true;
+        @Option(name = { "--startupFailOnCatalogErrors" },
+            description = "Fail on catalog subsystem failures during startup "
+                + "(default is to continue, so errors can be viewed via the API)")
+        public boolean startupFailOnCatalogErrors = false;
 
-        @Option(name = { "--ignoreManagedAppsStartupErrors" },
-            description = "Ignore failures starting managed applications passed on the command line on startup "
-                + "(default is true, so errors can be viewed via the API)")
-        public boolean ignoreAppErrors = true;
+        @Option(name = { "--startupFailOnManagedAppsErrors" },
+            description = "Fail startup on errors deploying of managed apps specified via the command line "
+                + "(default is to continue, so errors can be viewed via the API)")
+        public boolean startupFailOnManagedAppsErrors = false;
 
         @Beta
         @Option(name = { "--startBrooklynNode" },
-                description = "Whether to start a BrooklynNode entity representing this Brooklyn instance (default false)")
+                description = "Start a BrooklynNode entity representing this Brooklyn instance")
         public boolean startBrooklynNode = false;
 
         // Note in some cases, you can get java.util.concurrent.RejectedExecutionException
@@ -285,7 +290,7 @@ public class Main extends AbstractMain {
         // looks like: {@linktourl https://gist.github.com/47066f72d6f6f79b953e}
         @Beta
         @Option(name = { "-sk", "--stopOnKeyPress" },
-                description = "After startup, shutdown on user text entry (default false)")
+                description = "Shutdown immediately on user text entry after startup (useful for debugging and demos)")
         public boolean stopOnKeyPress = false;
 
         final static String STOP_WHICH_APPS_ON_SHUTDOWN = "--stopOnShutdown";
@@ -406,13 +411,21 @@ public class Main extends AbstractMain {
                 launcher = createLauncher();
 
                 CatalogInitialization catInit = new CatalogInitialization(catalogInitial, catalogReset, catalogAdd, catalogForce);
-                catInit.addPopulationCallback(new Function<ManagementContext,Void>() {
+                catInit.addPopulationCallback(new Function<CatalogInitialization,Void>() {
                     @Override
-                    public Void apply(ManagementContext mgmt) {
-                        populateCatalog(mgmt.getCatalog());
+                    public Void apply(CatalogInitialization catInit) {
+                        try {
+                            populateCatalog(catInit.getManagementContext().getCatalog());
+                        } catch (Throwable e) {
+                            catInit.handleException(e, "overridden main class populate catalog");
+                        }
+                        
+                        // Force load of catalog (so web console is up to date)
+                        confirmCatalog(catInit);
                         return null;
                     }
                 });
+                catInit.setFailOnStartupErrors(startupFailOnCatalogErrors);
                 launcher.catalogInitialization(catInit);
                 
                 launcher.persistMode(persistMode);
@@ -557,9 +570,10 @@ public class Main extends AbstractMain {
             BrooklynLauncher launcher;
             launcher = BrooklynLauncher.newInstance();
             launcher.localBrooklynPropertiesFile(localBrooklynProperties)
-                    .ignorePersistenceErrors(ignorePersistenceErrors)
-                    .ignoreWebErrors(ignoreWebErrors)
-                    .ignoreAppErrors(ignoreAppErrors)
+                    .ignorePersistenceErrors(!startupFailOnPersistenceErrors)
+                    .ignoreCatalogErrors(!startupFailOnCatalogErrors)
+                    .ignoreWebErrors(startupContinueOnWebErrors)
+                    .ignoreAppErrors(!startupFailOnManagedAppsErrors)
                     .locations(Strings.isBlank(locations) ? ImmutableList.<String>of() : JavaStringEscapes.unwrapJsonishListIfPossible(locations));
             
             launcher.webconsole(!noConsole);
@@ -597,17 +611,15 @@ public class Main extends AbstractMain {
             return launcher;
         }
 
-        /** method intended for subclassing, to add items to the catalog */
+        /** method intended for subclassing, to add custom items to the catalog */
         protected void populateCatalog(BrooklynCatalog catalog) {
-            // Force load of catalog (so web console is up to date)
-            confirmCatalog(catalog);
-
             // nothing else added here
         }
 
-        protected void confirmCatalog(BrooklynCatalog catalog) {
+        protected void confirmCatalog(CatalogInitialization catInit) {
             // Force load of catalog (so web console is up to date)
             Stopwatch time = Stopwatch.createStarted();
+            BrooklynCatalog catalog = catInit.getManagementContext().getCatalog();
             Iterable<CatalogItem<Object, Object>> items = catalog.getCatalogItems();
             for (CatalogItem<Object, Object> item: items) {
                 try {
@@ -617,14 +629,7 @@ public class Main extends AbstractMain {
                     }
                     log.debug("Catalog loaded spec "+spec+" for item "+item);                      
                 } catch (Throwable throwable) {
-                    // swallow errors, apart from interrupted
-                    if (throwable instanceof InterruptedException)
-                        throw new RuntimeInterruptedException((InterruptedException) throwable);
-                    if (throwable instanceof RuntimeInterruptedException)
-                        throw (RuntimeInterruptedException) throwable;
-
-                    log.error("Error loading catalog item '"+item+"': "+throwable);
-                    log.debug("Trace for error loading catalog item '"+item+"': "+throwable, throwable);
+                    catInit.handleException(throwable, item);
                 }
             }
             log.debug("Catalog (size "+Iterables.size(items)+") confirmed in "+Duration.of(time));                      
@@ -777,9 +782,10 @@ public class Main extends AbstractMain {
                     .add("bindAddress", bindAddress)
                     .add("noConsole", noConsole)
                     .add("noConsoleSecurity", noConsoleSecurity)
-                    .add("ignorePersistenceErrors", ignorePersistenceErrors)
-                    .add("ignoreWebErrors", ignoreWebErrors)
-                    .add("ignoreAppErrors", ignoreAppErrors)
+                    .add("startupFailOnPersistenceErrors", startupFailOnPersistenceErrors)
+                    .add("startupFailsOnCatalogErrors", startupFailOnCatalogErrors)
+                    .add("startupContinueOnWebErrors", startupContinueOnWebErrors)
+                    .add("startupFailOnManagedAppsErrors", startupFailOnManagedAppsErrors)
                     .add("stopWhichAppsOnShutdown", stopWhichAppsOnShutdown)
                     .add("noShutdownOnExit", noShutdownOnExit)
                     .add("stopOnKeyPress", stopOnKeyPress)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
index 4d3b1be..71f53a5 100644
--- a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
+++ b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
@@ -152,6 +152,7 @@ public class BrooklynLauncher {
     
     private boolean ignoreWebErrors = false;
     private boolean ignorePersistenceErrors = true;
+    private boolean ignoreCatalogErrors = true;
     private boolean ignoreAppErrors = true;
     
     private StopWhichAppsOnShutdown stopWhichAppsOnShutdown = StopWhichAppsOnShutdown.THESE_IF_NOT_PERSISTED;
@@ -401,6 +402,11 @@ public class BrooklynLauncher {
         return this;
     }
 
+    public BrooklynLauncher ignoreCatalogErrors(boolean ignoreCatalogErrors) {
+        this.ignoreCatalogErrors = ignoreCatalogErrors;
+        return this;
+    }
+
     public BrooklynLauncher ignoreWebErrors(boolean ignoreWebErrors) {
         this.ignoreWebErrors = ignoreWebErrors;
         return this;
@@ -562,6 +568,10 @@ public class BrooklynLauncher {
         // Create the management context
         initManagementContext();
 
+        // Inform catalog initialization that it is starting up
+        CatalogInitialization catInit = ((ManagementContextInternal)managementContext).getCatalogInitialization();
+        catInit.setStartingUp(true);
+
         // Start webapps as soon as mgmt context available -- can use them to detect progress of other processes
         if (startWebApps) {
             try {
@@ -586,15 +596,16 @@ public class BrooklynLauncher {
         }
 
         try {
-            // run cat init now if it hasn't yet been run
-            CatalogInitialization catInit = ((ManagementContextInternal)managementContext).getCatalogInitialization();
+            // run cat init now if it hasn't yet been run; 
+            // will also run if there was an ignored error in catalog above, allowing it to fail startup here if requested
             if (catInit!=null && !catInit.hasRun()) {
                 LOG.debug("Loading catalog as part of launcher (persistence did not run it)");
-                catInit.populateCatalog(managementContext, true, null);
+                catInit.populateCatalog(true, null);
             }
         } catch (Exception e) {
-            handleSubsystemStartupError(true, "initial catalog", e);
+            handleSubsystemStartupError(ignoreCatalogErrors, "initial catalog", e);
         }
+        catInit.setStartingUp(false);
 
         // Create the locations. Must happen after persistence is started in case the
         // management context's catalog is loaded from persisted state. (Location
@@ -612,7 +623,7 @@ public class BrooklynLauncher {
             try {
                 startBrooklynNode();
             } catch (Exception e) {
-                handleSubsystemStartupError(true, "brooklyn node / self entity", e);
+                handleSubsystemStartupError(ignoreAppErrors, "brooklyn node / self entity", e);
             }
         }
         

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java
index b4f963e..0886390 100644
--- a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java
+++ b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java
@@ -317,9 +317,9 @@ public class BrooklynLauncherTest {
     @Test  // takes a bit of time because starts webapp, but also tests rest api so useful
     public void testErrorsCaughtByApiAndRestApiWorks() throws Exception {
         launcher = newLauncherForTests(true)
-                .catalogInitialization(new CatalogInitialization(null, false, null, false).addPopulationCallback(new Function<ManagementContext, Void>() {
+                .catalogInitialization(new CatalogInitialization(null, false, null, false).addPopulationCallback(new Function<CatalogInitialization, Void>() {
                     @Override
-                    public Void apply(ManagementContext input) {
+                    public Void apply(CatalogInitialization input) {
                         throw new RuntimeException("deliberate-exception-for-testing");
                     }
                 }))



[12/23] incubator-brooklyn git commit: improve the clouds machine namer

Posted by he...@apache.org.
improve the clouds machine namer

make it an interface, move to sub-package, give good javadoc, and make the methods a little bit more intuitive.

fixes problems that CustomMachineNamer wasn't actually being invoked anymore since we called in via groups!
all the salting (unique id) stuff is a little confused, because different jclouds providers seem to do it in different ways.


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

Branch: refs/heads/master
Commit: 8008df4f2da54bc13bf373c6179cb889e165eec7
Parents: fa09efc
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri May 8 14:01:49 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:51:49 2015 +0100

----------------------------------------------------------------------
 .../location/cloud/CloudLocationConfig.java     |   6 +-
 .../location/cloud/CloudMachineNamer.java       | 169 -------------------
 .../location/cloud/CustomMachineNamer.java      |  72 --------
 .../cloud/names/AbstractCloudMachineNamer.java  | 149 ++++++++++++++++
 .../cloud/names/BasicCloudMachineNamer.java     |  91 ++++++++++
 .../location/cloud/names/CloudMachineNamer.java |  61 +++++++
 .../cloud/names/CustomMachineNamer.java         |  72 ++++++++
 .../location/cloud/CloudMachineNamerTest.java   |  45 ++---
 .../location/cloud/CustomMachineNamerTest.java  |   7 +-
 .../location/jclouds/JcloudsLocation.java       |  10 +-
 .../location/jclouds/JcloudsLocationConfig.java |   1 +
 .../location/jclouds/JcloudsMachineNamer.java   |  11 +-
 .../location/jclouds/JcloudsLocationTest.java   |   8 +-
 .../jclouds/JcloudsMachineNamerTest.java        |  10 +-
 .../entity/service/InitdServiceInstaller.java   |   4 +-
 15 files changed, 430 insertions(+), 286 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8008df4f/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java b/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
index 5aad7bd..39ef6fa 100644
--- a/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
+++ b/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
@@ -59,7 +59,11 @@ public interface CloudLocationConfig {
 
     // default is just shy of common 64-char boundary (could perhaps increase slightly...)
     public static final ConfigKey<Integer> VM_NAME_MAX_LENGTH = ConfigKeys.newIntegerConfigKey(
-            "vmNameMaxLength", "Maximum length of VM name", 61);
+        "vmNameMaxLength", "Maximum length of VM name", 61);
+
+    public static final ConfigKey<Integer> VM_NAME_SALT_LENGTH = ConfigKeys.newIntegerConfigKey(
+        "vmNameSaltLength", "Number of characters to use for a random identifier inserted in hostname "
+            + "to uniquely identify machines", 4);
 
     public static final ConfigKey<String> WAIT_FOR_SSHABLE = ConfigKeys.newStringConfigKey("waitForSshable", 
             "Whether and how long to wait for a newly provisioned VM to be accessible via ssh; " +

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8008df4f/core/src/main/java/brooklyn/location/cloud/CloudMachineNamer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/cloud/CloudMachineNamer.java b/core/src/main/java/brooklyn/location/cloud/CloudMachineNamer.java
deleted file mode 100644
index 5200a1c..0000000
--- a/core/src/main/java/brooklyn/location/cloud/CloudMachineNamer.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.location.cloud;
-
-import brooklyn.entity.Application;
-import brooklyn.entity.Entity;
-import brooklyn.entity.trait.HasShortName;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.text.Identifiers;
-import brooklyn.util.text.StringShortener;
-import brooklyn.util.text.Strings;
-
-import com.google.common.base.CharMatcher;
-
-public class CloudMachineNamer {
-
-    protected final ConfigBag setup;
-    int defaultMachineNameMaxLength = CloudLocationConfig.VM_NAME_MAX_LENGTH.getDefaultValue();
-    int nameInGroupReservedLength = 9;
-
-    public CloudMachineNamer(ConfigBag setup) {
-        this.setup = setup;
-    }
-    
-    public String generateNewMachineUniqueName() {
-        return generateNewIdReservingLength(0);
-    }
-    
-    public String generateNewMachineUniqueNameFromGroupId(String groupId) {
-        int suffixLength = getMaxNameLength() - (groupId.length() + 1); // +1 for the hyphen
-        if (suffixLength <= 0) {
-            return groupId;
-        }
-            
-        return groupId + "-" + Identifiers.makeRandomId(Math.min(4, suffixLength));
-    }
-
-    public String generateNewGroupId() {
-        return generateNewIdReservingLength(nameInGroupReservedLength);
-    }
-    
-    protected String generateNewIdReservingLength(int lengthToReserve) {
-        Object context = setup.peek(CloudLocationConfig.CALLER_CONTEXT);
-        Entity entity = null;
-        if (context instanceof Entity) entity = (Entity) context;
-        
-        StringShortener shortener = Strings.shortener().separator("-");
-        shortener.append("system", "brooklyn");
-        
-        // randId often not necessary, as an 8-char hex identifier is added later (in jclouds? can we override?)
-        // however it can be useful to have this early in the string, to prevent collisions in places where it is abbreviated 
-        shortener.append("randId", Identifiers.makeRandomId(4));
-        
-        String user = System.getProperty("user.name");
-        if (!"brooklyn".equals(user))
-            // include user; unless the user is 'brooklyn', as 'brooklyn-brooklyn-' is just silly!
-            shortener.append("user", user);
-        
-        if (entity!=null) {
-            Application app = entity.getApplication();
-            if (app!=null) {
-                shortener.append("app", shortName(app))
-                        .append("appId", app.getId());
-            }
-            shortener.append("entity", shortName(entity))
-                    .append("entityId", entity.getId());
-        } else if (context!=null) {
-            shortener.append("context", context.toString());
-        }
-        
-        shortener.truncate("user", 12)
-                .truncate("app", 16)
-                .truncate("entity", 16)
-                .truncate("appId", 4)
-                .truncate("entityId", 4)
-                .truncate("context", 12);
-        
-        shortener.canTruncate("user", 8)
-                .canTruncate("app", 5)
-                .canTruncate("entity", 5)
-                .canTruncate("system", 2)
-                .canTruncate("app", 3)
-                .canTruncate("entity", 3)
-                .canRemove("app")
-                .canTruncate("user", 4)
-                .canRemove("entity")
-                .canTruncate("context", 4)
-                .canTruncate("randId", 2)
-                .canRemove("user")
-                .canTruncate("appId", 2)
-                .canRemove("appId");
-        
-        int len = getMaxNameLength();
-        // decrement by e.g. 9 chars because jclouds adds that (dash plus 8 for hex id)
-        len -= lengthToReserve;
-        if (len<=0) return "";
-        String s = shortener.getStringOfMaxLength(len);
-        return sanitize(s).toLowerCase();
-    }
-
-    /** returns the max length of a VM name for the cloud specified in setup;
-     * this value is typically decremented by 9 to make room for jclouds labels;
-     * delegates to {@link #getCustomMaxNameLength()} when 
-     * {@link CloudLocationConfig#VM_NAME_MAX_LENGTH} is not set */
-    public int getMaxNameLength() {
-        if (setup.containsKey(CloudLocationConfig.VM_NAME_MAX_LENGTH)) {
-            // if a length is set, use that
-            return setup.get(CloudLocationConfig.VM_NAME_MAX_LENGTH);
-        }
-        
-        Integer custom = getCustomMaxNameLength();
-        if (custom!=null) return custom;
-        
-        // return the default
-        return defaultMachineNameMaxLength;  
-    }
-    
-    public CloudMachineNamer lengthMaxPermittedForMachineName(int defaultMaxLength) {
-        this.defaultMachineNameMaxLength = defaultMaxLength;
-        return this;
-    }
-
-    /** number of chars to use or reserve for the machine identifier when constructing a group identifier;
-     * defaults to 9, e.g. a hyphen and 8 random chars which is the jclouds model 
-     * @return */
-    public CloudMachineNamer lengthReservedForNameInGroup(int identifierRequiredLength) {
-        this.nameInGroupReservedLength = identifierRequiredLength;
-        return this;
-    }
-    
-    /** method for overriding to provide custom logic when an explicit config key is not set */
-    public Integer getCustomMaxNameLength() {
-        return null;
-    }
-
-    protected String shortName(Object x) {
-        if (x instanceof HasShortName) {
-            return ((HasShortName)x).getShortName();
-        }
-        if (x instanceof Entity) {
-            return ((Entity)x).getDisplayName();
-        }
-        return x.toString();
-    }
-
-    public static String sanitize(String s) {
-        return CharMatcher.inRange('A', 'Z')
-                .or(CharMatcher.inRange('a', 'z'))
-                .or(CharMatcher.inRange('0', '9'))
-                .negate()
-                .trimAndCollapseFrom(s, '-');
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8008df4f/core/src/main/java/brooklyn/location/cloud/CustomMachineNamer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/cloud/CustomMachineNamer.java b/core/src/main/java/brooklyn/location/cloud/CustomMachineNamer.java
deleted file mode 100644
index c818dc9..0000000
--- a/core/src/main/java/brooklyn/location/cloud/CustomMachineNamer.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.location.cloud;
-
-import java.util.Map;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.Entity;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.util.config.ConfigBag;
-import brooklyn.util.text.Strings;
-import brooklyn.util.text.TemplateProcessor;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.reflect.TypeToken;
-
-public class CustomMachineNamer extends CloudMachineNamer {
-    
-    public static final ConfigKey<String> MACHINE_NAME_TEMPLATE = ConfigKeys.newStringConfigKey("custom.machine.namer.machine", 
-            "Freemarker template format for custom machine name", "${entity.displayName}");
-    @SuppressWarnings("serial")
-    public static final ConfigKey<Map<String, ?>> EXTRA_SUBSTITUTIONS = ConfigKeys.newConfigKey(new TypeToken<Map<String, ?>>() {}, 
-            "custom.machine.namer.substitutions", "Additional substitutions to be used in the template", ImmutableMap.<String, Object>of());
-    
-    public CustomMachineNamer(ConfigBag setup) {
-        super(setup);
-    }
-    
-    @Override
-    public String generateNewMachineUniqueName() {
-        Object context = setup.peek(CloudLocationConfig.CALLER_CONTEXT);
-        Entity entity = null;
-        if (context instanceof Entity) {
-            entity = (Entity) context;
-        }
-        
-        String template = this.setup.get(MACHINE_NAME_TEMPLATE);
-        
-        String processed;
-        if (entity == null) {
-            processed = TemplateProcessor.processTemplateContents(template, this.setup.get(EXTRA_SUBSTITUTIONS));
-        } else {
-            processed = TemplateProcessor.processTemplateContents(template, (EntityInternal)entity, this.setup.get(EXTRA_SUBSTITUTIONS));
-        }
-        
-        processed = Strings.removeFromStart(processed, "#ftl\n");
-        
-        return sanitize(processed);
-    }
-    
-    @Override
-    public String generateNewMachineUniqueNameFromGroupId(String groupId) {
-        return generateNewMachineUniqueName();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8008df4f/core/src/main/java/brooklyn/location/cloud/names/AbstractCloudMachineNamer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/cloud/names/AbstractCloudMachineNamer.java b/core/src/main/java/brooklyn/location/cloud/names/AbstractCloudMachineNamer.java
new file mode 100644
index 0000000..4e81a46
--- /dev/null
+++ b/core/src/main/java/brooklyn/location/cloud/names/AbstractCloudMachineNamer.java
@@ -0,0 +1,149 @@
+/*
+ * 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.location.cloud.names;
+
+import brooklyn.entity.Entity;
+import brooklyn.entity.trait.HasShortName;
+import brooklyn.location.cloud.CloudLocationConfig;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.text.Identifiers;
+import brooklyn.util.text.Strings;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.CharMatcher;
+
+/** 
+ * Implements <b>most</b> of {@link CloudMachineNamer},
+ * leaving just one method -- {@link #generateNewIdOfLength(int)} --
+ * for subclasses to provide.
+ * <p>
+ * {@link CloudLocationConfig#VM_NAME_MAX_LENGTH} is used to find the VM length, 
+ * unless {@link #getCustomMaxNameLength(ConfigBag)} is overridden or
+ * {@link #setDefaultMachineNameMaxLength(int)} invoked on the instance supplied.
+ */
+public abstract class AbstractCloudMachineNamer implements CloudMachineNamer {
+
+    int defaultMachineNameMaxLength = CloudLocationConfig.VM_NAME_MAX_LENGTH.getDefaultValue();
+    int defaultMachineNameSaltLength = CloudLocationConfig.VM_NAME_SALT_LENGTH.getDefaultValue();
+    protected String separator = "-";
+
+    public String generateNewMachineUniqueName(ConfigBag setup) {
+        return generateNewIdReservingLength(setup, 0);
+    }
+    
+    public String generateNewMachineUniqueNameFromGroupId(ConfigBag setup, String groupId) {
+        int availSaltLength = getMaxNameLength(setup) - (groupId.length() + separator.length());
+        int requestedSaltLength = getLengthForMachineUniqueNameSalt(setup, false);
+        if (availSaltLength <= 0 || requestedSaltLength <= 0) {
+            return groupId;
+        }
+            
+        return sanitize(groupId + separator + Identifiers.makeRandomId(Math.min(requestedSaltLength, availSaltLength))).toLowerCase();
+    }
+
+    public String generateNewGroupId(ConfigBag setup) {
+        return sanitize(generateNewIdReservingLength(setup, getLengthForMachineUniqueNameSalt(setup, true))).toLowerCase();
+    }
+
+    protected String generateNewIdReservingLength(ConfigBag setup, int lengthToReserve) {
+        int len = getMaxNameLength(setup);
+        // decrement by e.g. 9 chars because jclouds adds that (dash plus 8 for hex id)
+        len -= lengthToReserve;
+        if (len<=0) return "";
+        return Strings.maxlen(generateNewIdOfLength(setup, len), len);
+    }
+    
+    /** Method for subclasses to provide to construct the context-specific part of an identifier,
+     * for use in {@link #generateNewGroupId()} and {@link #generateNewMachineUniqueName()}.
+     * 
+     * @param maxLengthHint an indication of the maximum length permitted for the ID generated,
+     * supplied for implementations which wish to use this information to decide what to truncate.
+     * (This class will truncate any return values longer than this.) 
+     */
+    protected abstract String generateNewIdOfLength(ConfigBag setup, int maxLengthHint);
+
+    /** Returns the max length of a VM name for the cloud specified in setup;
+     * this value is typically decremented by 9 to make room for jclouds labels;
+     * delegates to {@link #getCustomMaxNameLength()} when 
+     * {@link CloudLocationConfig#VM_NAME_MAX_LENGTH} is not set */
+    public int getMaxNameLength(ConfigBag setup) {
+        if (setup.containsKey(CloudLocationConfig.VM_NAME_MAX_LENGTH)) {
+            // if a length is set explicitly, use that (but intercept default behaviour)
+            return setup.get(CloudLocationConfig.VM_NAME_MAX_LENGTH);
+        }
+        
+        Integer custom = getCustomMaxNameLength(setup);
+        if (custom!=null) return custom;
+        
+        // return the default
+        return defaultMachineNameMaxLength;  
+    }
+    
+    public int getLengthForMachineUniqueNameSalt(ConfigBag setup, boolean includeSeparator) {
+        int saltLen;
+        if (setup.containsKey(CloudLocationConfig.VM_NAME_SALT_LENGTH)) {
+            saltLen = setup.get(CloudLocationConfig.VM_NAME_SALT_LENGTH);
+        } else {
+            // default value comes from key, but custom default can be set
+            saltLen = defaultMachineNameSaltLength;
+        }
+        
+        if (saltLen>0 && includeSeparator)
+            saltLen += separator.length();
+        
+        return saltLen;
+    }
+    
+    public AbstractCloudMachineNamer setDefaultMachineNameMaxLength(int defaultMaxLength) {
+        this.defaultMachineNameMaxLength = defaultMaxLength;
+        return this;
+    }
+
+    /** Number of chars to use or reserve for the machine identifier when constructing a group identifier;
+     * jclouds for instance uses "-" plus 8 */
+    public AbstractCloudMachineNamer setDefaultMachineNameSeparatorAndSaltLength(String separator, int defaultMachineUniqueNameSaltLength) {
+        this.separator = separator;
+        this.defaultMachineNameSaltLength = defaultMachineUniqueNameSaltLength;
+        return this;
+    }
+    
+    /** Method for overriding to provide custom logic when an explicit config key is not set for the machine length. */
+    public Integer getCustomMaxNameLength(ConfigBag setup) {
+        return null;
+    }
+
+    protected static String shortName(Object x) {
+        if (x instanceof HasShortName) {
+            return ((HasShortName)x).getShortName();
+        }
+        if (x instanceof Entity) {
+            return ((Entity)x).getDisplayName();
+        }
+        return x.toString();
+    }
+
+    @Beta //probably won't live here long-term
+    public static String sanitize(String s) {
+        return CharMatcher.inRange('A', 'Z')
+                .or(CharMatcher.inRange('a', 'z'))
+                .or(CharMatcher.inRange('0', '9'))
+                .negate()
+                .trimAndCollapseFrom(s, '-');
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8008df4f/core/src/main/java/brooklyn/location/cloud/names/BasicCloudMachineNamer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/cloud/names/BasicCloudMachineNamer.java b/core/src/main/java/brooklyn/location/cloud/names/BasicCloudMachineNamer.java
new file mode 100644
index 0000000..626a7ea
--- /dev/null
+++ b/core/src/main/java/brooklyn/location/cloud/names/BasicCloudMachineNamer.java
@@ -0,0 +1,91 @@
+/*
+ * 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.location.cloud.names;
+
+import brooklyn.entity.Application;
+import brooklyn.entity.Entity;
+import brooklyn.location.cloud.CloudLocationConfig;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.text.Identifiers;
+import brooklyn.util.text.StringShortener;
+import brooklyn.util.text.Strings;
+
+/** 
+ * Standard implementation of {@link CloudMachineNamer},
+ * which looks at several of the properties of the context (entity)
+ * and is clever about abbreviating them. */
+public class BasicCloudMachineNamer extends AbstractCloudMachineNamer {
+
+    @Override
+    protected String generateNewIdOfLength(ConfigBag setup, int len) {
+        Object context = setup.peek(CloudLocationConfig.CALLER_CONTEXT);
+        Entity entity = null;
+        if (context instanceof Entity) entity = (Entity) context;
+        
+        StringShortener shortener = Strings.shortener().separator("-");
+        shortener.append("system", "brooklyn");
+        
+        // randId often not necessary, as an 8-char hex identifier is added later (in jclouds? can we override?)
+        // however it can be useful to have this early in the string, to prevent collisions in places where it is abbreviated 
+        shortener.append("randId", Identifiers.makeRandomId(4));
+        
+        String user = System.getProperty("user.name");
+        if (!"brooklyn".equals(user))
+            // include user; unless the user is 'brooklyn', as 'brooklyn-brooklyn-' is just silly!
+            shortener.append("user", user);
+        
+        if (entity!=null) {
+            Application app = entity.getApplication();
+            if (app!=null) {
+                shortener.append("app", shortName(app))
+                        .append("appId", app.getId());
+            }
+            shortener.append("entity", shortName(entity))
+                    .append("entityId", entity.getId());
+        } else if (context!=null) {
+            shortener.append("context", context.toString());
+        }
+        
+        shortener.truncate("user", 12)
+                .truncate("app", 16)
+                .truncate("entity", 16)
+                .truncate("appId", 4)
+                .truncate("entityId", 4)
+                .truncate("context", 12);
+        
+        shortener.canTruncate("user", 8)
+                .canTruncate("app", 5)
+                .canTruncate("entity", 5)
+                .canTruncate("system", 2)
+                .canTruncate("app", 3)
+                .canTruncate("entity", 3)
+                .canRemove("app")
+                .canTruncate("user", 4)
+                .canRemove("entity")
+                .canTruncate("context", 4)
+                .canTruncate("randId", 2)
+                .canRemove("user")
+                .canTruncate("appId", 2)
+                .canRemove("appId");
+        
+        String s = shortener.getStringOfMaxLength(len);
+        return sanitize(s).toLowerCase();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8008df4f/core/src/main/java/brooklyn/location/cloud/names/CloudMachineNamer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/cloud/names/CloudMachineNamer.java b/core/src/main/java/brooklyn/location/cloud/names/CloudMachineNamer.java
new file mode 100644
index 0000000..022c1f4
--- /dev/null
+++ b/core/src/main/java/brooklyn/location/cloud/names/CloudMachineNamer.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.location.cloud.names;
+
+import brooklyn.entity.Entity;
+import brooklyn.location.Location;
+import brooklyn.location.cloud.CloudLocationConfig;
+import brooklyn.util.config.ConfigBag;
+
+/**
+ * Interface used to construct names for individual cloud machines and for groups of machines.
+ * <p>
+ * Implementations <b>must</b> provide a constructor which takes a single argument,
+ * being the {@link ConfigBag} for the context where the machine is being created
+ * (usually a {@link Location}).
+ * <p>
+ * With that bag, the config key {@link CloudLocationConfig#CALLER_CONTEXT}
+ * typically contains the {@link Entity} for which the machine is being created.   
+ */
+public interface CloudMachineNamer {
+
+    /**
+     * Generate a name for a new machine, based on context.
+     * <p>
+     * The name should normally be unique, as a context might produce multiple machines,
+     * for example basing it partially on information from the context but also including some random salt.
+     */
+    public String generateNewMachineUniqueName(ConfigBag setup);
+    /**
+     * Generate a name stem for a group of machines, based on context.
+     * <p>
+     * The name does not need to be unique, as uniqueness will be applied by {@link #generateNewMachineUniqueNameFromGroupId(String)}.
+     */
+    public String generateNewGroupId(ConfigBag setup);
+    
+    /**
+     * Generate a unique name from the given name stem.
+     * <p>
+     * The name stem is normally based on context information so the usual
+     * function of this method is to apply a suffix which helps to uniquely distinguish between machines
+     * in cases where the same name stem ({@link #generateNewGroupId()}) is used for multiple machines.
+     */
+    public String generateNewMachineUniqueNameFromGroupId(ConfigBag setup, String groupId);
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8008df4f/core/src/main/java/brooklyn/location/cloud/names/CustomMachineNamer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/cloud/names/CustomMachineNamer.java b/core/src/main/java/brooklyn/location/cloud/names/CustomMachineNamer.java
new file mode 100644
index 0000000..9fde17e
--- /dev/null
+++ b/core/src/main/java/brooklyn/location/cloud/names/CustomMachineNamer.java
@@ -0,0 +1,72 @@
+/*
+ * 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.location.cloud.names;
+
+import java.util.Map;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.location.cloud.CloudLocationConfig;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.text.Strings;
+import brooklyn.util.text.TemplateProcessor;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+
+/** Provides a machine namer which looks at a location config key {@link #MACHINE_NAME_TEMPLATE}
+ * to construct the hostname.
+ * For instance, setting this to <code>${config.entity_hostname}</code>
+ * will take the hostname from an <code>entity_hostname</code> key passed as entity <code>brooklyn.config</code>.
+ * <p>
+ * Note that this is not jclouds aware, so jclouds-specific cloud max lengths are not observed with this class.
+ */
+public class CustomMachineNamer extends BasicCloudMachineNamer {
+    
+    public static final ConfigKey<String> MACHINE_NAME_TEMPLATE = ConfigKeys.newStringConfigKey("custom.machine.namer.machine", 
+            "Freemarker template format for custom machine name", "${entity.displayName}");
+    @SuppressWarnings("serial")
+    public static final ConfigKey<Map<String, ?>> EXTRA_SUBSTITUTIONS = ConfigKeys.newConfigKey(new TypeToken<Map<String, ?>>() {}, 
+            "custom.machine.namer.substitutions", "Additional substitutions to be used in the template", ImmutableMap.<String, Object>of());
+    
+    @Override
+    protected String generateNewIdOfLength(ConfigBag setup, int len) {
+        Object context = setup.peek(CloudLocationConfig.CALLER_CONTEXT);
+        Entity entity = null;
+        if (context instanceof Entity) {
+            entity = (Entity) context;
+        }
+        
+        String template = setup.get(MACHINE_NAME_TEMPLATE);
+        
+        String processed;
+        if (entity == null) {
+            processed = TemplateProcessor.processTemplateContents(template, setup.get(EXTRA_SUBSTITUTIONS));
+        } else {
+            processed = TemplateProcessor.processTemplateContents(template, (EntityInternal)entity, setup.get(EXTRA_SUBSTITUTIONS));
+        }
+        
+        processed = Strings.removeFromStart(processed, "#ftl\n");
+        
+        return sanitize(processed);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8008df4f/core/src/test/java/brooklyn/location/cloud/CloudMachineNamerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/location/cloud/CloudMachineNamerTest.java b/core/src/test/java/brooklyn/location/cloud/CloudMachineNamerTest.java
index 4da08bc..0bff9d0 100644
--- a/core/src/test/java/brooklyn/location/cloud/CloudMachineNamerTest.java
+++ b/core/src/test/java/brooklyn/location/cloud/CloudMachineNamerTest.java
@@ -30,6 +30,9 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.location.cloud.names.AbstractCloudMachineNamer;
+import brooklyn.location.cloud.names.BasicCloudMachineNamer;
+import brooklyn.location.cloud.names.CloudMachineNamer;
 import brooklyn.test.entity.LocalManagementContextForTests;
 import brooklyn.test.entity.TestApplication;
 import brooklyn.test.entity.TestEntity;
@@ -54,7 +57,7 @@ public class CloudMachineNamerTest {
         ConfigBag cfg = new ConfigBag()
             .configure(CloudLocationConfig.CALLER_CONTEXT, child);
 
-        String result = new CloudMachineNamer(cfg).generateNewGroupId();
+        String result = new BasicCloudMachineNamer().generateNewGroupId(cfg);
 
         log.info("test entity child group id gives: "+result);
         // e.g. brooklyn-alex-tistapp-uube-testent-xisg-rwad
@@ -75,10 +78,10 @@ public class CloudMachineNamerTest {
 
         ConfigBag cfg = new ConfigBag()
             .configure(CloudLocationConfig.CALLER_CONTEXT, child);
-        CloudMachineNamer namer = new CloudMachineNamer(cfg);
+        BasicCloudMachineNamer namer = new BasicCloudMachineNamer();
         
-        String result = namer.generateNewMachineUniqueName();
-        Assert.assertTrue(result.length() <= namer.getMaxNameLength());
+        String result = namer.generateNewMachineUniqueName(cfg);
+        Assert.assertTrue(result.length() <= namer.getMaxNameLength(cfg));
         String user = Strings.maxlen(System.getProperty("user.name"), 4).toLowerCase();
         Assert.assertTrue(result.indexOf(user) >= 0);
         Assert.assertTrue(result.indexOf("-tistapp-") >= 0);
@@ -94,10 +97,10 @@ public class CloudMachineNamerTest {
 
         ConfigBag cfg = new ConfigBag()
             .configure(CloudLocationConfig.CALLER_CONTEXT, child);
-        CloudMachineNamer namer = new CloudMachineNamer(cfg);
+        CloudMachineNamer namer = new BasicCloudMachineNamer();
         
-        String groupId = namer.generateNewGroupId();
-        String result = namer.generateNewMachineUniqueNameFromGroupId(groupId);
+        String groupId = namer.generateNewGroupId(cfg);
+        String result = namer.generateNewMachineUniqueNameFromGroupId(cfg, groupId);
         Assert.assertTrue(result.startsWith(groupId));
         Assert.assertTrue(result.length() == groupId.length() + 5);
     }
@@ -109,24 +112,28 @@ public class CloudMachineNamerTest {
         
         ConfigBag cfg = new ConfigBag()
             .configure(CloudLocationConfig.CALLER_CONTEXT, child);
-        CloudMachineNamer namer = new CloudMachineNamer(cfg);
-        namer.lengthMaxPermittedForMachineName(10);
-        String result = namer.generateNewMachineUniqueName();
+        BasicCloudMachineNamer namer = new BasicCloudMachineNamer();
+        namer.setDefaultMachineNameMaxLength(10);
+        String result = namer.generateNewMachineUniqueName(cfg);
         Assert.assertEquals(result.length(), 10);
     }
     
     @Test
-    public void testLengthReserverdForNameInGroup() {
+    public void testLengthReservedForNameInGroup() {
         app = ApplicationBuilder.newManagedApp(EntitySpec.create(TestApplication.class).displayName("TistApp"), LocalManagementContextForTests.newInstance());
         TestEntity child = app.createAndManageChild(EntitySpec.create(TestEntity.class).displayName("TestEnt"));
         
         ConfigBag cfg = new ConfigBag()
             .configure(CloudLocationConfig.CALLER_CONTEXT, child);
-        CloudMachineNamer namer = new CloudMachineNamer(cfg);
-        namer.lengthMaxPermittedForMachineName(10);
-        namer.lengthReservedForNameInGroup(4);
-        String groupId = namer.generateNewGroupId();
-        Assert.assertEquals(5, groupId.length(), "groupId="+groupId);
+        BasicCloudMachineNamer namer = new BasicCloudMachineNamer();
+        namer.setDefaultMachineNameMaxLength(20);
+        namer.setDefaultMachineNameSeparatorAndSaltLength(":I", 5);
+        String groupId = namer.generateNewGroupId(cfg);
+        Assert.assertEquals(13, groupId.length(), "groupId="+groupId);
+        String machineId = namer.generateNewMachineUniqueNameFromGroupId(cfg, groupId);
+        Assert.assertEquals(20, machineId.length(), "machineId="+machineId);
+        // separator is not sanitized -- caller should know what they are doing there!
+        Assert.assertTrue(machineId.startsWith(groupId+"-i"), "machineId="+machineId);
     }
 
     @Test
@@ -136,9 +143,9 @@ public class CloudMachineNamerTest {
 
         ConfigBag cfg = new ConfigBag()
             .configure(CloudLocationConfig.CALLER_CONTEXT, child);
-        CloudMachineNamer namer = new CloudMachineNamer(cfg);
+        CloudMachineNamer namer = new BasicCloudMachineNamer();
         
-        String result = namer.generateNewMachineUniqueName();
+        String result = namer.generateNewMachineUniqueName(cfg);
         assertTrue(result.indexOf("t-ap") >= 0, "result="+result);
         for (int c : "_%$()\r\n\t[]*.!".getBytes()) {
             assertFalse(result.contains(new String(new char [] {(char)c})), "result="+result);
@@ -147,7 +154,7 @@ public class CloudMachineNamerTest {
     
     @Test
     public void testSanitize() {
-        Assert.assertEquals(CloudMachineNamer.sanitize(
+        Assert.assertEquals(AbstractCloudMachineNamer.sanitize(
                 "me & you like alphanumeric but not _ or !!! or dots...dots...dots %$()\r\n\t[]*etc"),
                 "me-you-like-alphanumeric-but-not-or-or-dots-dots-dots-etc");
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8008df4f/core/src/test/java/brooklyn/location/cloud/CustomMachineNamerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/location/cloud/CustomMachineNamerTest.java b/core/src/test/java/brooklyn/location/cloud/CustomMachineNamerTest.java
index 829edb4..322acd1 100644
--- a/core/src/test/java/brooklyn/location/cloud/CustomMachineNamerTest.java
+++ b/core/src/test/java/brooklyn/location/cloud/CustomMachineNamerTest.java
@@ -26,6 +26,7 @@ import org.testng.annotations.Test;
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.location.cloud.names.CustomMachineNamer;
 import brooklyn.test.entity.LocalManagementContextForTests;
 import brooklyn.test.entity.TestApplication;
 import brooklyn.test.entity.TestEntity;
@@ -55,21 +56,21 @@ public class CustomMachineNamerTest {
     @Test
     public void testMachineNameNoConfig() {
         config.configure(CloudLocationConfig.CALLER_CONTEXT, child);
-        Assert.assertEquals(new CustomMachineNamer(config).generateNewMachineUniqueName(), "TestEnt");
+        Assert.assertEquals(new CustomMachineNamer().generateNewMachineUniqueName(config), "TestEnt");
     }
     
     @Test
     public void testMachineNameWithConfig() {
         child.setSequenceValue(999);
         config.configure(CustomMachineNamer.MACHINE_NAME_TEMPLATE, "number${entity.sequenceValue}");
-        Assert.assertEquals(new CustomMachineNamer(config).generateNewMachineUniqueName(), "number999");
+        Assert.assertEquals(new CustomMachineNamer().generateNewMachineUniqueName(config), "number999");
     }
     
     @Test
     public void testMachineNameWithExtraSubstitutions() {
         config.configure(CustomMachineNamer.MACHINE_NAME_TEMPLATE, "foo-${fooName}-bar-${barName}-baz-${bazName.substitution}")
             .configure(CustomMachineNamer.EXTRA_SUBSTITUTIONS, ImmutableMap.of("fooName", "foo", "barName", "bar", "bazName", this));
-        Assert.assertEquals(new CustomMachineNamer(config).generateNewMachineUniqueName(), "foo-foo-bar-bar-baz-baz");
+        Assert.assertEquals(new CustomMachineNamer().generateNewMachineUniqueName(config), "foo-foo-bar-bar-baz-baz");
     }
     
     public String getSubstitution() {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8008df4f/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
index 45019d5..1e1dccf 100644
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
+++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
@@ -108,7 +108,7 @@ import brooklyn.location.basic.LocationConfigUtils.OsCredential;
 import brooklyn.location.basic.SshMachineLocation;
 import brooklyn.location.cloud.AbstractCloudMachineProvisioningLocation;
 import brooklyn.location.cloud.AvailabilityZoneExtension;
-import brooklyn.location.cloud.CloudMachineNamer;
+import brooklyn.location.cloud.names.CloudMachineNamer;
 import brooklyn.location.jclouds.JcloudsPredicates.NodeInLocation;
 import brooklyn.location.jclouds.networking.JcloudsPortForwarderExtension;
 import brooklyn.location.jclouds.templates.PortableTemplateBuilder;
@@ -321,14 +321,14 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
     protected CloudMachineNamer getCloudMachineNamer(ConfigBag config) {
         String namerClass = config.get(LocationConfigKeys.CLOUD_MACHINE_NAMER_CLASS);
         if (Strings.isNonBlank(namerClass)) {
-            Optional<CloudMachineNamer> cloudNamer = Reflections.invokeConstructorWithArgs(getManagementContext().getCatalogClassLoader(), namerClass, config);
+            Optional<CloudMachineNamer> cloudNamer = Reflections.invokeConstructorWithArgs(getManagementContext().getCatalogClassLoader(), namerClass);
             if (cloudNamer.isPresent()) {
                 return cloudNamer.get();
             } else {
                 throw new IllegalStateException("Failed to create CloudMachineNamer "+namerClass+" for location "+this);
             }
         } else {
-            return new JcloudsMachineNamer(config);
+            return new JcloudsMachineNamer();
         }
     }
 
@@ -565,7 +565,7 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
 
         final ComputeService computeService = getConfig(COMPUTE_SERVICE_REGISTRY).findComputeService(setup, true);
         CloudMachineNamer cloudMachineNamer = getCloudMachineNamer(setup);
-        String groupId = elvis(setup.get(GROUP_ID), cloudMachineNamer.generateNewGroupId());
+        String groupId = elvis(setup.get(GROUP_ID), cloudMachineNamer.generateNewGroupId(setup));
         NodeMetadata node = null;
         JcloudsSshMachineLocation sshMachineLocation = null;
 
@@ -609,7 +609,7 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
                             setup.getUnusedConfig());
 
                 templateTimestamp = Duration.of(provisioningStopwatch);
-                template.getOptions().getUserMetadata().put("Name", cloudMachineNamer.generateNewMachineUniqueNameFromGroupId(groupId));
+                template.getOptions().getUserMetadata().put("Name", cloudMachineNamer.generateNewMachineUniqueNameFromGroupId(setup, groupId));
 
                 nodes = computeService.createNodesInGroup(groupId, 1, template);
                 provisionTimestamp = Duration.of(provisioningStopwatch);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8008df4f/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java
index b7b61a7..3e03837 100644
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java
+++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java
@@ -250,6 +250,7 @@ public interface JcloudsLocationConfig extends CloudLocationConfig {
             "Registry/Factory for creating jclouds ComputeService; default is almost always fine, except where tests want to customize behaviour",
             ComputeServiceRegistryImpl.INSTANCE);
     
+    @SuppressWarnings("serial")
     public static final ConfigKey<Map<String,String>> TEMPLATE_OPTIONS = ConfigKeys.newConfigKey(
             new TypeToken<Map<String, String>>() {}, "templateOptions", "Additional jclouds template options");
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8008df4f/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsMachineNamer.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsMachineNamer.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsMachineNamer.java
index c3f7b28..301ed43 100644
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsMachineNamer.java
+++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsMachineNamer.java
@@ -18,18 +18,15 @@
  */
 package brooklyn.location.jclouds;
 
-import brooklyn.location.cloud.CloudMachineNamer;
+import brooklyn.location.cloud.names.BasicCloudMachineNamer;
 import brooklyn.util.config.ConfigBag;
 
-public class JcloudsMachineNamer extends CloudMachineNamer {
-
-    public JcloudsMachineNamer(ConfigBag setup) {
-        super(setup);
-    }
+public class JcloudsMachineNamer extends BasicCloudMachineNamer {
 
+    @Override
     /** returns the max length of a VM name for the cloud specified in setup;
      * this value is typically decremented by 9 to make room for jclouds labels */
-    public Integer getCustomMaxNameLength() {
+    public Integer getCustomMaxNameLength(ConfigBag setup) {
         // otherwise, for some known clouds which only allow a short name, use that length
         if ("vcloud".equals( setup.peek(JcloudsLocationConfig.CLOUD_PROVIDER) ))
             return 24;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8008df4f/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTest.java b/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTest.java
index 60aec22..b133457 100644
--- a/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTest.java
+++ b/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTest.java
@@ -47,6 +47,7 @@ import brooklyn.entity.basic.Entities;
 import brooklyn.location.LocationSpec;
 import brooklyn.location.NoMachinesAvailableException;
 import brooklyn.location.basic.LocationConfigKeys;
+import brooklyn.location.cloud.names.CustomMachineNamer;
 import brooklyn.location.geo.HostGeoInfo;
 import brooklyn.location.jclouds.JcloudsLocation.UserCreation;
 import brooklyn.management.internal.LocalManagementContext;
@@ -436,10 +437,10 @@ public class JcloudsLocationTest implements JcloudsLocationConfig {
     
     @Test
     public void testCreateWithCustomMachineNamer() {
-        final String machineNamerClass = "brooklyn.location.cloud.CustomMachineNamer";
+        final String machineNamerClass = CustomMachineNamer.class.getName();
         BailOutJcloudsLocation jcloudsLocation = newSampleBailOutJcloudsLocationForTesting(ImmutableMap.of(
                 LocationConfigKeys.CLOUD_MACHINE_NAMER_CLASS, machineNamerClass));
-        jcloudsLocation.tryObtainAndCheck(ImmutableMap.of(), new Predicate<ConfigBag>() {
+        jcloudsLocation.tryObtainAndCheck(ImmutableMap.of(CustomMachineNamer.MACHINE_NAME_TEMPLATE, "ignored"), new Predicate<ConfigBag>() {
             public boolean apply(ConfigBag input) {
                 Assert.assertEquals(input.get(LocationConfigKeys.CLOUD_MACHINE_NAMER_CLASS), machineNamerClass);
                 return true;
@@ -449,9 +450,10 @@ public class JcloudsLocationTest implements JcloudsLocationConfig {
     
     @Test
     public void testCreateWithCustomMachineNamerOnObtain() {
-        final String machineNamerClass = "brooklyn.location.cloud.CustomMachineNamer";
+        final String machineNamerClass = CustomMachineNamer.class.getName();
         BailOutJcloudsLocation jcloudsLocation = newSampleBailOutJcloudsLocationForTesting();
         jcloudsLocation.tryObtainAndCheck(ImmutableMap.of(
+                CustomMachineNamer.MACHINE_NAME_TEMPLATE, "ignored",
                 LocationConfigKeys.CLOUD_MACHINE_NAMER_CLASS, machineNamerClass), new Predicate<ConfigBag>() {
             public boolean apply(ConfigBag input) {
                 Assert.assertEquals(input.get(LocationConfigKeys.CLOUD_MACHINE_NAMER_CLASS), machineNamerClass);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8008df4f/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsMachineNamerTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsMachineNamerTest.java b/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsMachineNamerTest.java
index 2a51617..da21750 100644
--- a/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsMachineNamerTest.java
+++ b/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsMachineNamerTest.java
@@ -36,15 +36,15 @@ public class JcloudsMachineNamerTest {
             .configure(JcloudsLocationConfig.CLOUD_PROVIDER, "vcloud")
             .configure(JcloudsLocationConfig.CALLER_CONTEXT, "!mycontext!");
         
-        String result = new JcloudsMachineNamer(cfg).generateNewGroupId();
+        String result = new JcloudsMachineNamer().generateNewGroupId(cfg);
         
         log.info("test mycontext vcloud group id gives: "+result);
-        // brooklyn-user-mycontext!-1234
-        // br-user-myco-1234
-        Assert.assertTrue(result.length() <= 15);
+        // brooklyn-user-!mycontext!-1234
+        // br-<code>-<user>-myco-1234
+        Assert.assertTrue(result.length() <= 24-4-1, "result: "+result);
         
         String user = Strings.maxlen(System.getProperty("user.name"), 2).toLowerCase();
-        // (length 2 will happen if user is brooklyn)
+        // (length 2 will happen if user is brooklyn, to avoid brooklyn-brooklyn at start!)
         Assert.assertTrue(result.indexOf(user) >= 0);
         Assert.assertTrue(result.indexOf("-myc") >= 0);
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8008df4f/software/base/src/main/java/brooklyn/entity/service/InitdServiceInstaller.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/service/InitdServiceInstaller.java b/software/base/src/main/java/brooklyn/entity/service/InitdServiceInstaller.java
index ce52038..6586bb1 100644
--- a/software/base/src/main/java/brooklyn/entity/service/InitdServiceInstaller.java
+++ b/software/base/src/main/java/brooklyn/entity/service/InitdServiceInstaller.java
@@ -32,7 +32,7 @@ import brooklyn.entity.basic.SoftwareProcess;
 import brooklyn.entity.effector.EffectorTasks;
 import brooklyn.entity.trait.HasShortName;
 import brooklyn.location.basic.SshMachineLocation;
-import brooklyn.location.cloud.CloudMachineNamer;
+import brooklyn.location.cloud.names.AbstractCloudMachineNamer;
 import brooklyn.management.Task;
 import brooklyn.policy.Enricher;
 import brooklyn.util.ResourceUtils;
@@ -112,7 +112,7 @@ public class InitdServiceInstaller implements SystemServiceInstaller {
         } else {
             name = "brooklyn-service";
         }
-        return CloudMachineNamer.sanitize(name.toString()).toLowerCase();
+        return AbstractCloudMachineNamer.sanitize(name.toString()).toLowerCase();
     }
 
     private String getStartScriptName() {


[23/23] incubator-brooklyn git commit: This closes #653

Posted by he...@apache.org.
This closes #653


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

Branch: refs/heads/master
Commit: 321d8879068c1d5a159962c486c3851c2eaa64b8
Parents: 866cf20 44273ad
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri May 22 10:04:11 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 22 10:04:11 2015 +0100

----------------------------------------------------------------------
 .../entity/software/ssh/SshCommandEffector.java |  6 ++--
 .../entity/software/ssh/SshCommandSensor.java   | 37 ++++++++++++++++----
 2 files changed, 35 insertions(+), 8 deletions(-)
----------------------------------------------------------------------



[22/23] incubator-brooklyn git commit: This closes #651

Posted by he...@apache.org.
This closes #651


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

Branch: refs/heads/master
Commit: 866cf2014be01664069c727c6177d58ef44798d8
Parents: f4de73e 1749737
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri May 22 10:04:10 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 22 10:04:10 2015 +0100

----------------------------------------------------------------------
 .../basic/EmptySoftwareProcessSshDriver.java    | 34 ++++++++++----------
 1 file changed, 17 insertions(+), 17 deletions(-)
----------------------------------------------------------------------



[15/23] incubator-brooklyn git commit: clean up jclouds location docs, and add bits for hostname and for tags

Posted by he...@apache.org.
clean up jclouds location docs, and add bits for hostname and for tags


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

Branch: refs/heads/master
Commit: 38c92c8c9952b50a9c6363d2fd3769b5886d6e88
Parents: 41c2d9b
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri May 8 16:18:15 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri May 8 18:51:50 2015 +0100

----------------------------------------------------------------------
 docs/guide/ops/locations/index.md | 115 +++++++++++++++++++--------------
 1 file changed, 68 insertions(+), 47 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/38c92c8c/docs/guide/ops/locations/index.md
----------------------------------------------------------------------
diff --git a/docs/guide/ops/locations/index.md b/docs/guide/ops/locations/index.md
index 803c000..3c1f6b8 100644
--- a/docs/guide/ops/locations/index.md
+++ b/docs/guide/ops/locations/index.md
@@ -108,6 +108,55 @@ cloud provisioning.
 For more keys and more detail on the keys below, see 
 {% include java_link.html class_name="JcloudsLocationConfig" package_path="brooklyn/location/jclouds" project_subpath="locations/jclouds" %}.
 
+###### VM Creation
+    
+- Most providers require exactly one of either `region` (e.g. `us-east-1`) or `endpoint` (the URL, usually for private cloud deployments)
+
+- Hardware requirements can be specified, including 
+  `minRam`, `minCores`, and `os64Bit`; or as a specific `hardwareId`
+
+- VM image constraints can be set using `osFamily` (e.g. `Ubuntu`, `CentOS`, `Debian`, `RHEL`)
+  and `osVersionRegex`, or specific VM images can be specified using `imageId` or `imageNameRegex`
+
+- Specific VM images can be specified using `imageId` or `imageNameRegex`
+
+- Specific Security Groups can be specified using `securityGroups`, as a list of strings (the existing security group names),
+  or `inboundPorts` can be set, as a list of numeric ports (selected clouds only)
+
+- A specific existing key pair known at the cloud to use can be specified with `keyPair`
+  (selected clouds only)
+
+- A specific VM name (often the hostname) base to be used can be specified by setting `groupId`.
+  By default, this name is constructed based on the entity which is creating it,
+  including the ID of the app and of the entity.
+  (As many cloud portals let you filter views, this can help find a specific entity or all machines for a given application.)
+  For more sophisticated control over host naming, you can supply a custom 
+  {% include java_link.html class_name="CloudMachineNamer" package_path="brooklyn/location/cloud/names" project_subpath="core" %},
+  for example
+  `cloudMachineNamer: brooklyn.location.cloud.names.CustomMachineNamer`.
+  {% include java_link.html class_name="CustomMachineNamer" package_path="brooklyn/location/cloud/names" project_subpath="core" %}
+  will use the entity's name or following a template you supply.
+  For all names, a random suffix will be appended to help guarantee uniqueness;
+  this can be removed by setting `vmNameSaltLength: 0`.
+  
+- User metadata can be attached using the syntax `userMetadata: { key: value, key2: "value 2" }` 
+  (or `userMetadata=key=value,key2="value 2"` in a properties file)
+
+- By default, several pieces of user metadata are set to correlate VMs with Brooklyn entities,
+  prefixed with `brooklyn-`.
+  This user metadata can be omitted by setting `includeBrooklynUserMetadata: false`.
+
+- You can specify the number of attempts Brooklyn should make to create
+  machines with `machineCreateAttempts` (jclouds only). This is useful as an efficient low-level fix
+  for those occasions when cloud providers give machines that are dead on arrival.
+  You can of course also resolve it at a higher level with a policy such as 
+  {% include java_link.html class_name="ServiceRestarter" package_path="brooklyn/policy/ha" project_subpath="policies" %}.
+
+- If you want to investigate failures, set `destroyOnFailure: false`
+  to keep failed VM's around. (You'll have to manually clean them up.)
+  The default is false: if a VM fails to start, or is never ssh'able, then the VM will be terminated.
+
+
 ###### OS Setup
 
 - `user` and `password` can be used to configure the operating user created on cloud-provisioned machines
@@ -115,20 +164,20 @@ For more keys and more detail on the keys below, see
 - The `loginUser` config key (and subkeys) control the initial user to log in as,
   in cases where this cannot be discovered from the cloud provider
  
-- Private keys can be specified using ``privateKeyFile``; 
+- Private keys can be specified using `privateKeyFile`; 
   these are not copied to provisioned machines, but are required if using a local public key
   or a pre-defined `authorized_keys` on the server.
   (For more information on SSH keys, see [here](ssh-keys.html).) 
 
 - If there is a passphrase on the key file being used, you must supply it to Brooklyn for it to work, of course!
-  ``privateKeyPassphrase`` does the trick (as in ``brooklyn.location.jclouds.privateKeyPassphrase``, or other places
-  where ``privateKeyFile`` is valid).  If you don't like keys, you can just use a plain old ``password``.
+  `privateKeyPassphrase` does the trick (as in `brooklyn.location.jclouds.privateKeyPassphrase`, or other places
+  where `privateKeyFile` is valid).  If you don't like keys, you can just use a plain old `password`.
 
-- Public keys can be specified using ``publicKeyFile``, 
+- Public keys can be specified using `publicKeyFile`, 
   although these can usually be omitted if they follow the common pattern of being
-  the private key file with the suffix ``.pub`` appended.
-  (It is useful in the case of ``loginUser.publicKeyFile``, where you shouldn't need,
-  or might not even have, the private key of the ``root`` user in order to log in.)
+  the private key file with the suffix `.pub` appended.
+  (It is useful in the case of `loginUser.publicKeyFile`, where you shouldn't need,
+  or might not even have, the private key of the `root` user when you log in.)
 
 - Use `dontCreateUser` to have Brooklyn run as the initial `loginUser` (usually `root`),
   without creating any other user.
@@ -137,61 +186,33 @@ For more keys and more detail on the keys below, see
   before making the `Location` available to entities,
   optionally also using `setup.script.vars` (set as `key1:value1,key2:value2`)
 
-- Use `openIptables=true` to automatically configure `iptables`, to open the TCP ports required by
-  the software process. One can alternatively use `stopIptables=true` to entirely stop the
+- Use `openIptables: true` to automatically configure `iptables`, to open the TCP ports required by
+  the software process. One can alternatively use `stopIptables: true` to entirely stop the
   iptables service.
 
-- Use `installDevUrandom=true` to fall back to using `/dev/urandom` rather than `/dev/random`. This setting
+- Use `installDevUrandom: true` to fall back to using `/dev/urandom` rather than `/dev/random`. This setting
   is useful for cloud VMs where there is not enough random entropy, which can cause `/dev/random` to be
   extremely slow (causing `ssh` to be extremely slow to respond).
 
-- Use `useJcloudsSshInit=false` to disable the use of the native jclouds support for initial commands executed 
+- Use `useJcloudsSshInit: false` to disable the use of the native jclouds support for initial commands executed 
   on the VM (e.g. for creating new users, setting root passwords, etc.). Instead, Brooklyn's ssh support will
   be used. Timeouts and retries are more configurable within Brooklyn itself. Therefore this option is particularly 
   recommended when the VM startup is unusual (for example, if guest customizations will cause reboots and/or will 
   change login credentials).
 
-- Use `brooklyn.ssh.config.noDeleteAfterExec=true` can be used during dev/test. This prevents the scripts executed 
-  on the VMs from being deleted on completion. This can help with debugging some issues. However, the contents of the 
-  scripts and the stdout/stderr of their execution is also available in the AMP debug log.
-
-
-###### VM Creation
-    
-- Most providers require exactly one of either `region` (e.g. `us-east-1`) or `endpoint` (the URL, usually for private cloud deployments)
-
-- Hardware requirements can be specified, including 
-  ``minRam``, ``minCores``, and `os64Bit`; or as a specific ``hardwareId``
-
-- VM image constraints can be set using `osFamily` (e.g. `Ubuntu`, `CentOS`, `Debian`, `RHEL`)
-  and `osVersionRegex`, or specific VM images can be specified using ``imageId`` or ``imageNameRegex``
-
-- Specific VM images can be specified using ``imageId`` or ``imageNameRegex``
-
-- Specific Security Groups can be specified using `securityGroups`, as a list of strings (the existing security group names),
-  or `inboundPorts` can be set, as a list of numeric ports (selected clouds only)
-
-- A specific existing key pair for the cloud to set for `loginUser` can be specified using `keyPair`
-  (selected clouds only)
-
-- User metadata can be attached, using the syntax ``userMetadata=key=value,key2="value 2"``
-
-- You can specify the number of attempts Brooklyn should make to create
-  machines with ``machineCreateAttempts`` (jclouds only). This is extremely useful for
-  working around the rare occasions in which cloud providers give machines that
-  are dead on arrival.
-
-- If you want to investigate failures, set `destroyOnFailure=false`
-  to keep failed VM's around. (You'll have to manually clean them up.)
-  The default is false: if a VM fails to start, or is never ssh'able, then the VM will be terminated.
+- Use `brooklyn.ssh.config.noDeleteAfterExec: true` to keep scripts on the server after execution.
+  The contents of the scripts and the stdout/stderr of their execution are available in the Brooklyn web console,
+  but sometimes it can also be useful to have them on the box.
+  This setting prevents scripts executed on the VMs from being deleted on completion.
+  Note that some scripts run periodically so this can eventually fill a disk; it should only be used for dev/test. 
 
 
 ### Inheritance and Named Locations
 
 Named locations can be defined for commonly used groups of properties, 
-with the syntax ``brooklyn.location.named.your-group-name.``
+with the syntax `brooklyn.location.named.your-group-name.`
 followed by the relevant properties.
-These can be accessed at runtime using the syntax ``named:your-group-name`` as the deployment location.
+These can be accessed at runtime using the syntax `named:your-group-name` as the deployment location.
 
 Some illustrative examples using named locations and
 showing the syntax and properties above are as follows:
@@ -285,7 +306,7 @@ As before, if the brooklyn user and its default key are authorized for the hosts
 those fields can be omitted.
 
 Named locations can also be configured in your `brooklyn.properties`,
-using the format ``byon:(key=value,key2=value2)``.
+using the format `byon:(key=value,key2=value2)`.
 For convenience, for hosts wildcard globs are supported.
 
 {% highlight bash %}