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

[01/12] brooklyn-server git commit: Add unit tests for the new upload archive feature

Repository: brooklyn-server
Updated Branches:
  refs/heads/master b7fef0f70 -> 40d8cfa23


Add unit tests for the new upload archive feature


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

Branch: refs/heads/master
Commit: e2fa07759cb11c5c6fc268fe7b15a7aafebea859
Parents: 5977702
Author: Thomas Bouron <th...@cloudsoftcorp.com>
Authored: Thu Mar 23 16:52:38 2017 +0000
Committer: Thomas Bouron <th...@cloudsoftcorp.com>
Committed: Thu Mar 23 17:21:02 2017 +0000

----------------------------------------------------------------------
 .../rest/resources/CatalogResourceTest.java     | 291 ++++++++++++++++++-
 .../brooklyn/util/osgi/OsgiTestResources.java   |   3 +
 2 files changed, 291 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e2fa0775/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
index 9e70156..b2b098e 100644
--- a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/CatalogResourceTest.java
@@ -27,6 +27,8 @@ import java.awt.Toolkit;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
@@ -34,15 +36,23 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
 import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 import javax.ws.rs.core.GenericType;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.objs.BrooklynObject;
+import org.apache.brooklyn.api.objs.Configurable;
+import org.apache.brooklyn.api.objs.Identifiable;
 import org.apache.brooklyn.api.typereg.OsgiBundleWithUrl;
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.entity.EntityPredicates;
 import org.apache.brooklyn.core.mgmt.osgi.OsgiStandaloneTest;
 import org.apache.brooklyn.core.test.entity.TestEntity;
 import org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy;
@@ -57,9 +67,12 @@ import org.apache.brooklyn.util.core.ResourceUtils;
 import org.apache.brooklyn.util.core.osgi.BundleMaker;
 import org.apache.brooklyn.util.javalang.Reflections;
 import org.apache.brooklyn.util.os.Os;
+import org.apache.brooklyn.util.osgi.OsgiTestResources;
 import org.apache.brooklyn.util.stream.Streams;
+import org.apache.http.HttpEntity;
 import org.apache.http.HttpHeaders;
 import org.apache.http.entity.ContentType;
+import org.apache.http.util.EntityUtils;
 import org.eclipse.jetty.http.HttpStatus;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -68,6 +81,8 @@ import org.testng.annotations.Test;
 import org.testng.reporters.Files;
 
 import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 
 @Test( // by using a different suite name we disallow interleaving other tests between the methods of this test class, which wrecks the test fixtures
@@ -495,7 +510,152 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
         //equivalent to HTTP response 404 text/html
         addAddCatalogItemWithInvalidBundleUrl("classpath://missing-jar-file.txt");
     }
