You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ge...@apache.org on 2017/03/27 10:35:24 UTC

[05/12] brooklyn-server git commit: Split out internal classes into their own class (for CatalogBundleTracker and CatalogBundleLoader)

Split out internal classes into their own class (for CatalogBundleTracker and CatalogBundleLoader)


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

Branch: refs/heads/master
Commit: e8cc8d224b1285ca6bbdb6ba8784e39a3b344c79
Parents: e2fa077
Author: Thomas Bouron <th...@cloudsoftcorp.com>
Authored: Fri Mar 24 11:34:04 2017 +0000
Committer: Thomas Bouron <th...@cloudsoftcorp.com>
Committed: Fri Mar 24 11:34:04 2017 +0000

----------------------------------------------------------------------
 .../catalog/internal/CatalogBomScanner.java     | 261 +------------------
 .../catalog/internal/CatalogBundleLoader.java   | 191 ++++++++++++++
 .../catalog/internal/CatalogBundleTracker.java  | 104 ++++++++
 .../rest/resources/CatalogResource.java         |   4 +-
 4 files changed, 303 insertions(+), 257 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e8cc8d22/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBomScanner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBomScanner.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBomScanner.java
index 1348886..78e7d54 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBomScanner.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBomScanner.java
@@ -18,37 +18,18 @@
  */
 package org.apache.brooklyn.core.catalog.internal;
 
-import static org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType.TEMPLATE;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
 import java.util.List;
-import java.util.Map;
 
-import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.core.BrooklynFeatureEnablement;
-import org.apache.brooklyn.util.collections.MutableList;
-import org.apache.brooklyn.util.exceptions.Exceptions;
-import org.apache.brooklyn.util.stream.Streams;
 import org.apache.brooklyn.util.text.Strings;
-import org.apache.brooklyn.util.yaml.Yamls;
 import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleEvent;
 import org.osgi.framework.ServiceReference;
-import org.osgi.util.tracker.BundleTracker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.yaml.snakeyaml.DumperOptions;
-import org.yaml.snakeyaml.Yaml;
 
 import com.google.common.annotations.Beta;
-import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
 
 /** Scans bundles being added, filtered by a whitelist and blacklist, and adds catalog.bom files to the catalog.
  * See karaf blueprint.xml for configuration, and tests in dist project. */
