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:52 UTC

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

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;
     }