-    
+
+    @Test
+    public void testInvalidArchive() throws Exception {
+        File f = Os.newTempFile("osgi", "zip");
+
+        Response response = client().path("/catalog")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        assertTrue(response.readEntity(String.class).contains("Invalid ZIP/JAR archive"));
+    }
+
+    @Test
+    public void testArchiveWithoutBom() throws Exception {
+        File f = createZip(ImmutableMap.<String, String>of());
+
+        Response response = client().path("/catalog")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        assertTrue(response.readEntity(String.class).contains("Archive must contain a catalog.bom file in the root"));
+    }
+
+    @Test
+    public void testArchiveWithoutBundleAndVersion() throws Exception {
+        File f = createZip(ImmutableMap.<String, String>of("catalog.bom", Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  itemType: entity",
+                "  name: My Catalog App",
+                "  description: My description",
+                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity")));
+
+        Response response = client().path("/catalog")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        assertTrue(response.readEntity(String.class).contains("Catalog BOM must define bundle and version"));
+    }
+
+    @Test
+    public void testArchiveWithoutBundle() throws Exception {
+        File f = createZip(ImmutableMap.<String, String>of("catalog.bom", Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  version: 0.1.0",
+                "  itemType: entity",
+                "  name: My Catalog App",
+                "  description: My description",
+                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity")));
+
+        Response response = client().path("/catalog")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        assertTrue(response.readEntity(String.class).contains("Catalog BOM must define bundle"));
+    }
+
+    @Test
+    public void testArchiveWithoutVersion() throws Exception {
+        File f = createZip(ImmutableMap.<String, String>of("catalog.bom", Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  bundle: org.apache.brooklyn.test",
+                "  itemType: entity",
+                "  name: My Catalog App",
+                "  description: My description",
+                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity")));
+
+        Response response = client().path("/catalog")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        assertTrue(response.readEntity(String.class).contains("Catalog BOM must define version"));
+    }
+
+    @Test
+    public void testJarWithoutMatchingBundle() throws Exception {
+        String name = "My Catalog App";
+        String bundle = "org.apache.brooklyn.test";
+        String version = "0.1.0";
+        File f = createJar(ImmutableMap.<String, String>of(
+                "catalog.bom", Joiner.on("\n").join(
+                        "brooklyn.catalog:",
+                        "  bundle: " + bundle,
+                        "  version: " + version,
+                        "  itemType: entity",
+                        "  name: " + name,
+                        "  description: My description",
+                        "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                        "  item:",
+                        "    type: org.apache.brooklyn.core.test.entity.TestEntity"),
+                "META-INF/MANIFEST.MF", Joiner.on("\n").join(
+                        "Manifest-Version: 1.0",
+                        "Bundle-Name: " + name,
+                        "Bundle-SymbolicName: org.apache.brooklyn.test2",
+                        "Bundle-Version: " + version,
+                        "Bundle-ManifestVersion: " + version)));
+
+        Response response = client().path("/catalog")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        assertTrue(response.readEntity(String.class).contains("JAR MANIFEST symbolic-name 'org.apache.brooklyn.test2' does not match '"+bundle+"' defined in BOM"));
+    }
+
+    @Test
+    public void testJarWithoutMatchingVersion() throws Exception {
+        String name = "My Catalog App";
+        String bundle = "org.apache.brooklyn.test";
+        String version = "0.1.0";
+        File f = createJar(ImmutableMap.<String, String>of(
+                "catalog.bom", Joiner.on("\n").join(
+                        "brooklyn.catalog:",
+                        "  bundle: " + bundle,
+                        "  version: " + version,
+                        "  itemType: entity",
+                        "  name: " + name,
+                        "  description: My description",
+                        "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                        "  item:",
+                        "    type: org.apache.brooklyn.core.test.entity.TestEntity"),
+                "META-INF/MANIFEST.MF", Joiner.on("\n").join(
+                        "Manifest-Version: 1.0",
+                        "Bundle-Name: " + name,
+                        "Bundle-SymbolicName: " + bundle,
+                        "Bundle-Version: 0.3.0",
+                        "Bundle-ManifestVersion: 0.3.0")));
+
+        Response response = client().path("/catalog")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        assertTrue(response.readEntity(String.class).contains("JAR MANIFEST version '0.3.0' does not match '"+version+"' defined in BOM"));
+    }
+
     @Test
     public void testOsgiBundleWithBom() throws Exception {
         TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
@@ -508,8 +668,9 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
         
         String bom = Joiner.on("\n").join(
                 "brooklyn.catalog:",
-                "  id: " + symbolicName,
+                "  bundle: " + symbolicName,
                 "  version: " + version,
+                "  id: " + symbolicName,
                 "  itemType: entity",
                 "  name: My Catalog App",
                 "  description: My description",
@@ -520,7 +681,7 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
         f = bm.copyAdding(f, MutableMap.of(new ZipEntry("catalog.bom"), (InputStream) new ByteArrayInputStream(bom.getBytes())));
 
         Response response = client().path("/catalog")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
                 .post(Streams.readFully(new FileInputStream(f)));
 
         
@@ -569,6 +730,94 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
         assertEquals(iconData.length, 43);
     }
 
+    @Test
+    public void testOsgiBundleWithBomNotInBrooklynNamespace() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_PATH);
+        final String symbolicName = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_SYMBOLIC_NAME_FULL;
+        final String version = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_VERSION;
+        final String bundleUrl = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_URL;
+        final String entityType = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_ENTITY;
+        final String iconPath = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_ICON_PATH;
+        BundleMaker bm = new BundleMaker(manager);
+        File f = Os.newTempFile("osgi", "jar");
+        Files.copyFile(ResourceUtils.create(this).getResourceFromUrl(bundleUrl), f);
+
+        String bom = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  bundle: " + symbolicName,
+                "  version: " + version,
+                "  id: " + symbolicName,
+                "  itemType: entity",
+                "  name: My Catalog App",
+                "  description: My description",
+                "  icon_url: classpath:" + iconPath,
+                "  item:",
+                "    type: " + entityType);
+
+        f = bm.copyAdding(f, MutableMap.of(new ZipEntry("catalog.bom"), (InputStream) new ByteArrayInputStream(bom.getBytes())));
+
+        Response response = client().path("/catalog")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+
+        assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
+
+        CatalogEntitySummary entityItem = client().path("/catalog/entities/"+symbolicName + "/" + version)
+                .get(CatalogEntitySummary.class);
+
+        Assert.assertNotNull(entityItem.getPlanYaml());
+        Assert.assertTrue(entityItem.getPlanYaml().contains(entityType));
+
+        assertEquals(entityItem.getId(), CatalogUtils.getVersionedId(symbolicName, version));
+        assertEquals(entityItem.getSymbolicName(), symbolicName);
+        assertEquals(entityItem.getVersion(), version);
+
+        // and internally let's check we have libraries
+        RegisteredType item = getManagementContext().getTypeRegistry().get(symbolicName, version);
+        Assert.assertNotNull(item);
+        Collection<OsgiBundleWithUrl> libs = item.getLibraries();
+        assertEquals(libs.size(), 1);
+        OsgiBundleWithUrl lib = Iterables.getOnlyElement(libs);
+        Assert.assertNull(lib.getUrl());
+
+        assertEquals(lib.getSymbolicName(), symbolicName);
+        assertEquals(lib.getVersion(), version);
+
+        // now let's check other things on the item
+        URI expectedIconUrl = URI.create(getEndpointAddress() + "/catalog/icon/" + symbolicName + "/" + entityItem.getVersion()).normalize();
+        assertEquals(entityItem.getName(), "My Catalog App");
+        assertEquals(entityItem.getDescription(), "My description");
+        assertEquals(entityItem.getIconUrl(), expectedIconUrl.getPath());
+        assertEquals(item.getIconUrl(), "classpath:" + iconPath);
+
+        // an InterfacesTag should be created for every catalog item
+        assertEquals(entityItem.getTags().size(), 1);
+        Object tag = entityItem.getTags().iterator().next();
+        @SuppressWarnings("unchecked")
+        List<String> actualInterfaces = ((Map<String, List<String>>) tag).get("traits");
+        List<String> expectedInterfaces = ImmutableList.of(Entity.class.getName(), BrooklynObject.class.getName(), Identifiable.class.getName(), Configurable.class.getName());
+        assertTrue(actualInterfaces.containsAll(expectedInterfaces), "actual="+actualInterfaces);
+
+        byte[] iconData = client().path("/catalog/icon/" + symbolicName + "/" + version).get(byte[].class);
+        assertEquals(iconData.length, 43);
+
+        // Check that the catalog item is useable (i.e. can deploy the entity)
+        String appYaml = Joiner.on("\n").join(
+                "services:",
+                "- type: " + symbolicName + ":" + version,
+                "  name: myEntityName");
+
+        Response appResponse = client().path("/applications")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-yaml")
+                .post(appYaml);
+
+        assertEquals(appResponse.getStatus(), Response.Status.CREATED.getStatusCode());
+
+        Entity entity = Iterables.tryFind(getManagementContext().getEntityManager().getEntities(), EntityPredicates.displayNameEqualTo("myEntityName")).get();
+        assertEquals(entity.getEntityType().getName(), entityType);
+    }
+
     private void addAddCatalogItemWithInvalidBundleUrl(String bundleUrl) {
         String symbolicName = "my.catalog.entity.id";
         String yaml = Joiner.on("\n").join(
@@ -593,4 +842,40 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
     private static String ver(String id) {
         return CatalogUtils.getVersionedId(id, TEST_VERSION);
     }
+
+    private static File createZip(Map<String, String> files) throws Exception {
+        File f = Os.newTempFile("osgi", "zip");
+
+        ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(f));
+
+        for (Map.Entry<String, String> entry : files.entrySet()) {
+            ZipEntry ze = new ZipEntry(entry.getKey());
+            zip.putNextEntry(ze);
+            zip.write(entry.getValue().getBytes());
+        }
+
+        zip.closeEntry();
+        zip.flush();
+        zip.close();
+
+        return f;
+    }
+
+    private static File createJar(Map<String, String> files) throws Exception {
+        File f = Os.newTempFile("osgi", "jar");
+
+        JarOutputStream zip = new JarOutputStream(new FileOutputStream(f));
+
+        for (Map.Entry<String, String> entry : files.entrySet()) {
+            JarEntry ze = new JarEntry(entry.getKey());
+            zip.putNextEntry(ze);
+            zip.write(entry.getValue().getBytes());
+        }
+
+        zip.closeEntry();
+        zip.flush();
+        zip.close();
+
+        return f;
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e2fa0775/utils/common/src/test/java/org/apache/brooklyn/util/osgi/OsgiTestResources.java
----------------------------------------------------------------------
diff --git a/utils/common/src/test/java/org/apache/brooklyn/util/osgi/OsgiTestResources.java b/utils/common/src/test/java/org/apache/brooklyn/util/osgi/OsgiTestResources.java
index 83a44ef..7e19843 100644
--- a/utils/common/src/test/java/org/apache/brooklyn/util/osgi/OsgiTestResources.java
+++ b/utils/common/src/test/java/org/apache/brooklyn/util/osgi/OsgiTestResources.java
@@ -62,6 +62,9 @@ public class OsgiTestResources {
     public static final String BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_SYMBOLIC_NAME_FULL =
         "com.example.brooklyn.test.resources.osgi."+BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_SYMBOLIC_NAME_FINAL_PART;
     public static final String BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_PATH = "/brooklyn/osgi/brooklyn-test-osgi-com-example-entities.jar";
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_URL = "classpath:"+BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_PATH;
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_VERSION = "0.1.0";
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_ICON_PATH = "/com/example/brooklyn/test/osgi/entities/icon.gif";
     public static final String BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_APPLICATION = "com.example.brooklyn.test.osgi.entities.SimpleApplication";
     public static final String BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_ENTITY = "com.example.brooklyn.test.osgi.entities.SimpleEntity";
     public static final String BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_POLICY = "com.example.brooklyn.test.osgi.entities.SimplePolicy";


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

Posted by ge...@apache.org.
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();


[09/12] brooklyn-server git commit: Tidy ugly dependency on `CatalogBomScanner` in the tracker and loader.

Posted by ge...@apache.org.
Tidy ugly dependency on `CatalogBomScanner` in the tracker and loader.

Replace it with a simple predicate that wraps the whitelist checks and
inject the predicate into the loader. That way the CatalogBomScanner
can inject the correct whitelist configuration in the blueprint.xml,
while in the REST API here we just live with an "always true" for the
moment.

Create the loader the scanner and inject it into the tracker,
which simplifies the latter a bit.


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

Branch: refs/heads/master
Commit: ffded0c8a4b451b5162fcd8a9f046ad94a4070da
Parents: f86eb87
Author: Geoff Macartney <ge...@cloudsoftcorp.com>
Authored: Fri Mar 24 17:37:40 2017 +0000
Committer: Geoff Macartney <ge...@cloudsoftcorp.com>
Committed: Fri Mar 24 17:37:40 2017 +0000

----------------------------------------------------------------------
 .../catalog/internal/CatalogBomScanner.java     | 44 ++++++++++++++++----
 .../catalog/internal/CatalogBundleLoader.java   | 29 ++++---------
 .../catalog/internal/CatalogBundleTracker.java  | 36 +++-------------
 .../core/catalog/internal/CatalogUtils.java     |  7 ++++
 .../rest/resources/CatalogResource.java         |  5 ++-
 5 files changed, 60 insertions(+), 61 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ffded0c8/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 78e7d54..1f528c8 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
@@ -20,15 +20,19 @@ package org.apache.brooklyn.core.catalog.internal;
 
 import java.util.List;
 
+import javax.annotation.Nullable;
+
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.core.BrooklynFeatureEnablement;
 import org.apache.brooklyn.util.text.Strings;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.Beta;
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 
 /** Scans bundles being added, filtered by a whitelist and blacklist, and adds catalog.bom files to the catalog.
@@ -45,16 +49,22 @@ public class CatalogBomScanner {
 
     private CatalogBundleTracker catalogBundleTracker;
 
-    public void bind(ServiceReference<ManagementContext> managementContext) throws Exception {
+    public void bind(ServiceReference<ManagementContext> mgmtContextReference) throws Exception {
         if (isEnabled()) {
             LOG.debug("Binding management context with whiteList [{}] and blacklist [{}]",
                 Strings.join(getWhiteList(), "; "),
                 Strings.join(getBlackList(), "; "));
-            catalogBundleTracker = new CatalogBundleTracker(this, managementContext);
+
+            final BundleContext bundleContext = mgmtContextReference.getBundle().getBundleContext();
+            ManagementContext mgmt = bundleContext.getService(mgmtContextReference);
+            CatalogBundleLoader bundleLoader = new CatalogBundleLoader(new SymbolicNameAccessControl(), mgmt);
+
+            catalogBundleTracker = new CatalogBundleTracker(bundleContext, bundleLoader);
+            catalogBundleTracker.open();
         }
     }
 
-    public void unbind(ServiceReference<ManagementContext> managementContext) throws Exception {
+    public void unbind(ServiceReference<ManagementContext> mgmtContextReference) throws Exception {
         if (isEnabled()) {
             LOG.debug("Unbinding management context");
             if (null != catalogBundleTracker) {
@@ -62,6 +72,7 @@ public class CatalogBomScanner {
                 catalogBundleTracker = null;
                 temp.close();
             }
+            mgmtContextReference.getBundle().getBundleContext().ungetService(mgmtContextReference);
         }
     }
 
@@ -69,12 +80,6 @@ public class CatalogBomScanner {
         return BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_LOAD_BUNDLE_CATALOG_BOM);
     }
 
-    public String[] bundleIds(Bundle bundle) {
-        return new String[] {
-            String.valueOf(bundle.getBundleId()), bundle.getSymbolicName(), bundle.getVersion().toString()
-        };
-    }
-
     public List<String> getWhiteList() {
         return whiteList;
     }
@@ -101,4 +106,25 @@ public class CatalogBomScanner {
         this.blackList = Strings.parseCsv(blackListText);
     }
 
+    public class SymbolicNameAccessControl implements Predicate<Bundle> {
+        @Override
+        public boolean apply(@Nullable Bundle input) {
+            return passesWhiteAndBlacklists(input);
+        }
+    }
+
+    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;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ffded0c8/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
index 20fe5e2..b1f8fd3 100644
--- 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
@@ -41,6 +41,7 @@ import org.yaml.snakeyaml.DumperOptions;
 import org.yaml.snakeyaml.Yaml;
 
 import com.google.common.annotations.Beta;
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 
@@ -52,11 +53,11 @@ public class CatalogBundleLoader {
     private static final String BROOKLYN_CATALOG = "brooklyn.catalog";
     private static final String BROOKLYN_LIBRARIES = "brooklyn.libraries";
 
-    private CatalogBomScanner catalogBomScanner;
+    private Predicate<Bundle> applicationsPermitted;
     private ManagementContext managementContext;
 
-    public CatalogBundleLoader(CatalogBomScanner catalogBomScanner, ManagementContext managementContext) {
-        this.catalogBomScanner = catalogBomScanner;
+    public CatalogBundleLoader(Predicate<Bundle> applicationsPermitted, ManagementContext managementContext) {
+        this.applicationsPermitted = applicationsPermitted;
         this.managementContext = managementContext;
     }
 
@@ -73,7 +74,7 @@ public class CatalogBundleLoader {
 
         final URL bom = bundle.getResource(CatalogBundleLoader.CATALOG_BOM_URL);
         if (null != bom) {
-            LOG.debug("Found catalog BOM in {} {} {}", catalogBomScanner.bundleIds(bundle));
+            LOG.debug("Found catalog BOM in {} {} {}", CatalogUtils.bundleIds(bundle));
             String bomText = readBom(bom);
             String bomWithLibraryPath = addLibraryDetails(bundle, bomText);
             catalogItems = this.managementContext.getCatalog().addItems(bomWithLibraryPath);
@@ -81,10 +82,10 @@ public class CatalogBundleLoader {
                 LOG.debug("Added to catalog: {}, {}", item.getSymbolicName(), item.getVersion());
             }
         } else {
-            LOG.debug("No BOM found in {} {} {}", catalogBomScanner.bundleIds(bundle));
+            LOG.debug("No BOM found in {} {} {}", CatalogUtils.bundleIds(bundle));
         }
 
-        if (!passesWhiteAndBlacklists(bundle)) {
+        if (!applicationsPermitted.apply(bundle)) {
             catalogItems = removeAnyApplications(catalogItems);
         }
 
@@ -125,7 +126,8 @@ public class CatalogBundleLoader {
                 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());
+                LOG.warn("Unexpected syntax for {} (expected Map, but got {}), ignoring",
+                    CatalogBundleLoader.BROOKLYN_CATALOG, catalog.getClass().getName());
             }
         }
         final String updatedBom = backToYaml(bom);
@@ -175,17 +177,4 @@ public class CatalogBundleLoader {
         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/ffded0c8/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
index 4cb2470..074d7f7 100644
--- 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
@@ -20,10 +20,9 @@
 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.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;
@@ -35,36 +34,11 @@ public class CatalogBundleTracker extends BundleTracker<Iterable<? extends Catal
 
     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;
+    public CatalogBundleTracker(BundleContext bundleContext, CatalogBundleLoader catalogBundleLoader) {
+        super(bundleContext, Bundle.ACTIVE, null);
+        this.catalogBundleLoader = catalogBundleLoader;
     }
 
     /**
@@ -94,7 +68,7 @@ public class CatalogBundleTracker extends BundleTracker<Iterable<? extends Catal
         if (!items.iterator().hasNext()) {
             return;
         }
-        LOG.debug("Unloading catalog BOM entries from {} {} {}", catalogBomScanner.bundleIds(bundle));
+        LOG.debug("Unloading catalog BOM entries from {} {} {}", CatalogUtils.bundleIds(bundle));
         for (CatalogItem<?, ?> item : items) {
             LOG.debug("Unloading {} {} from catalog", item.getSymbolicName(), item.getVersion());
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ffded0c8/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java
index ff96ae5..8306b71 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogUtils.java
@@ -48,6 +48,7 @@ import org.apache.brooklyn.core.typereg.RegisteredTypes;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.time.Time;
+import org.osgi.framework.Bundle;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -323,4 +324,10 @@ public class CatalogUtils {
         mgmt.getCatalog().persist(item);
     }
 
+
+    public static String[] bundleIds(Bundle bundle) {
+        return new String[] {
+            String.valueOf(bundle.getBundleId()), bundle.getSymbolicName(), bundle.getVersion().toString()
+        };
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ffded0c8/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 a5cdfe4..e00cad8 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
@@ -247,7 +247,10 @@ 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 CatalogBundleLoader(new CatalogBomScanner(), mgmt()).scanForCatalog(bundle);
+                    // TODO improve on this - it ignores the configuration of whitelists, see CatalogBomScanner.
+                    final Predicate<Bundle> applicationsPermitted = Predicates.<Bundle>alwaysTrue();
+
+                    new CatalogBundleLoader(applicationsPermitted, mgmt()).scanForCatalog(bundle);
                 } catch (RuntimeException ex) {
                     try {
                         bundle.uninstall();


[06/12] brooklyn-server git commit: Make swagger display only the generic method to upload a YAML of ZIP/JAR archive

Posted by ge...@apache.org.
Make swagger display only the generic method to upload a YAML of ZIP/JAR archive


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

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

----------------------------------------------------------------------
 rest/rest-api/pom.xml                           |  2 +-
 .../apache/brooklyn/rest/api/CatalogApi.java    | 63 ++++++++++++++------
 2 files changed, 45 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c93b05e5/rest/rest-api/pom.xml
----------------------------------------------------------------------
diff --git a/rest/rest-api/pom.xml b/rest/rest-api/pom.xml
index 7b3a05d..0c2c690 100644
--- a/rest/rest-api/pom.xml
+++ b/rest/rest-api/pom.xml
@@ -113,7 +113,7 @@
             <plugin>
                 <groupId>com.github.kongchen</groupId>
                 <artifactId>swagger-maven-plugin</artifactId>
-                <version>3.1.0</version>
+                <version>3.1.4</version>
                 <configuration>
                     <apiSources>
                         <apiSource>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c93b05e5/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
index 66db391..dcfaa78 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
@@ -55,41 +55,66 @@ public interface CatalogApi {
     @Deprecated /** @deprecated since 0.11.0 use {@link #createFromYaml(String)} instead */
     public Response create(String yaml);
 
+    @POST
     @Consumes({MediaType.APPLICATION_JSON, "application/x-yaml",
         // see http://stackoverflow.com/questions/332129/yaml-mime-type
         "text/yaml", "text/x-yaml", "application/yaml"})
-    @POST
-    @ApiOperation(value = "Add a catalog item (e.g. new type of entity, policy or location) by uploading YAML descriptor. "
-        + "Return value is map of ID to CatalogItemSummary, with code 201 CREATED.", response = String.class)
+    @ApiOperation(
+            value = "Add a catalog items (e.g. new type of entity, policy or location) by uploading YAML descriptor.",
+            notes = "Return value is map of ID to CatalogItemSummary.",
+            response = String.class,
+            hidden = true
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 400, message = "Error processing the given YAML"),
+            @ApiResponse(code = 201, message = "Catalog items added successfully")
+    })
     public Response createFromYaml(
             @ApiParam(name = "yaml", value = "YAML descriptor of catalog item", required = true)
             @Valid String yaml);
 
-    @POST
     @Beta
-    @Consumes  // anything (if doesn't match other methods with specific content types
-    @ApiOperation(value = "Add items to the catalog, either YAML or JAR/ZIP, format autodetected. "
-            + "Specify a content-type header to skip auto-detection and invoke one of the more specific methods. "
-            + "Return value is 201 CREATED if bundle could be added.", response = String.class)
-    public Response createFromUpload(
-            @ApiParam(
-                    name = "item",
-                    value = "Item to install, as JAR/ZIP or Catalog YAML (autodetected)",
-                    required = true)
-            byte[] item);
-
     @POST
-    @Beta
     @Consumes({"application/x-zip", "application/x-jar"})
-    @ApiOperation(value = "Add a catalog item (e.g. new type of entity, policy or location) by uploading OSGi bundle JAR, or ZIP which will be turned into bundle JAR, "
-            + "containing catalog.bom containing bundle name and version. "
-            + "Return value is 201 CREATED if bundle could be added.", response = String.class)
+    @ApiOperation(
+            value = "Add a catalog items (e.g. new type of entity, policy or location) by uploading a ZIP/JAR archive.",
+            notes = "Accepts either an OSGi bundle JAR, or ZIP which will be turned into bundle JAR. Bother format must "
+                    + "contain a catalog.bom at the root of the archive, which must contain the bundle and version key."
+                    + "Return value is map of ID to CatalogItemSummary.",
+            response = String.class,
+            hidden = true)
+    @ApiResponses(value = {
+            @ApiResponse(code = 400, message = "Error processing the given archive, or the catalog.bom is invalid"),
+            @ApiResponse(code = 201, message = "Catalog items added successfully")
+    })
     public Response createFromArchive(
             @ApiParam(
                     name = "archive",
                     value = "Bundle to install, in ZIP or JAR format, requiring catalog.bom containing bundle name and version",
                     required = true)
             byte[] archive);
+
+    @Beta
+    @POST
+    @Consumes // anything (if doesn't match other methods with specific content types
+    @ApiOperation(
+            value = "Add a catalog items (e.g. new type of entity, policy or location) by uploading either YAML or ZIP/JAR archive (format autodetected)",
+            notes = "Specify a content-type header to skip auto-detection and invoke one of the more specific methods. "
+                    + "Accepts either an OSGi bundle JAR, or ZIP which will be turned into bundle JAR. Bother format must "
+                    + "contain a catalog.bom at the root of the archive, which must contain the bundle and version key."
+                    + "Return value is map of ID to CatalogItemSummary.",
+            response = String.class
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 400, message = "Error processing the given archive, or the catalog.bom is invalid"),
+            @ApiResponse(code = 201, message = "Catalog items added successfully")
+    })
+    public Response createFromUpload(
+            @ApiParam(
+                    name = "item",
+                    value = "Item to install, as JAR/ZIP or Catalog YAML (autodetected)",
+                    required = true)
+                    byte[] item);
     
     @POST
     @Consumes(MediaType.APPLICATION_XML)


[10/12] brooklyn-server git commit: Merge pull request #2 from geomacy/feature/upload-archive

Posted by ge...@apache.org.
Merge pull request #2 from geomacy/feature/upload-archive

Tidy ugly dependency on `CatalogBomScanner` in the tracker and loader.

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

Branch: refs/heads/master
Commit: 99bf769256cab27dd3acffdcabcfd2b3effb3a6a
Parents: 5fb40ff ffded0c
Author: Thomas Bouron <tb...@gmail.com>
Authored: Mon Mar 27 10:30:08 2017 +0100
Committer: GitHub <no...@github.com>
Committed: Mon Mar 27 10:30:08 2017 +0100

----------------------------------------------------------------------
 .../catalog/internal/CatalogBomScanner.java     | 44 ++++++++++++++++----
 .../catalog/internal/CatalogBundleLoader.java   | 29 ++++---------
 .../catalog/internal/CatalogBundleTracker.java  | 36 +++-------------
 .../core/catalog/internal/CatalogUtils.java     |  7 ++++
 .../rest/resources/CatalogResource.java         |  5 ++-
 5 files changed, 60 insertions(+), 61 deletions(-)
----------------------------------------------------------------------



[04/12] brooklyn-server git commit: Separate the CatalogPopulator from the CatalogBundleLoader

Posted by ge...@apache.org.
Separate the CatalogPopulator from the 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/84c1b69f
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/84c1b69f
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/84c1b69f

Branch: refs/heads/master
Commit: 84c1b69f83b79ce43a5b7d19c68971775c71d000
Parents: 2ed7543
Author: Thomas Bouron <th...@cloudsoftcorp.com>
Authored: Thu Mar 23 16:05:29 2017 +0000
Committer: Thomas Bouron <th...@cloudsoftcorp.com>
Committed: Thu Mar 23 17:21:02 2017 +0000

----------------------------------------------------------------------
 .../catalog/internal/CatalogBomScanner.java     | 144 +++++++++++--------
 1 file changed, 87 insertions(+), 57 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/84c1b69f/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 8bbb34d..1348886 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
@@ -44,6 +44,7 @@ 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;
@@ -51,6 +52,7 @@ 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. */
+@Beta
 public class CatalogBomScanner {
 
     private final String ACCEPT_ALL_BY_DEFAULT = ".*";
@@ -126,6 +128,7 @@ public class CatalogBomScanner {
         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);
@@ -145,6 +148,7 @@ public class CatalogBomScanner {
             if (mgmtContextReference != null) {
                 bundleContext = getBundleContext();
                 managementContext = getManagementContext();
+                catalogBundleLoader = new CatalogBundleLoader(managementContext);
             }
             super.open();
         }
@@ -156,6 +160,7 @@ public class CatalogBomScanner {
                 managementContext = null;
                 getBundleContext().ungetService(mgmtContextReference);
                 bundleContext = null;
+                catalogBundleLoader = null;
             }
         }
 
@@ -179,13 +184,23 @@ public class CatalogBomScanner {
          *
          * @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 scanForCatalog(bundle);
+            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()) {
@@ -195,22 +210,29 @@ public class CatalogBomScanner {
             for (CatalogItem<?, ?> item : items) {
                 LOG.debug("Unloading {} {} from catalog", item.getSymbolicName(), item.getVersion());
 
-                removeFromCatalog(item);
+                catalogBundleLoader.removeFromCatalog(item);
             }
         }
+    }
 
-        private void removeFromCatalog(CatalogItem<?, ?> item) {
-            try {
-                getManagementContext().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);
-            }
+    @Beta
+    public class CatalogBundleLoader {
+
+        private ManagementContext managementContext;
+
+        public CatalogBundleLoader(ManagementContext managementContext) {
+            this.managementContext = managementContext;
         }
 
-        private Iterable<? extends CatalogItem<?, ?>> scanForCatalog(Bundle bundle) {
+        /**
+         * 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();
 
@@ -219,11 +241,10 @@ public class CatalogBomScanner {
                 LOG.debug("Found catalog BOM in {} {} {}", bundleIds(bundle));
                 String bomText = readBom(bom);
                 String bomWithLibraryPath = addLibraryDetails(bundle, bomText);
-                catalogItems = getManagementContext().getCatalog().addItems(bomWithLibraryPath);
+                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));
             }
@@ -235,33 +256,12 @@ public class CatalogBomScanner {
             return catalogItems;
         }
 
-        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;
-                }
+        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);
             }
-            return false;
         }
 
         private String addLibraryDetails(Bundle bundle, String bomText) {
@@ -282,20 +282,13 @@ public class CatalogBomScanner {
             return updatedBom;
         }
 
-        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 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 " 
+                throw new RuntimeException("expected " + BROOKLYN_LIBRARIES + " to be a list, but got "
                         + (librarySpec == null ? "null" : librarySpec.getClass().getName()));
             }
             @SuppressWarnings("unchecked")
@@ -304,20 +297,57 @@ public class CatalogBomScanner {
                 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()));
+                    "name", bundle.getSymbolicName(),
+                    "version", bundle.getVersion().toString()));
             LOG.debug("library spec is {}", librarySpec);
         }
 
-        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 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);
+            }
+        }
+    }
 
 }


[12/12] brooklyn-server git commit: Closes #608

Posted by ge...@apache.org.
Closes #608

`BundleMaker` REST call allowing to add ZIP/JAR to catalog - Part #2

Builds on top of #485. It implements the missing bits and pieces, such as:
- fields `version` and `bundle` are required under `brooklyn.catalog` when uploading a ZIP/JAR archive
- returns 400 HTTP status code is anything goes wrong during the process of the archive (as this is a bad input, therefore a bad request)
- avoid using the `CatalogPopulator` as it reloads all bundles. Instead, it uses the new `CatalogBundleLoader` that loads only a given bundle
- if the bundle has been installed, but the CAMP parser fails, the bundle is uninstalled
- adds more unit tests, especially to check error handling


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

Branch: refs/heads/master
Commit: 40d8cfa2343b75328a6f9466d98482221172d38d
Parents: b7fef0f 2e91b99
Author: Geoff Macartney <ge...@cloudsoftcorp.com>
Authored: Mon Mar 27 11:34:49 2017 +0100
Committer: Geoff Macartney <ge...@cloudsoftcorp.com>
Committed: Mon Mar 27 11:34:49 2017 +0100

----------------------------------------------------------------------
 .../catalog/internal/BasicBrooklynCatalog.java  |  39 +--
 .../catalog/internal/CatalogBomScanner.java     | 257 ++--------------
 .../catalog/internal/CatalogBundleLoader.java   | 180 ++++++++++++
 .../catalog/internal/CatalogBundleTracker.java  |  78 +++++
 .../core/catalog/internal/CatalogUtils.java     |   7 +
 .../brooklyn/util/core/osgi/BundleMaker.java    |   4 +-
 rest/rest-api/pom.xml                           |  11 +-
 .../apache/brooklyn/rest/api/CatalogApi.java    |  77 +++--
 .../rest/resources/CatalogResource.java         |  51 ++--
 .../rest/resources/CatalogResourceTest.java     | 291 ++++++++++++++++++-
 .../brooklyn/util/osgi/OsgiTestResources.java   |   3 +
 11 files changed, 693 insertions(+), 305 deletions(-)
----------------------------------------------------------------------



[03/12] brooklyn-server git commit: Using the new CatalogBundleLoader and make sure the REST API returns a 400 + uninstall the bundle if an error occur

Posted by ge...@apache.org.
Using the new CatalogBundleLoader and make sure the REST API returns a 400 + uninstall the bundle if an error occur


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

Branch: refs/heads/master
Commit: 5977702aef29c73c7cc38a4119ca7be39dd590ca
Parents: 84c1b69
Author: Thomas Bouron <th...@cloudsoftcorp.com>
Authored: Thu Mar 23 16:15:24 2017 +0000
Committer: Thomas Bouron <th...@cloudsoftcorp.com>
Committed: Thu Mar 23 17:21:02 2017 +0000

----------------------------------------------------------------------
 .../apache/brooklyn/rest/api/CatalogApi.java    |  4 +-
 .../rest/resources/CatalogResource.java         | 41 ++++++++++++--------
 2 files changed, 27 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5977702a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
index 79b6186..66db391 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
@@ -71,13 +71,13 @@ public interface CatalogApi {
     @ApiOperation(value = "Add items to the catalog, either YAML or JAR/ZIP, format autodetected. "
             + "Specify a content-type header to skip auto-detection and invoke one of the more specific methods. "
             + "Return value is 201 CREATED if bundle could be added.", response = String.class)
-    public Response createPoly(
+    public Response createFromUpload(
             @ApiParam(
                     name = "item",
                     value = "Item to install, as JAR/ZIP or Catalog YAML (autodetected)",
                     required = true)
             byte[] item);
-    
+
     @POST
     @Beta
     @Consumes({"application/x-zip", "application/x-jar"})

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5977702a/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 9fdf977..8cf4d01 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
@@ -85,10 +85,12 @@ import org.apache.brooklyn.util.yaml.Yamls;
 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
 import org.apache.commons.compress.archivers.zip.ZipFile;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
@@ -115,7 +117,8 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
     static Set<String> missingIcons = MutableSet.of();
 
     @Override
-    public Response createPoly(byte[] item) {
+    @Beta
+    public Response createFromUpload(byte[] item) {
         Throwable yamlException = null;
         try {
             MutableList.copyOf( Yamls.parseAll(new InputStreamReader(new ByteArrayInputStream(item))) );
@@ -129,14 +132,11 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
             return createFromYaml(new String(item));
         }
         
-        try {
-            return createFromArchive(item);
-        } catch (Exception e) {
-            throw Exceptions.propagate("Unable to handle input: not YAML or compatible ZIP. Specify Content-Type to clarify.", e);
-        }
+        return createFromArchive(item);
     }
     
-    @Override @Deprecated
+    @Override
+    @Deprecated
     public Response create(String yaml) {
         return createFromYaml(yaml);
     }
@@ -177,6 +177,7 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
     }
 
     @Override
+    @Beta
     public Response createFromArchive(byte[] zipInput) {
         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.ROOT, null)) {
             throw WebResourceUtils.forbidden("User '%s' is not authorized to add catalog item",
@@ -197,20 +198,20 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
             try {
                 zf = new ZipFile(f);
             } catch (IOException e) {
-                throw WebResourceUtils.badRequest("Invalid ZIP/JAR archive: "+e);
+                throw new IllegalArgumentException("Invalid ZIP/JAR archive: "+e);
             }
             ZipArchiveEntry bom = zf.getEntry("catalog.bom");
             if (bom==null) {
                 bom = zf.getEntry("/catalog.bom");
             }
             if (bom==null) {
-                throw WebResourceUtils.badRequest("Archive must contain a catalog.bom file in the root");
+                throw new IllegalArgumentException("Archive must contain a catalog.bom file in the root");
             }
             String bomS;
             try {
                 bomS = Streams.readFullyString(zf.getInputStream(bom));
             } catch (IOException e) {
-                throw WebResourceUtils.badRequest("Error reading catalog.bom from ZIP/JAR archive: "+e);
+                throw new IllegalArgumentException("Error reading catalog.bom from ZIP/JAR archive: "+e);
             }
             VersionedName vn = BasicBrooklynCatalog.getVersionedName( BasicBrooklynCatalog.getCatalogMetadata(bomS) );
             
@@ -221,7 +222,7 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
             String bundleNameInMF = mf.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
             if (Strings.isNonBlank(bundleNameInMF)) {
                 if (!bundleNameInMF.equals(vn.getSymbolicName())) {
-                    throw new IllegalStateException("JAR MANIFEST symbolic-name '"+bundleNameInMF+"' does not match '"+vn.getSymbolicName()+"' defined in BOM");
+                    throw new IllegalArgumentException("JAR MANIFEST symbolic-name '"+bundleNameInMF+"' does not match '"+vn.getSymbolicName()+"' defined in BOM");
                 }
             } else {
                 mf.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, vn.getSymbolicName());
@@ -230,7 +231,7 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
             String bundleVersionInMF = mf.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
             if (Strings.isNonBlank(bundleVersionInMF)) {
                 if (!bundleVersionInMF.equals(vn.getVersion().toString())) {
-                    throw new IllegalStateException("JAR MANIFEST version '"+bundleVersionInMF+"' does not match '"+vn.getVersion()+"' defined in BOM");
+                    throw new IllegalArgumentException("JAR MANIFEST version '"+bundleVersionInMF+"' does not match '"+vn.getVersion()+"' defined in BOM");
                 }
             } else {
                 mf.getMainAttributes().putValue(Constants.BUNDLE_VERSION, vn.getVersion().toString());
@@ -245,13 +246,21 @@ 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)
-                new CatalogBomScanner().new CatalogPopulator(
-                        ((LocalManagementContext) mgmt()).getOsgiManager().get().getFramework().getBundleContext(),
-                        mgmt()).addingBundle(bundle, null);
+                try {
+                    new CatalogBomScanner().new CatalogBundleLoader(mgmt()).scanForCatalog(bundle);
+                } catch (RuntimeException ex) {
+                    try {
+                        bundle.uninstall();
+                    } catch (BundleException e) {
+                        log.error("Cannot uninstall bundle " + bundle.getSymbolicName() + ":" + bundle.getVersion(), e);
+                    }
+                    throw new IllegalArgumentException("Error installing catalog items", ex);
+                }
             }
             
             return Response.status(Status.CREATED).build();
-            
+        } catch (RuntimeException ex) {
+            throw WebResourceUtils.badRequest(ex);
         } finally {
             if (f!=null) f.delete();
             if (f2!=null) f2.delete();


[11/12] brooklyn-server git commit: Make sure that the right management context is passed to the BundleMaker

Posted by ge...@apache.org.
Make sure that the right management context is passed to the BundleMaker


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

Branch: refs/heads/master
Commit: 2e91b99fce7c2e4f3227d4a2bf4b4f7bf7052e10
Parents: 99bf769
Author: Thomas Bouron <th...@cloudsoftcorp.com>
Authored: Mon Mar 27 10:52:14 2017 +0100
Committer: Thomas Bouron <th...@cloudsoftcorp.com>
Committed: Mon Mar 27 10:55:12 2017 +0100

----------------------------------------------------------------------
 .../java/org/apache/brooklyn/util/core/osgi/BundleMaker.java    | 4 ++--
 .../org/apache/brooklyn/rest/resources/CatalogResource.java     | 5 +++--
 2 files changed, 5 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2e91b99f/core/src/main/java/org/apache/brooklyn/util/core/osgi/BundleMaker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/osgi/BundleMaker.java b/core/src/main/java/org/apache/brooklyn/util/core/osgi/BundleMaker.java
index 1cdcff8..35135a0 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/osgi/BundleMaker.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/osgi/BundleMaker.java
@@ -36,7 +36,7 @@ import java.util.zip.ZipOutputStream;
 import javax.annotation.Nonnull;
 
 import org.apache.brooklyn.api.mgmt.ManagementContext;
-import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.core.ResourceUtils;
@@ -73,7 +73,7 @@ public class BundleMaker {
     }
     
     public BundleMaker(@Nonnull ManagementContext mgmt) {
-        this( ((LocalManagementContext)mgmt).getOsgiManager().get().getFramework(), ResourceUtils.create() );
+        this(((ManagementContextInternal) mgmt).getOsgiManager().get().getFramework(), ResourceUtils.create());
     }
 
     /** if set, this will be used to resolve relative classpath fragments;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2e91b99f/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 e00cad8..ed4885f 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
@@ -50,7 +50,6 @@ import org.apache.brooklyn.api.typereg.RegisteredType;
 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;
@@ -184,7 +183,7 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
                 Entitlements.getEntitlementContext().user());
         }
 
-        BundleMaker bm = new BundleMaker(mgmt());
+        BundleMaker bm = new BundleMaker(mgmtInternal());
         File f=null, f2=null;
         try {
             f = Os.newTempFile("brooklyn-posted-archive", "zip");
@@ -248,6 +247,8 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
                 // if the above feature is not enabled, let's do it manually (as a contract of this method)
                 try {
                     // TODO improve on this - it ignores the configuration of whitelists, see CatalogBomScanner.
+                    // One way would be to add the CatalogBomScanner to the new Scratchpad area, then retrieving the singleton
+                    // here to get back the predicate from it.
                     final Predicate<Bundle> applicationsPermitted = Predicates.<Bundle>alwaysTrue();
 
                     new CatalogBundleLoader(applicationsPermitted, mgmt()).scanForCatalog(bundle);


[02/12] brooklyn-server git commit: Require bundle and version fields when uploading an archive through the REST API

Posted by ge...@apache.org.
Require bundle and version fields when uploading an archive through the REST API


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

Branch: refs/heads/master
Commit: 2ed7543170c41790ee1fb404bfc3f2853303d102
Parents: 48beb5c
Author: Thomas Bouron <th...@cloudsoftcorp.com>
Authored: Thu Mar 23 16:04:43 2017 +0000
Committer: Thomas Bouron <th...@cloudsoftcorp.com>
Committed: Thu Mar 23 17:21:02 2017 +0000

----------------------------------------------------------------------
 .../catalog/internal/BasicBrooklynCatalog.java  | 39 +++-----------------
 1 file changed, 6 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2ed75431/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
index 6e2a802..2634ecf 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
@@ -425,45 +425,18 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     }
     
     public static VersionedName getVersionedName(Map<?,?> catalogMetadata) {
-        String id = getFirstAs(catalogMetadata, String.class, "id").orNull();
         String version = getFirstAs(catalogMetadata, String.class, "version").orNull();
-        String symbolicName = getFirstAs(catalogMetadata, String.class, "symbolicName").orNull();
-        symbolicName = findAssertingConsistentIfSet("symbolicName", symbolicName, getFirstAs(catalogMetadata, String.class, "symbolic-name").orNull());
-        // prefer symbolic name and version, if supplied
-        // else use id as symbolic name : version or just symbolic name
-        // (must match if both supplied)
-        if (Strings.isNonBlank(id)) {
-            if (CatalogUtils.looksLikeVersionedId(id)) {
-                symbolicName = findAssertingConsistentIfSet("symbolicName", symbolicName, CatalogUtils.getSymbolicNameFromVersionedId(id));
-                version = findAssertingConsistentIfSet("version", version, CatalogUtils.getVersionFromVersionedId(id));
-            } else {
-                symbolicName = findAssertingConsistentIfSet("symbolicName", symbolicName, id);
-            }
-        }
-        if (Strings.isBlank(symbolicName) && Strings.isBlank(version)) {
-            throw new IllegalStateException("Catalog BOM must define symbolicName and version");
+        String bundle = getFirstAs(catalogMetadata, String.class, "bundle").orNull();
+        if (Strings.isBlank(bundle) && Strings.isBlank(version)) {
+            throw new IllegalStateException("Catalog BOM must define bundle and version");
         }
-        if (Strings.isBlank(symbolicName)) {
-            throw new IllegalStateException("Catalog BOM must define symbolicName");
+        if (Strings.isBlank(bundle)) {
+            throw new IllegalStateException("Catalog BOM must define bundle");
         }
         if (Strings.isBlank(version)) {
             throw new IllegalStateException("Catalog BOM must define version");
         }
-        return new VersionedName(symbolicName, Version.valueOf(version));
-    }
-    
-    private static String findAssertingConsistentIfSet(String context, String ...values) {
-        String v = null;
-        for (String vi: values) {
-            if (Strings.isNonBlank(vi)) {
-                if (Strings.isNonBlank(v)) {
-                    if (!v.equals(vi)) throw new IllegalStateException("Mismatch in "+context+": '"+v+"' or '"+vi+"' supplied");
-                } else {
-                    v = vi;
-                }
-            }
-        }
-        return v;
+        return new VersionedName(bundle, Version.valueOf(version));
     }
 
     private void collectCatalogItems(String yaml, List<CatalogItemDtoAbstract<?, ?>> result, Map<?, ?> parentMeta) {


[08/12] brooklyn-server git commit: Update swagger maven plugin configuration to work with older version

Posted by ge...@apache.org.
Update swagger maven plugin configuration to work with older version


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

Branch: refs/heads/master
Commit: 5fb40ff8ae077da061aee927a773ec7a232df97d
Parents: f86eb87
Author: Thomas Bouron <th...@cloudsoftcorp.com>
Authored: Fri Mar 24 17:02:16 2017 +0000
Committer: Thomas Bouron <th...@cloudsoftcorp.com>
Committed: Fri Mar 24 17:02:16 2017 +0000

----------------------------------------------------------------------
 rest/rest-api/pom.xml | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/5fb40ff8/rest/rest-api/pom.xml
----------------------------------------------------------------------
diff --git a/rest/rest-api/pom.xml b/rest/rest-api/pom.xml
index 0c2c690..179231d 100644
--- a/rest/rest-api/pom.xml
+++ b/rest/rest-api/pom.xml
@@ -118,8 +118,13 @@
                     <apiSources>
                         <apiSource>
                             <springmvc>false</springmvc>
-                            <locations>org.apache.brooklyn.rest.api</locations>
-                            <schemes>http,https</schemes>
+                            <locations>
+                                <location>org.apache.brooklyn.rest.api</location>
+                            </locations>
+                            <schemes>
+                                <scheme>http</scheme>
+                                <scheme>https</scheme>
+                            </schemes>
                             <info>
                                 <title>Swagger API Specification for Brooklyn REST server</title>
                                 <version>v1</version>


[07/12] brooklyn-server git commit: Fix brooklyn-rest-client tests: must annotate all API methods, even deprecated ones (annotation was removed by #485)

Posted by ge...@apache.org.
Fix brooklyn-rest-client tests: must annotate all API methods, even deprecated ones (annotation was removed by #485)


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

Branch: refs/heads/master
Commit: f86eb8744be849b692dea2e9445712629d7349a9
Parents: c93b05e
Author: Thomas Bouron <th...@cloudsoftcorp.com>
Authored: Fri Mar 24 12:33:37 2017 +0000
Committer: Thomas Bouron <th...@cloudsoftcorp.com>
Committed: Fri Mar 24 12:33:37 2017 +0000

----------------------------------------------------------------------
 .../java/org/apache/brooklyn/rest/api/CatalogApi.java | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/f86eb874/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
index dcfaa78..a95aa9f 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/CatalogApi.java
@@ -52,7 +52,19 @@ import io.swagger.annotations.ApiResponses;
 @Produces(MediaType.APPLICATION_JSON)
 public interface CatalogApi {
 
-    @Deprecated /** @deprecated since 0.11.0 use {@link #createFromYaml(String)} instead */
+    /** @deprecated since 0.11.0 use {@link #createFromYaml(String)} instead */
+    @Deprecated
+    @POST
+    @ApiOperation(
+            value = "Add a catalog items (e.g. new type of entity, policy or location) by uploading YAML descriptor.",
+            notes = "Return value is map of ID to CatalogItemSummary.",
+            response = String.class,
+            hidden = true
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 400, message = "Error processing the given YAML"),
+            @ApiResponse(code = 201, message = "Catalog items added successfully")
+    })
     public Response create(String yaml);
 
     @POST