@@ -58,30 +39,27 @@ public class CatalogBomScanner {
     private final String ACCEPT_ALL_BY_DEFAULT = ".*";
 
     private static final Logger LOG = LoggerFactory.getLogger(CatalogBomScanner.class);
-    private static final String CATALOG_BOM_URL = "catalog.bom";
-    private static final String BROOKLYN_CATALOG = "brooklyn.catalog";
-    private static final String BROOKLYN_LIBRARIES = "brooklyn.libraries";
 
     private List<String> whiteList = ImmutableList.of(ACCEPT_ALL_BY_DEFAULT);
     private List<String> blackList = ImmutableList.of();
 
-    private CatalogPopulator catalogTracker;
+    private CatalogBundleTracker catalogBundleTracker;
 
     public void bind(ServiceReference<ManagementContext> managementContext) throws Exception {
         if (isEnabled()) {
             LOG.debug("Binding management context with whiteList [{}] and blacklist [{}]",
                 Strings.join(getWhiteList(), "; "),
                 Strings.join(getBlackList(), "; "));
-            catalogTracker = new CatalogPopulator(managementContext);
+            catalogBundleTracker = new CatalogBundleTracker(this, managementContext);
         }
     }
 
     public void unbind(ServiceReference<ManagementContext> managementContext) throws Exception {
         if (isEnabled()) {
             LOG.debug("Unbinding management context");
-            if (null != catalogTracker) {
-                CatalogPopulator temp = catalogTracker;
-                catalogTracker = null;
+            if (null != catalogBundleTracker) {
+                CatalogBundleTracker temp = catalogBundleTracker;
+                catalogBundleTracker = null;
                 temp.close();
             }
         }
@@ -91,7 +69,7 @@ public class CatalogBomScanner {
         return BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_LOAD_BUNDLE_CATALOG_BOM);
     }
 
-    private String[] bundleIds(Bundle bundle) {
+    public String[] bundleIds(Bundle bundle) {
         return new String[] {
             String.valueOf(bundle.getBundleId()), bundle.getSymbolicName(), bundle.getVersion().toString()
         };
@@ -123,231 +101,4 @@ public class CatalogBomScanner {
         this.blackList = Strings.parseCsv(blackListText);
     }
 
-    public class CatalogPopulator extends BundleTracker<Iterable<? extends CatalogItem<?, ?>>> {
-
-        private ServiceReference<ManagementContext> mgmtContextReference;
-        private BundleContext bundleContext;
-        private ManagementContext managementContext;
-        private CatalogBundleLoader catalogBundleLoader;
-
-        public CatalogPopulator(ServiceReference<ManagementContext> serviceReference) {
-            super(serviceReference.getBundle().getBundleContext(), Bundle.ACTIVE, null);
-            this.mgmtContextReference = serviceReference;
-            open();
-        }
-        
-        public CatalogPopulator(BundleContext bundleContext, ManagementContext managementContext) {
-            super(Preconditions.checkNotNull(bundleContext, "bundleContext required; is OSGi running?"), Bundle.ACTIVE, null);
-            this.bundleContext = bundleContext;
-            this.managementContext = managementContext;
-            open();
-        }
-
-        @Override
-        public void open() {
-            if (mgmtContextReference != null) {
-                bundleContext = getBundleContext();
-                managementContext = getManagementContext();
-                catalogBundleLoader = new CatalogBundleLoader(managementContext);
-            }
-            super.open();
-        }
-
-        @Override
-        public void close() {
-            super.close();
-            if (mgmtContextReference != null) {
-                managementContext = null;
-                getBundleContext().ungetService(mgmtContextReference);
-                bundleContext = null;
-                catalogBundleLoader = null;
-            }
-        }
-
-        public BundleContext getBundleContext() {
-            if (bundleContext != null) return bundleContext;
-            if (mgmtContextReference != null) return mgmtContextReference.getBundle().getBundleContext();
-            throw new IllegalStateException("Bundle context or management context reference must be supplied");
-        }
-        
-        public ManagementContext getManagementContext() {
-            if (managementContext != null) return managementContext;
-            if (mgmtContextReference != null) return getBundleContext().getService(mgmtContextReference);
-            throw new IllegalStateException("Bundle context or management context reference must be supplied");
-        }
-
-        /**
-         * Scans the bundle being added for a catalog.bom file and adds any entries in it to the catalog.
-         *
-         * @param bundle The bundle being added to the bundle context.
-         * @param bundleEvent The event of the addition.
-         *
-         * @return The items added to the catalog; these will be tracked by the {@link BundleTracker} mechanism
-         *         and supplied to the {@link #removedBundle(Bundle, BundleEvent, Iterable)} method.
-         *
-         * @throws RuntimeException if the catalog items failed to be added to the catalog
-         */
-        @Override
-        public Iterable<? extends CatalogItem<?, ?>> addingBundle(Bundle bundle, BundleEvent bundleEvent) {
-            return catalogBundleLoader.scanForCatalog(bundle);
-        }
-
-        /**
-         * Remove the given entries from the catalog, related to the given bundle.
-         *
-         * @param bundle The bundle being removed to the bundle context.
-         * @param bundleEvent The event of the removal.
-         * @param items The items being removed
-         *
-         * @throws RuntimeException if the catalog items failed to be added to the catalog
-         */
-        @Override
-        public void removedBundle(Bundle bundle, BundleEvent bundleEvent, Iterable<? extends CatalogItem<?, ?>> items) {
-            if (!items.iterator().hasNext()) {
-                return;
-            }
-            LOG.debug("Unloading catalog BOM entries from {} {} {}", bundleIds(bundle));
-            for (CatalogItem<?, ?> item : items) {
-                LOG.debug("Unloading {} {} from catalog", item.getSymbolicName(), item.getVersion());
-
-                catalogBundleLoader.removeFromCatalog(item);
-            }
-        }
-    }
-
-    @Beta
-    public class CatalogBundleLoader {
-
-        private ManagementContext managementContext;
-
-        public CatalogBundleLoader(ManagementContext managementContext) {
-            this.managementContext = managementContext;
-        }
-
-        /**
-         * Scan the given bundle for a catalog.bom and adds it to the catalog.
-         *
-         * @param bundle The bundle to add
-         * @return A list of items added to the catalog
-         *
-         * @throws RuntimeException if the catalog items failed to be added to the catalog
-         */
-        public Iterable<? extends CatalogItem<?, ?>> scanForCatalog(Bundle bundle) {
-
-            Iterable<? extends CatalogItem<?, ?>> catalogItems = MutableList.of();
-
-            final URL bom = bundle.getResource(CATALOG_BOM_URL);
-            if (null != bom) {
-                LOG.debug("Found catalog BOM in {} {} {}", bundleIds(bundle));
-                String bomText = readBom(bom);
-                String bomWithLibraryPath = addLibraryDetails(bundle, bomText);
-                catalogItems = this.managementContext.getCatalog().addItems(bomWithLibraryPath);
-                for (CatalogItem<?, ?> item : catalogItems) {
-                    LOG.debug("Added to catalog: {}, {}", item.getSymbolicName(), item.getVersion());
-                }
-            } else {
-                LOG.debug("No BOM found in {} {} {}", bundleIds(bundle));
-            }
-
-            if (!passesWhiteAndBlacklists(bundle)) {
-                catalogItems = removeAnyApplications(catalogItems);
-            }
-
-            return catalogItems;
-        }
-
-        private String readBom(URL bom) {
-            try (final InputStream ins = bom.openStream()) {
-                return Streams.readFullyString(ins);
-            } catch (IOException e) {
-                throw Exceptions.propagate("Error loading Catalog BOM from " + bom, e);
-            }
-        }
-
-        private String addLibraryDetails(Bundle bundle, String bomText) {
-            @SuppressWarnings("unchecked")
-            final Map<String, Object> bom = (Map<String, Object>)Iterables.getOnlyElement(Yamls.parseAll(bomText));
-            final Object catalog = bom.get(BROOKLYN_CATALOG);
-            if (null != catalog) {
-                if (catalog instanceof Map<?, ?>) {
-                    @SuppressWarnings("unchecked")
-                    Map<String, Object> catalogMap = (Map<String, Object>) catalog;
-                    addLibraryDetails(bundle, catalogMap);
-                } else {
-                    LOG.warn("Unexpected syntax for {} (expected Map, but got {}), ignoring", BROOKLYN_CATALOG, catalog.getClass().getName());
-                }
-            }
-            final String updatedBom = backToYaml(bom);
-            LOG.trace("Updated catalog bom:\n{}", updatedBom);
-            return updatedBom;
-        }
-
-        private void addLibraryDetails(Bundle bundle, Map<String, Object> catalog) {
-            if (!catalog.containsKey(BROOKLYN_LIBRARIES)) {
-                catalog.put(BROOKLYN_LIBRARIES, MutableList.of());
-            }
-            final Object librarySpec = catalog.get(BROOKLYN_LIBRARIES);
-            if (!(librarySpec instanceof List)) {
-                throw new RuntimeException("expected " + BROOKLYN_LIBRARIES + " to be a list, but got "
-                        + (librarySpec == null ? "null" : librarySpec.getClass().getName()));
-            }
-            @SuppressWarnings("unchecked")
-            List<Map<String,String>> libraries = (List<Map<String,String>>)librarySpec;
-            if (bundle.getSymbolicName() == null || bundle.getVersion() == null) {
-                throw new IllegalStateException("Cannot scan "+bundle+" for catalog files: name or version is null");
-            }
-            libraries.add(ImmutableMap.of(
-                    "name", bundle.getSymbolicName(),
-                    "version", bundle.getVersion().toString()));
-            LOG.debug("library spec is {}", librarySpec);
-        }
-
-        private String backToYaml(Map<String, Object> bom) {
-            final DumperOptions options = new DumperOptions();
-            options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
-            options.setPrettyFlow(true);
-            return new Yaml(options).dump(bom);
-        }
-
-        private Iterable<? extends CatalogItem<?, ?>> removeAnyApplications(
-                Iterable<? extends CatalogItem<?, ?>> catalogItems) {
-
-            List<CatalogItem<?, ?>> result = MutableList.of();
-
-            for (CatalogItem<?, ?> item: catalogItems) {
-                if (TEMPLATE.equals(item.getCatalogItemType())) {
-                    removeFromCatalog(item);
-                } else {
-                    result.add(item);
-                }
-            }
-            return result;
-        }
-
-        private boolean passesWhiteAndBlacklists(Bundle bundle) {
-            return on(bundle, getWhiteList()) && !on(bundle, getBlackList());
-        }
-
-        private boolean on(Bundle bundle, List<String> list) {
-            for (String candidate : list) {
-                final String symbolicName = bundle.getSymbolicName();
-                if (symbolicName.matches(candidate.trim())) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        private void removeFromCatalog(CatalogItem<?, ?> item) {
-            try {
-                this.managementContext.getCatalog().deleteCatalogItem(item.getSymbolicName(), item.getVersion());
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                LOG.warn(Strings.join(new String[] {
-                        "Failed to remove", item.getSymbolicName(), item.getVersion(), "from catalog"
-                }, " "), e);
-            }
-        }
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e8cc8d22/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleLoader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleLoader.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleLoader.java
new file mode 100644
index 0000000..20fe5e2
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleLoader.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.brooklyn.core.catalog.internal;
+
+import static org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType.TEMPLATE;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.yaml.Yamls;
+import org.osgi.framework.Bundle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+@Beta
+public class CatalogBundleLoader {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CatalogBundleLoader.class);
+    private static final String CATALOG_BOM_URL = "catalog.bom";
+    private static final String BROOKLYN_CATALOG = "brooklyn.catalog";
+    private static final String BROOKLYN_LIBRARIES = "brooklyn.libraries";
+
+    private CatalogBomScanner catalogBomScanner;
+    private ManagementContext managementContext;
+
+    public CatalogBundleLoader(CatalogBomScanner catalogBomScanner, ManagementContext managementContext) {
+        this.catalogBomScanner = catalogBomScanner;
+        this.managementContext = managementContext;
+    }
+
+    /**
+     * Scan the given bundle for a catalog.bom and adds it to the catalog.
+     *
+     * @param bundle The bundle to add
+     * @return A list of items added to the catalog
+     * @throws RuntimeException if the catalog items failed to be added to the catalog
+     */
+    public Iterable<? extends CatalogItem<?, ?>> scanForCatalog(Bundle bundle) {
+
+        Iterable<? extends CatalogItem<?, ?>> catalogItems = MutableList.of();
+
+        final URL bom = bundle.getResource(CatalogBundleLoader.CATALOG_BOM_URL);
+        if (null != bom) {
+            LOG.debug("Found catalog BOM in {} {} {}", catalogBomScanner.bundleIds(bundle));
+            String bomText = readBom(bom);
+            String bomWithLibraryPath = addLibraryDetails(bundle, bomText);
+            catalogItems = this.managementContext.getCatalog().addItems(bomWithLibraryPath);
+            for (CatalogItem<?, ?> item : catalogItems) {
+                LOG.debug("Added to catalog: {}, {}", item.getSymbolicName(), item.getVersion());
+            }
+        } else {
+            LOG.debug("No BOM found in {} {} {}", catalogBomScanner.bundleIds(bundle));
+        }
+
+        if (!passesWhiteAndBlacklists(bundle)) {
+            catalogItems = removeAnyApplications(catalogItems);
+        }
+
+        return catalogItems;
+    }
+
+    /**
+     * Remove the given items from the catalog.
+     *
+     * @param item Catalog items to remove
+     */
+    public void removeFromCatalog(CatalogItem<?, ?> item) {
+        try {
+            this.managementContext.getCatalog().deleteCatalogItem(item.getSymbolicName(), item.getVersion());
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            LOG.warn(Strings.join(new String[]{
+                    "Failed to remove", item.getSymbolicName(), item.getVersion(), "from catalog"
+            }, " "), e);
+        }
+    }
+
+    private String readBom(URL bom) {
+        try (final InputStream ins = bom.openStream()) {
+            return Streams.readFullyString(ins);
+        } catch (IOException e) {
+            throw Exceptions.propagate("Error loading Catalog BOM from " + bom, e);
+        }
+    }
+
+    private String addLibraryDetails(Bundle bundle, String bomText) {
+        @SuppressWarnings("unchecked")
+        final Map<String, Object> bom = (Map<String, Object>) Iterables.getOnlyElement(Yamls.parseAll(bomText));
+        final Object catalog = bom.get(CatalogBundleLoader.BROOKLYN_CATALOG);
+        if (null != catalog) {
+            if (catalog instanceof Map<?, ?>) {
+                @SuppressWarnings("unchecked")
+                Map<String, Object> catalogMap = (Map<String, Object>) catalog;
+                addLibraryDetails(bundle, catalogMap);
+            } else {
+                LOG.warn("Unexpected syntax for {} (expected Map, but got {}), ignoring", CatalogBundleLoader.BROOKLYN_CATALOG, catalog.getClass().getName());
+            }
+        }
+        final String updatedBom = backToYaml(bom);
+        LOG.trace("Updated catalog bom:\n{}", updatedBom);
+        return updatedBom;
+    }
+
+    private void addLibraryDetails(Bundle bundle, Map<String, Object> catalog) {
+        if (!catalog.containsKey(CatalogBundleLoader.BROOKLYN_LIBRARIES)) {
+            catalog.put(CatalogBundleLoader.BROOKLYN_LIBRARIES, MutableList.of());
+        }
+        final Object librarySpec = catalog.get(CatalogBundleLoader.BROOKLYN_LIBRARIES);
+        if (!(librarySpec instanceof List)) {
+            throw new RuntimeException("expected " + CatalogBundleLoader.BROOKLYN_LIBRARIES + " to be a list, but got "
+                    + (librarySpec == null ? "null" : librarySpec.getClass().getName()));
+        }
+        @SuppressWarnings("unchecked")
+        List<Map<String, String>> libraries = (List<Map<String, String>>) librarySpec;
+        if (bundle.getSymbolicName() == null || bundle.getVersion() == null) {
+            throw new IllegalStateException("Cannot scan " + bundle + " for catalog files: name or version is null");
+        }
+        libraries.add(ImmutableMap.of(
+                "name", bundle.getSymbolicName(),
+                "version", bundle.getVersion().toString()));
+        LOG.debug("library spec is {}", librarySpec);
+    }
+
+    private String backToYaml(Map<String, Object> bom) {
+        final DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        options.setPrettyFlow(true);
+        return new Yaml(options).dump(bom);
+    }
+
+    private Iterable<? extends CatalogItem<?, ?>> removeAnyApplications(
+            Iterable<? extends CatalogItem<?, ?>> catalogItems) {
+
+        List<CatalogItem<?, ?>> result = MutableList.of();
+
+        for (CatalogItem<?, ?> item : catalogItems) {
+            if (TEMPLATE.equals(item.getCatalogItemType())) {
+                removeFromCatalog(item);
+            } else {
+                result.add(item);
+            }
+        }
+        return result;
+    }
+
+    private boolean passesWhiteAndBlacklists(Bundle bundle) {
+        return on(bundle, catalogBomScanner.getWhiteList()) && !on(bundle, catalogBomScanner.getBlackList());
+    }
+
+    private boolean on(Bundle bundle, List<String> list) {
+        for (String candidate : list) {
+            final String symbolicName = bundle.getSymbolicName();
+            if (symbolicName.matches(candidate.trim())) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e8cc8d22/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleTracker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleTracker.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleTracker.java
new file mode 100644
index 0000000..4cb2470
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogBundleTracker.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.brooklyn.core.catalog.internal;
+
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.BundleTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public class CatalogBundleTracker extends BundleTracker<Iterable<? extends CatalogItem<?, ?>>> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CatalogBundleTracker.class);
+
+    private ServiceReference<ManagementContext> mgmtContextReference;
+    private ManagementContext managementContext;
+
+    private CatalogBomScanner catalogBomScanner;
+    private CatalogBundleLoader catalogBundleLoader;
+
+    public CatalogBundleTracker(CatalogBomScanner catalogBomScanner, ServiceReference<ManagementContext> serviceReference) {
+        super(serviceReference.getBundle().getBundleContext(), Bundle.ACTIVE, null);
+        this.mgmtContextReference = serviceReference;
+        this.catalogBomScanner = catalogBomScanner;
+        open();
+    }
+
+    @Override
+    public void open() {
+        managementContext = mgmtContextReference.getBundle().getBundleContext().getService(mgmtContextReference);
+        catalogBundleLoader = new CatalogBundleLoader(catalogBomScanner, managementContext);
+        super.open();
+    }
+
+    @Override
+    public void close() {
+        super.close();
+        managementContext = null;
+        mgmtContextReference.getBundle().getBundleContext().ungetService(mgmtContextReference);
+        catalogBundleLoader = null;
+    }
+
+    public ManagementContext getManagementContext() {
+        return managementContext;
+    }
+
+    /**
+     * Scans the bundle being added for a catalog.bom file and adds any entries in it to the catalog.
+     *
+     * @param bundle      The bundle being added to the bundle context.
+     * @param bundleEvent The event of the addition.
+     * @return The items added to the catalog; these will be tracked by the {@link BundleTracker} mechanism
+     * and supplied to the {@link #removedBundle(Bundle, BundleEvent, Iterable)} method.
+     * @throws RuntimeException if the catalog items failed to be added to the catalog
+     */
+    @Override
+    public Iterable<? extends CatalogItem<?, ?>> addingBundle(Bundle bundle, BundleEvent bundleEvent) {
+        return catalogBundleLoader.scanForCatalog(bundle);
+    }
+
+    /**
+     * Remove the given entries from the catalog, related to the given bundle.
+     *
+     * @param bundle      The bundle being removed to the bundle context.
+     * @param bundleEvent The event of the removal.
+     * @param items       The items being removed
+     * @throws RuntimeException if the catalog items failed to be added to the catalog
+     */
+    @Override
+    public void removedBundle(Bundle bundle, BundleEvent bundleEvent, Iterable<? extends CatalogItem<?, ?>> items) {
+        if (!items.iterator().hasNext()) {
+            return;
+        }
+        LOG.debug("Unloading catalog BOM entries from {} {} {}", catalogBomScanner.bundleIds(bundle));
+        for (CatalogItem<?, ?> item : items) {
+            LOG.debug("Unloading {} {} from catalog", item.getSymbolicName(), item.getVersion());
+
+            catalogBundleLoader.removeFromCatalog(item);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e8cc8d22/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
index 8cf4d01..a5cdfe4 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
@@ -51,12 +51,12 @@ import org.apache.brooklyn.core.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.catalog.CatalogPredicates;
 import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
 import org.apache.brooklyn.core.catalog.internal.CatalogBomScanner;
+import org.apache.brooklyn.core.catalog.internal.CatalogBundleLoader;
 import org.apache.brooklyn.core.catalog.internal.CatalogDto;
 import org.apache.brooklyn.core.catalog.internal.CatalogItemComparator;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
 import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.StringAndArgument;
-import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
 import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts;
 import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
 import org.apache.brooklyn.core.typereg.RegisteredTypes;
@@ -247,7 +247,7 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
             if (!BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_LOAD_BUNDLE_CATALOG_BOM)) {
                 // if the above feature is not enabled, let's do it manually (as a contract of this method)
                 try {
-                    new CatalogBomScanner().new CatalogBundleLoader(mgmt()).scanForCatalog(bundle);
+                    new CatalogBundleLoader(new CatalogBomScanner(), mgmt()).scanForCatalog(bundle);
                 } catch (RuntimeException ex) {
                     try {
                         bundle.uninstall();