You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2017/09/25 09:29:32 UTC

[01/11] brooklyn-server git commit: REST API for bundles, types, and subtypes

Repository: brooklyn-server
Updated Branches:
  refs/heads/master 3ac961dbc -> 05ae90a71


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeAndSubtypeResourcesTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeAndSubtypeResourcesTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeAndSubtypeResourcesTest.java
new file mode 100644
index 0000000..03578f1
--- /dev/null
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeAndSubtypeResourcesTest.java
@@ -0,0 +1,1093 @@
+/*
+ * 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.rest.resources;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+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.policy.Policy;
+import org.apache.brooklyn.api.typereg.ManagedBundle;
+import org.apache.brooklyn.api.typereg.OsgiBundleWithUrl;
+import org.apache.brooklyn.api.typereg.RegisteredType;
+import org.apache.brooklyn.core.entity.EntityPredicates;
+import org.apache.brooklyn.core.mgmt.ha.OsgiManager;
+import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.mgmt.osgi.OsgiStandaloneTest;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.enricher.stock.Aggregator;
+import org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy;
+import org.apache.brooklyn.rest.domain.BundleInstallationRestResult;
+import org.apache.brooklyn.rest.domain.BundleSummary;
+import org.apache.brooklyn.rest.domain.TypeDetail;
+import org.apache.brooklyn.rest.domain.TypeSummary;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.test.support.TestResourceUnavailableException;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.core.ResourceUtils;
+import org.apache.brooklyn.util.core.osgi.BundleMaker;
+import org.apache.brooklyn.util.javalang.JavaClassNames;
+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.HttpHeaders;
+import org.eclipse.jetty.http.HttpStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+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;
+
+public class BundleAndTypeAndSubtypeResourcesTest extends BrooklynRestResourceTest {
+
+    private static final Logger log = LoggerFactory.getLogger(BundleAndTypeAndSubtypeResourcesTest.class);
+    
+    private static String TEST_VERSION = "0.1.2";
+    private static String TEST_LASTEST_VERSION = "0.1.3";
+
+    private Collection<ManagedBundle> initialBundles;
+
+    @Override
+    protected boolean useLocalScannedCatalog() {
+        return true;
+    }
+
+    @Override
+    protected void initClass() throws Exception {
+        super.initClass();
+        // cache initially installed bundles
+        OsgiManager osgi = ((ManagementContextInternal)getManagementContext()).getOsgiManager().get();
+        initialBundles = osgi.getManagedBundles().values();
+    }
+    
+    protected void initMethod() throws Exception {
+        super.initMethod();
+        
+        // and reset OSGi container
+        OsgiManager osgi = ((ManagementContextInternal)getManagementContext()).getOsgiManager().get();
+        for (ManagedBundle b: osgi.getManagedBundles().values()) {
+            if (!initialBundles.contains(b)) {
+                osgi.uninstallUploadedBundle(b);
+            }
+        }
+    }
+    
+    @Test
+    /** based on CampYamlLiteTest */
+    public void testRegisterCustomEntityTopLevelSyntaxWithBundleWhereEntityIsFromCoreAndIconFromBundle() {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+
+        String symbolicName = "my.catalog.entity.id";
+        String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: entity",
+                "  name: My Catalog App",
+                "  description: My description",
+                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  libraries:",
+                "  - url: " + bundleUrl,
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
+
+        Response response = client().path("/bundles")
+                .post(yaml);
+
+        assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
+        BundleInstallationRestResult installed = response.readEntity(BundleInstallationRestResult.class);
+        Asserts.assertSize(installed.getTypes().values(), 1);
+        TypeSummary installedItem = installed.getTypes().get(symbolicName+":"+TEST_VERSION);
+        Assert.assertNotNull(installedItem, ""+installed.getTypes());
+
+        TypeDetail entityItem = client().path("/types/"+symbolicName + "/" + TEST_VERSION)
+                .get(TypeDetail.class);
+
+        Assert.assertEquals(new TypeSummary(entityItem), installedItem);
+        Assert.assertNotNull(entityItem.getPlan());
+        Assert.assertTrue(((String)entityItem.getPlan().getData()).contains("org.apache.brooklyn.core.test.entity.TestEntity"));
+
+        assertEquals(entityItem.getSymbolicName(), symbolicName);
+        assertEquals(entityItem.getVersion(), TEST_VERSION);
+
+        // also check it's included in various lists
+        List<TypeSummary> list1 = client().path("/types/"+symbolicName).get(new GenericType<List<TypeSummary>>() {});
+        assertEquals(list1, MutableList.of(installedItem));
+        List<TypeSummary> list2 = client().path("/types").get(new GenericType<List<TypeSummary>>() {});
+        Assert.assertTrue(list2.contains(installedItem), ""+list2);
+        List<TypeSummary> list3 = client().path("/subtypes/entity").get(new GenericType<List<TypeSummary>>() {});
+        Assert.assertTrue(list3.contains(installedItem), ""+list3);
+        
+        // and internally let's check we have libraries
+        RegisteredType item = getManagementContext().getTypeRegistry().get(symbolicName, TEST_VERSION);
+        Assert.assertNotNull(item);
+        Collection<OsgiBundleWithUrl> libs = item.getLibraries();
+        assertEquals(libs.size(), 1);
+        assertEquals(Iterables.getOnlyElement(libs).getUrl(), bundleUrl);
+
+        // now let's check other things on the item
+        URI expectedIconUrl = URI.create(getEndpointAddress() + "/types/" + symbolicName + "/" + entityItem.getVersion()+"/icon").normalize();
+        assertEquals(entityItem.getDisplayName(), "My Catalog App");
+        assertEquals(entityItem.getDescription(), "My description");
+        assertEquals(entityItem.getIconUrl(), expectedIconUrl.getPath());
+        assertEquals(item.getIconUrl(), "classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif");
+
+        // an InterfacesTag should be created for every catalog item
+        if (checkTraits(false)) {
+            @SuppressWarnings("unchecked")
+            Map<String, List<String>> traitsMapTag = Iterables.getOnlyElement(Iterables.filter(entityItem.getTags(), Map.class));
+            List<String> actualInterfaces = traitsMapTag.get("traits");
+            List<Class<?>> expectedInterfaces = Reflections.getAllInterfaces(TestEntity.class);
+            assertEquals(actualInterfaces.size(), expectedInterfaces.size());
+            for (Class<?> expectedInterface : expectedInterfaces) {
+                assertTrue(actualInterfaces.contains(expectedInterface.getName()));
+            }
+        }
+
+        byte[] iconData = client().path("/types/" + symbolicName + "/" + TEST_VERSION+"/icon").get(byte[].class);
+        assertEquals(iconData.length, 43);
+    }
+
+    @Test
+    public void testRegisterOsgiPolicyTopLevelSyntax() {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+
+        String symbolicName = "my.catalog.entity.id."+JavaClassNames.niceClassAndMethod();
+        String policyType = "org.apache.brooklyn.test.osgi.entities.SimplePolicy";
+        String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
+
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: policy",
+                "  name: My Catalog App",
+                "  description: My description",
+                "  libraries:",
+                "  - url: " + bundleUrl,
+                "  item:",
+                "    type: " + policyType);
+
+        TypeSummary installedItem = Iterables.getOnlyElement( client().path("/bundles")
+                .post(yaml, BundleInstallationRestResult.class).getTypes().values() );
+
+        assertEquals(installedItem.getSymbolicName(), symbolicName);
+        assertEquals(installedItem.getVersion(), TEST_VERSION);
+        Assert.assertTrue(installedItem.getSupertypes().contains(Policy.class.getName()), ""+installedItem.getSupertypes());
+    }
+
+    @Test
+    public void testFilterListOfEntitiesByName() {
+        List<TypeSummary> entities = client().path("/types")
+                .query("fragment", "vaNIllasOFTWAREpROCESS").get(new GenericType<List<TypeSummary>>() {});
+        log.info("Matching entities: " + entities);
+        assertEquals(entities.size(), 1);
+
+        entities = client().path("/subtypes/entity")
+                .query("fragment", "vaNIllasOFTWAREpROCESS").get(new GenericType<List<TypeSummary>>() {});
+        log.info("Matching entities: " + entities);
+        assertEquals(entities.size(), 1);
+
+        List<TypeSummary> entities2 = client().path("/types")
+                .query("regex", "[Vv]an.[alS]+oftware\\w+").get(new GenericType<List<TypeSummary>>() {});
+        assertEquals(entities2.size(), 1);
+
+        assertEquals(entities, entities2);
+    
+        entities = client().path("/subtypes/entity")
+                .query("fragment", "bweqQzZ").get(new GenericType<List<TypeSummary>>() {});
+        Asserts.assertSize(entities, 0);
+
+        entities = client().path("/subtypes/entity")
+                .query("regex", "bweq+z+").get(new GenericType<List<TypeSummary>>() {});
+        Asserts.assertSize(entities, 0);
+    }
+
+    @Test
+    public void testGetCatalogEntityIconDetails() throws IOException {
+        String catalogItemId = "testGetCatalogEntityIconDetails";
+        addTestCatalogItemAsEntity(catalogItemId);
+        Response response = client().path(URI.create("/types/" + catalogItemId + "/" + TEST_VERSION + "/icon"))
+                .get();
+        response.bufferEntity();
+        Assert.assertEquals(response.getStatus(), 200);
+        Assert.assertEquals(response.getMediaType(), MediaType.valueOf("image/png"));
+        Image image = Toolkit.getDefaultToolkit().createImage(Files.readFile(response.readEntity(InputStream.class)));
+        Assert.assertNotNull(image);
+    }
+
+    private void addTestCatalogItemAsEntity(String catalogItemId) {
+        addTestCatalogItem(catalogItemId, "entity", TEST_VERSION, "org.apache.brooklyn.rest.resources.DummyIconEntity");
+    }
+
+    private void addTestCatalogItem(String catalogItemId, String itemType, String version, String service) {
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + catalogItemId,
+                "  version: " + TEST_VERSION,
+                "  itemType: " + checkNotNull(itemType),
+                "  name: My Catalog App",
+                "  description: My description",
+                "  icon_url: classpath:///bridge-small.png",
+                "  version: " + version,
+                "  item:",
+                "    type: " + service);
+
+        client().path("/bundles").post(yaml);
+    }
+
+    @Test
+    public void testListPolicies() {
+        Set<TypeSummary> policies = client().path("/subtypes/policy")
+                .get(new GenericType<Set<TypeSummary>>() {});
+
+        assertTrue(policies.size() > 0);
+        TypeSummary asp = null;
+        for (TypeSummary p : policies) {
+            if (AutoScalerPolicy.class.getName().equals(p.getSymbolicName()))
+                asp = p;
+        }
+        Assert.assertNotNull(asp, "didn't find AutoScalerPolicy");
+    }
+
+    @Test
+    public void testLocationAddGetAndRemove() {
+        String symbolicName = "my.catalog.location.id";
+        String locationType = "localhost";
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: location",
+                "  name: My Catalog Location",
+                "  description: My description",
+                "  item:",
+                "    type: " + locationType);
+
+        // Create location item
+        Map<String, TypeSummary> items = client().path("/bundles")
+                .post(yaml, BundleInstallationRestResult.class).getTypes();
+        TypeSummary locationItem = Iterables.getOnlyElement(items.values());
+
+        assertEquals(locationItem.getSymbolicName(), symbolicName);
+        assertEquals(locationItem.getVersion(), TEST_VERSION);
+
+        // Retrieve location item
+        TypeDetail location = client().path("/types/"+symbolicName+"/"+TEST_VERSION).get(TypeDetail.class);
+        assertEquals(location.getSymbolicName(), symbolicName);
+
+        // Retrieve all locations
+        Set<TypeSummary> locations = client().path("/subtypes/location")
+                .get(new GenericType<Set<TypeSummary>>() {});
+        boolean found = false;
+        for (TypeSummary contender : locations) {
+            if (contender.getSymbolicName().equals(symbolicName)) {
+                found = true;
+                break;
+            }
+        }
+        Assert.assertTrue(found, "contenders="+locations);
+        
+        // Delete
+        Response deleteResponse = client().path("/bundles/"+locationItem.getContainingBundle().replaceAll(":", "/"))
+                .delete();
+        assertEquals(deleteResponse.getStatus(), Response.Status.OK.getStatusCode());
+        BundleInstallationRestResult deletionResponse = deleteResponse.readEntity(BundleInstallationRestResult.class);
+        Assert.assertEquals(deletionResponse.getBundle(), symbolicName+":"+TEST_VERSION);
+        Assert.assertEquals(deletionResponse.getTypes().keySet(), MutableSet.of(symbolicName+":"+TEST_VERSION));
+
+        Response getPostDeleteResponse = client().path("/types/"+symbolicName+"/"+TEST_VERSION)
+                .get();
+        assertEquals(getPostDeleteResponse.getStatus(), Response.Status.NOT_FOUND.getStatusCode());
+    }
+
+    @Test
+    public void testListEnrichers() {
+        Set<TypeSummary> enrichers = client().path("/subtypes/enricher")
+                .get(new GenericType<Set<TypeSummary>>() {});
+
+        assertTrue(enrichers.size() > 0);
+        TypeSummary asp = null;
+        for (TypeSummary p : enrichers) {
+            if (Aggregator.class.getName().equals(p.getSymbolicName()))
+                asp = p;
+        }
+        Assert.assertNotNull(asp, "didn't find Aggregator");
+    }
+
+    @Test
+    public void testEnricherAddGet() {
+        String symbolicName = "my.catalog.enricher.id";
+        String enricherType = "org.apache.brooklyn.enricher.stock.Aggregator";
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: enricher",
+                "  name: My Catalog Enricher",
+                "  description: My description",
+                "  item:",
+                "    type: " + enricherType);
+
+        // Create location item
+        Map<String, TypeSummary> items = client().path("/bundles")
+                .post(yaml, BundleInstallationRestResult.class).getTypes();
+        TypeSummary enricherItem = Iterables.getOnlyElement(items.values());
+
+        assertEquals(enricherItem.getSymbolicName(), symbolicName);
+        assertEquals(enricherItem.getVersion(), TEST_VERSION);
+
+        // Retrieve location item
+        TypeSummary enricher = client().path("/types/"+symbolicName+"/"+TEST_VERSION)
+                .get(TypeSummary.class);
+        assertEquals(enricher.getSymbolicName(), symbolicName);
+
+        // Retrieve all locations
+        Set<TypeSummary> enrichers = client().path("/subtypes/enricher")
+                .get(new GenericType<Set<TypeSummary>>() {});
+        boolean found = false;
+        for (TypeSummary contender : enrichers) {
+            if (contender.getSymbolicName().equals(symbolicName)) {
+                found = true;
+                break;
+            }
+        }
+        Assert.assertTrue(found, "contenders="+enrichers);
+    }
+
+    @Test
+    // osgi may fail in IDE, typically works on mvn CLI though
+    public void testRegisterOsgiEnricherTopLevelSyntax() {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+
+        String symbolicName = "my.catalog.enricher.id";
+        String enricherType = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENRICHER;
+        String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
+
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: enricher",
+                "  name: My Catalog Enricher",
+                "  description: My description",
+                "  libraries:",
+                "  - url: " + bundleUrl,
+                "  item:",
+                "    type: " + enricherType);
+
+        TypeSummary installedItem = Iterables.getOnlyElement( client().path("/bundles")
+                .post(yaml, BundleInstallationRestResult.class).getTypes().values() );
+
+        assertEquals(installedItem.getSymbolicName(), symbolicName);
+        assertEquals(installedItem.getVersion(), TEST_VERSION);
+    }
+
+    @Test
+    public void testDeleteCustomEntityFromCatalog() {
+        String symbolicName = "my.catalog.app.id.to.subsequently.delete";
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: entity",
+                "  name: My Catalog App To Be Deleted",
+                "  description: My description",
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
+
+        client().path("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/yaml")
+                .post(yaml);
+
+        BundleSummary getInstalledBundle = client().path("/bundles/"+symbolicName+"/"+TEST_VERSION)
+            .get(BundleSummary.class);
+        assertEquals(getInstalledBundle.getSymbolicName(), symbolicName);
+        assertEquals(getInstalledBundle.getVersion(), TEST_VERSION);
+        Asserts.assertNotNull(getInstalledBundle.getTypes(), "expected 'types' in: "+getInstalledBundle.getExtraFields());
+        Asserts.assertStringContains(""+getInstalledBundle.getTypes(), "My Catalog App");
+
+        Response deleteResponse = client().path("/bundles/"+symbolicName+"/"+TEST_VERSION)
+                .delete();
+
+        assertEquals(deleteResponse.getStatus(), Response.Status.OK.getStatusCode());
+        // contents of delete tested in delete location method
+
+        Response getPostDeleteResponse = client().path("/bundles/"+symbolicName+"/"+TEST_VERSION)
+                .get();
+        assertEquals(getPostDeleteResponse.getStatus(), Response.Status.NOT_FOUND.getStatusCode());
+    }
+
+    private void addCatalogItemWithInvalidBundleUrl(String bundleUrl) {
+        String symbolicName = "my.catalog.entity.id";
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: entity",
+                "  name: My Catalog App",
+                "  description: My description",
+                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  libraries:",
+                "  - url: " + bundleUrl,
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
+
+        Response response = client().path("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-yaml")
+                .post(yaml);
+
+        assertEquals(response.getStatus(), HttpStatus.BAD_REQUEST_400);
+    }
+    
+    @Test
+    public void testAddUnreachableItem() {
+        addCatalogItemWithInvalidBundleUrl("http://0.0.0.0/can-not-connect");
+    }
+
+    @Test
+    public void testAddInvalidItem() {
+        //equivalent to HTTP response 200 text/html
+        addCatalogItemWithInvalidBundleUrl("classpath://not-a-jar-file.txt");
+    }
+
+    @Test
+    public void testAddMissingItem() {
+        //equivalent to HTTP response 404 text/html
+        addCatalogItemWithInvalidBundleUrl("classpath://missing-jar-file.txt");
+    }
+
+    @Test
+    public void testInvalidArchive() throws Exception {
+        File f = Os.newTempFile("osgi", "zip");
+
+        Response response = client().path("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), "zip file is empty");
+    }
+
+    @Test
+    public void testArchiveWithoutBom() throws Exception {
+        File f = createZip(ImmutableMap.<String, String>of());
+
+        Response response = client().path("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), "Missing bundle symbolic name in BOM or MANIFEST");
+    }
+
+    @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("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), "Missing bundle symbolic name in BOM or MANIFEST");
+    }
+
+    @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("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), 
+            "Missing bundle symbolic name in BOM or MANIFEST");
+    }
+
+    @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("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), "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";
+        String wrongBundleName = "org.apache.brooklyn.test2";
+        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: "+wrongBundleName,
+                        "Bundle-Version: " + version,
+                        "Bundle-ManifestVersion: " + version)));
+
+        Response response = client().path("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), 
+            "symbolic name mismatch",
+            wrongBundleName, bundle);
+    }
+
+    @Test
+    public void testJarWithoutMatchingVersion() throws Exception {
+        String name = "My Catalog App";
+        String bundle = "org.apache.brooklyn.test";
+        String version = "0.1.0";
+        String wrongVersion = "0.3.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: " + wrongVersion,
+                        "Bundle-ManifestVersion: " + wrongVersion)));
+
+        Response response = client().path("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), 
+            "version mismatch",
+            wrongVersion, version);
+    }
+
+    @Test
+    public void testOsgiBundleWithBom() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+        final String symbolicName = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_SYMBOLIC_NAME_FULL;
+        final String version = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_VERSION;
+        final String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
+        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:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
+        
+        f = bm.copyAdding(f, MutableMap.of(new ZipEntry("catalog.bom"), (InputStream) new ByteArrayInputStream(bom.getBytes())));
+
+        Response response = client().path("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
+                .post(Streams.readFully(new FileInputStream(f)));
+        
+        assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
+
+        TypeSummary entityItem = client().path("/types/"+symbolicName + "/" + version)
+                .get(TypeSummary.class);
+
+        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(), "org.apache.brooklyn.test.resources.osgi.brooklyn-test-osgi-entities");
+        assertEquals(lib.getSuppliedVersionString(), version);
+
+        // now let's check other things on the item
+        URI expectedIconUrl = URI.create(getEndpointAddress() + "/types/" + symbolicName + "/" + entityItem.getVersion()+"/icon").normalize();
+        assertEquals(entityItem.getDisplayName(), "My Catalog App");
+        assertEquals(entityItem.getDescription(), "My description");
+        assertEquals(entityItem.getIconUrl(), expectedIconUrl.getPath());
+        assertEquals(item.getIconUrl(), "classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif");
+
+        if (checkTraits(false)) {
+            // an InterfacesTag should be created for every catalog item
+            @SuppressWarnings("unchecked")
+            Map<String, List<String>> traitsMapTag = Iterables.getOnlyElement(Iterables.filter(entityItem.getTags(), Map.class));
+            List<String> actualInterfaces = traitsMapTag.get("traits");
+            List<Class<?>> expectedInterfaces = Reflections.getAllInterfaces(TestEntity.class);
+            assertEquals(actualInterfaces.size(), expectedInterfaces.size());
+            for (Class<?> expectedInterface : expectedInterfaces) {
+                assertTrue(actualInterfaces.contains(expectedInterface.getName()));
+            }
+        }
+
+        byte[] iconData = client().path("/types/" + symbolicName + "/" + version + "/icon").get(byte[].class);
+        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("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+
+        assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
+
+        TypeDetail entityItem = client().path("/types/"+symbolicName + "/" + version)
+                .get(TypeDetail.class);
+
+        Assert.assertNotNull(entityItem.getPlan().getData());
+        Assert.assertTrue(entityItem.getPlan().getData().toString().contains(entityType));
+
+        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.getSuppliedVersionString(), version);
+
+        // now let's check other things on the item
+        assertEquals(entityItem.getDescription(), "My description");
+        URI expectedIconUrl = URI.create(getEndpointAddress() + "/types/" + symbolicName + "/" + entityItem.getVersion() + "/icon").normalize();
+        assertEquals(entityItem.getIconUrl(), expectedIconUrl.getPath());
+        assertEquals(item.getIconUrl(), "classpath:" + iconPath);
+
+        if (checkTraits(false)) {
+            // an InterfacesTag should be created for every catalog item
+            @SuppressWarnings("unchecked")
+            Map<String, List<String>> traitsMapTag = Iterables.getOnlyElement(Iterables.filter(entityItem.getTags(), Map.class));
+            List<String> actualInterfaces = traitsMapTag.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("/types/" + symbolicName + "/" + version + "/icon").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 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;
+    }
+
+    @Test
+    public void testGetOnlyLatestApplication() {
+        String symbolicName = "latest.catalog.application.id";
+        String itemType = "template";
+        String serviceType = "org.apache.brooklyn.core.test.entity.TestEntity";
+
+        addTestCatalogItem(symbolicName, itemType, TEST_VERSION, serviceType);
+        addTestCatalogItem(symbolicName, itemType, TEST_LASTEST_VERSION, serviceType);
+
+        TypeSummary application = client().path("/types/" + symbolicName + "/latest")
+                .get(TypeSummary.class);
+        assertEquals(application.getVersion(), TEST_LASTEST_VERSION);
+    }
+
+    @Test
+    public void testGetOnlyLatestDifferentCases() {
+        // depends on installation of this
+        testGetOnlyLatestApplication();
+        
+        String symbolicName = "latest.catalog.application.id";
+
+        TypeSummary application = client().path("/types/" + symbolicName + "/LaTeSt")
+                .get(TypeSummary.class);
+        assertEquals(application.getVersion(), TEST_LASTEST_VERSION);
+
+        application = client().path("/types/" + symbolicName + "/LATEST")
+                .get(TypeSummary.class);
+        assertEquals(application.getVersion(), TEST_LASTEST_VERSION);
+    }
+
+    @Test
+    public void testGetOnlyLatestEntity() {
+        String symbolicName = "latest.catalog.entity.id";
+        String itemType = "entity";
+        String serviceType = "org.apache.brooklyn.core.test.entity.TestEntity";
+
+        addTestCatalogItem(symbolicName, itemType, TEST_VERSION, serviceType);
+        addTestCatalogItem(symbolicName, itemType, TEST_LASTEST_VERSION, serviceType);
+
+        TypeSummary application = client().path("/types/" + symbolicName + "/latest")
+                .get(TypeSummary.class);
+        assertEquals(application.getVersion(), TEST_LASTEST_VERSION);
+    }
+
+    @Test
+    public void testGetOnlyLatestLocation() {
+        String symbolicName = "latest.catalog.location.id";
+        String itemType = "location";
+        String serviceType = "localhost";
+
+        addTestCatalogItem(symbolicName, itemType, TEST_VERSION, serviceType);
+        addTestCatalogItem(symbolicName, itemType, TEST_LASTEST_VERSION, serviceType);
+
+        TypeSummary application = client().path("/types/" + symbolicName + "/latest")
+                .get(TypeSummary.class);
+        assertEquals(application.getVersion(), TEST_LASTEST_VERSION);
+    }
+
+
+    @Test
+    public void testForceUpdateForYAML() {
+        String symbolicName = "force.update.catalog.application.id";
+        String itemType = "template";
+        String initialName = "My Catalog App";
+        String initialDescription = "My description";
+        String updatedName = initialName + " 2";
+        String updatedDescription = initialDescription + " 2";
+
+        String initialYaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: " + itemType,
+                "  name: " + initialName,
+                "  description: " + initialDescription,
+                "  icon_url: classpath:///bridge-small.png",
+                "  version: " + TEST_VERSION,
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
+        String updatedYaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: " + itemType,
+                "  name: " + updatedName,
+                "  description: " + updatedDescription,
+                "  icon_url: classpath:///bridge-small.png",
+                "  version: " + TEST_VERSION,
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
+
+        client().path("/bundles").post(initialYaml);
+
+        TypeDetail initialApplication = client().path("/types/" + symbolicName + "/" + TEST_VERSION)
+                .get(TypeDetail.class);
+        assertEquals(initialApplication.getDisplayName(), initialName);
+        assertEquals(initialApplication.getDescription(), initialDescription);
+
+        Response invalidResponse = client().path("/bundles").post(updatedYaml);
+
+        assertEquals(invalidResponse.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+
+        Response validResponse = client().path("/bundles").query("force", true).post(updatedYaml);
+
+        assertEquals(validResponse.getStatus(), Response.Status.CREATED.getStatusCode());
+
+        TypeSummary application = client().path("/types/" + symbolicName + "/" + TEST_VERSION)
+                .get(TypeSummary.class);
+        assertEquals(application.getDisplayName(), updatedName);
+        assertEquals(application.getDescription(), updatedDescription);
+    }
+
+    @Test
+    public void testForceUpdateForZip() throws Exception {
+        final String symbolicName = "force.update.zip.catalog.application.id";
+        final String initialName = "My Catalog App";
+        final String initialDescription = "My Description";
+        final String updatedName = initialName + " 2";
+        final String updatedDescription = initialDescription  +" 2";
+
+        File initialZip = createZip(ImmutableMap.<String, String>of("catalog.bom", Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  bundle: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  id: " + symbolicName,
+                "  itemType: entity",
+                "  name: " + initialName,
+                "  description: " + initialDescription,
+                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity")));
+        File updatedZip = createZip(ImmutableMap.<String, String>of("catalog.bom", Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  bundle: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  id: " + symbolicName,
+                "  itemType: entity",
+                "  name: " + updatedName,
+                "  description: " + updatedDescription,
+                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity")));
+
+        client().path("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(initialZip)));
+
+        TypeSummary initialEntity = client().path("/types/" + symbolicName + "/" + TEST_VERSION)
+                .get(TypeSummary.class);
+        assertEquals(initialEntity.getDisplayName(), initialName);
+        assertEquals(initialEntity.getDescription(), initialDescription);
+
+        Response invalidResponse = client().path("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(updatedZip)));
+
+        assertEquals(invalidResponse.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+
+        Response validResponse = client().path("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .query("force", true)
+                .post(Streams.readFully(new FileInputStream(updatedZip)));
+
+        assertEquals(validResponse.getStatus(), Response.Status.CREATED.getStatusCode());
+
+        TypeSummary entity = client().path("/types/" + symbolicName + "/" + TEST_VERSION)
+                .get(TypeSummary.class);
+        assertEquals(entity.getDisplayName(), updatedName);
+        assertEquals(entity.getDescription(), updatedDescription);
+    }
+
+    @Test
+    public void testForceUpdateForJar() throws Exception {
+        final String symbolicName = "force.update.jar.catalog.application.id";
+        final String initialName = "My Catalog App";
+        final String initialDescription = "My Description";
+        final String updatedName = initialName + " 2";
+        final String updatedDescription = initialDescription  +" 2";
+
+        File initialJar = createJar(ImmutableMap.<String, String>of("catalog.bom", Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  bundle: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  id: " + symbolicName,
+                "  itemType: entity",
+                "  name: " + initialName,
+                "  description: " + initialDescription,
+                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity")));
+        File updatedJar = createJar(ImmutableMap.<String, String>of("catalog.bom", Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  bundle: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  id: " + symbolicName,
+                "  itemType: entity",
+                "  name: " + updatedName,
+                "  description: " + updatedDescription,
+                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity")));
+
+        client().path("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
+                .post(Streams.readFully(new FileInputStream(initialJar)));
+
+        TypeSummary initialEntity = client().path("/types/" + symbolicName + "/" + TEST_VERSION)
+                .get(TypeSummary.class);
+        assertEquals(initialEntity.getDisplayName(), initialName);
+        assertEquals(initialEntity.getDescription(), initialDescription);
+
+        Response invalidResponse = client().path("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
+                .post(Streams.readFully(new FileInputStream(updatedJar)));
+
+        assertEquals(invalidResponse.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+
+        Response validResponse = client().path("/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
+                .query("force", true)
+                .post(Streams.readFully(new FileInputStream(updatedJar)));
+
+        assertEquals(validResponse.getStatus(), Response.Status.CREATED.getStatusCode());
+
+        TypeSummary entity = client().path("/types/" + symbolicName + "/" + TEST_VERSION)
+                .get(TypeSummary.class);
+        assertEquals(entity.getDisplayName(), updatedName);
+        assertEquals(entity.getDescription(), updatedDescription);
+    }
+    
+    // TODO traits no longer always set - we have supertypes so not needed, we should investigate when they are and when they aren't
+    // and switch those to setting and using the supertypes
+    private boolean checkTraits(boolean currentExpectedToBeWorking) {
+        return currentExpectedToBeWorking;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/utils/common/src/main/java/org/apache/brooklyn/util/osgi/VersionedName.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/osgi/VersionedName.java b/utils/common/src/main/java/org/apache/brooklyn/util/osgi/VersionedName.java
index 9fec03c..9734153 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/osgi/VersionedName.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/osgi/VersionedName.java
@@ -17,19 +17,24 @@ package org.apache.brooklyn.util.osgi;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.util.Comparator;
+
 import javax.annotation.Nullable;
 
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.text.BrooklynVersionSyntax;
+import org.apache.brooklyn.util.text.NaturalOrderComparator;
 import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.text.VersionComparator;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.Version;
 
 import com.google.common.base.Objects;
+import com.google.common.collect.ComparisonChain;
 
 /** Records a name (string) and version (string),
  * with conveniences for pretty-printing and converting to OSGi format. */
-public class VersionedName {
+public class VersionedName implements Comparable<VersionedName> {
     private final String name;
     private final String v;
     
@@ -172,4 +177,21 @@ public class VersionedName {
         return Maybe.of(new VersionedName(parts[0], parts.length == 2 ? parts[1] : null));
     }
 
+    @Override
+    public int compareTo(VersionedName other) {
+        return VersionedNameComparator.INSTANCE.compare(this, other);
+    }
+
+    public static class VersionedNameComparator implements Comparator<VersionedName> {
+        public static final VersionedNameComparator INSTANCE = new VersionedNameComparator();
+        
+        @Override
+        public int compare(VersionedName o1, VersionedName o2) {
+            return ComparisonChain.start()
+                .compare(o1.getSymbolicName(), o2.getSymbolicName(), NaturalOrderComparator.INSTANCE)
+                .compare(o2.getOsgiVersionString(), o1.getOsgiVersionString(), VersionComparator.INSTANCE)
+                .compare(o2.getVersionString(), o1.getVersionString(), VersionComparator.INSTANCE)
+                .result();
+        }
+    }
 }


[06/11] brooklyn-server git commit: address most PR comments

Posted by he...@apache.org.
address most PR comments

still to do: supertypes as query param, and restructure to hang both of catalog (remove POST /catalog/bundles)


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

Branch: refs/heads/master
Commit: 206f744a84e93b364fbd912927ca6849a29873a3
Parents: 831db4a
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri Sep 22 11:42:02 2017 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Sep 22 11:42:02 2017 +0100

----------------------------------------------------------------------
 .../core/mgmt/entitlement/Entitlements.java     |  1 +
 .../org/apache/brooklyn/rest/api/BundleApi.java | 14 +++++++--
 .../org/apache/brooklyn/rest/api/TypeApi.java   |  7 +++--
 .../brooklyn/rest/domain/BundleSummary.java     |  7 +++++
 .../apache/brooklyn/rest/domain/TypeDetail.java |  3 +-
 .../brooklyn/rest/domain/TypeSummary.java       | 12 ++++++--
 .../brooklyn/rest/resources/BundleResource.java | 25 +++++++++-------
 .../brooklyn/rest/resources/TypeResource.java   |  2 ++
 .../rest/transform/TypeTransformer.java         | 31 +++++++++++---------
 9 files changed, 68 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/206f744a/core/src/main/java/org/apache/brooklyn/core/mgmt/entitlement/Entitlements.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/entitlement/Entitlements.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/entitlement/Entitlements.java
index 60d998e..f7b5f72 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/entitlement/Entitlements.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/entitlement/Entitlements.java
@@ -60,6 +60,7 @@ public class Entitlements {
     
     // ------------------- individual permissions
     
+    // TODO applies to bundles and registered types; should pass object or probably better add more entitlements?
     public static EntitlementClass<String> SEE_CATALOG_ITEM = new BasicEntitlementClassDefinition<String>("catalog.see", String.class); 
     public static EntitlementClass<Object> ADD_CATALOG_ITEM = new BasicEntitlementClassDefinition<Object>("catalog.add", Object.class); 
     public static EntitlementClass<StringAndArgument> MODIFY_CATALOG_ITEM = new BasicEntitlementClassDefinition<StringAndArgument>("catalog.modify", StringAndArgument.class); 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/206f744a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java
index 2c2eeae..6bf46ea 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java
@@ -54,9 +54,13 @@ public interface BundleApi {
             responseContainer = "List")
     public List<BundleSummary> list(
         @ApiParam(name = "versions", value = "Whether to list 'latest' for each symbolic-name or 'all' versions", 
-            required = false, defaultValue = "latest")
+        required = false, defaultValue = "latest")
         @QueryParam("versions")
-        String versions);
+        String versions,
+        @ApiParam(name = "detail", value = "Whether to include types and other detail info, default 'false'", 
+        required = false, defaultValue = "false")
+        @QueryParam("detail")
+        boolean detail);
 
     @Path("/{symbolicName}")
     @GET
@@ -66,7 +70,11 @@ public interface BundleApi {
     public List<BundleSummary> listVersions(
         @ApiParam(name = "symbolicName", value = "Bundle name to query", required = true)
         @PathParam("symbolicName")
-        String symbolicName);
+        String symbolicName,
+        @ApiParam(name = "detail", value = "Whether to include types and other detail info, default 'false'", 
+        required = false, defaultValue = "false")
+        @QueryParam("detail")
+        boolean detail);
 
     @Path("/{symbolicName}/{version}")
     @GET

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/206f744a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/TypeApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/TypeApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/TypeApi.java
index c39d5ef..f8167b5 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/TypeApi.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/TypeApi.java
@@ -33,6 +33,8 @@ import javax.ws.rs.core.Response;
 import org.apache.brooklyn.rest.domain.TypeDetail;
 import org.apache.brooklyn.rest.domain.TypeSummary;
 
+import com.google.common.annotations.Beta;
+
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
@@ -41,6 +43,7 @@ import io.swagger.annotations.ApiParam;
 @Api("Types")
 @Consumes(MediaType.APPLICATION_JSON)
 @Produces(MediaType.APPLICATION_JSON)
+@Beta
 public interface TypeApi {
 
     @GET
@@ -52,9 +55,9 @@ public interface TypeApi {
             required = false, defaultValue = "latest")
         @QueryParam("versions")
         String versions,
-        @ApiParam(name = "regex", value = "Regular expression to search for")
+        @ApiParam(name = "regex", value = "Regular expression to search for (in name and description)")
         @QueryParam("regex") @DefaultValue("") String regex,
-        @ApiParam(name = "fragment", value = "Substring case-insensitive to search for")
+        @ApiParam(name = "fragment", value = "Substring case-insensitive to search for (in name and description)")
         @QueryParam("fragment") @DefaultValue("") String fragment);
 
     @Path("/{nameOrAlias}")

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/206f744a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/BundleSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/BundleSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/BundleSummary.java
index f14ee07..c8f442b 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/BundleSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/BundleSummary.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.rest.domain;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.brooklyn.api.typereg.ManagedBundle;
 import org.apache.brooklyn.api.typereg.OsgiBundleWithUrl;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
@@ -35,6 +36,12 @@ import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.google.common.collect.ComparisonChain;
 
+/** Summary info of {@link ManagedBundle} bundles in the catalog providing types,
+ * essentially the symbolic name and version.
+ * Extra fields listing the types may be added.
+ * <p>
+ * These are comparable in alpha-then-version order with most recent preferring non-snapshot versions first,
+ * as per {@link VersionComparator}. */
 public class BundleSummary implements Comparable<BundleSummary> {
 
     private final String symbolicName;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/206f744a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeDetail.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeDetail.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeDetail.java
index 2f9e32f..0fafcbe 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeDetail.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeDetail.java
@@ -24,6 +24,7 @@ import org.apache.brooklyn.api.typereg.RegisteredType.TypeImplementationPlan;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 
+/** As {@link TypeSummary} but including plan information. */
 public class TypeDetail extends TypeSummary {
 
     public static class TypeImplementationPlanSummary {
@@ -42,7 +43,7 @@ public class TypeDetail extends TypeSummary {
             return data;
         }
     }
-    private final TypeImplementationPlanSummary plan;
+    private TypeImplementationPlanSummary plan;
     
     /** Constructor for JSON deserialization use only. */
     TypeDetail() {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/206f744a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeSummary.java
index 0a045d4..70942cf 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeSummary.java
@@ -34,7 +34,10 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.ImmutableSet;
 
+/** Summary info of {@link RegisteredType} items in the catalog.
+ * See {@link TypeDetail} for further information. */
 public class TypeSummary implements Comparable<TypeSummary> {
 
     private final String symbolicName;
@@ -52,7 +55,7 @@ public class TypeSummary implements Comparable<TypeSummary> {
     @JsonInclude(value=Include.NON_EMPTY)
     private Set<String> aliases;
     @JsonInclude(value=Include.NON_EMPTY)
-    private Set<Object> supertypes;
+    private Set<String> supertypes;
     @JsonInclude(value=Include.NON_EMPTY)
     private Set<Object> tags;
 
@@ -88,7 +91,10 @@ public class TypeSummary implements Comparable<TypeSummary> {
         iconUrl = t.getIconUrl();
         
         aliases = t.getAliases();
-        supertypes = t.getSuperTypes();
+        supertypes = ImmutableSet.copyOf(t.getSuperTypes().stream().map(s ->
+            s instanceof Class ? ((Class<?>)s).getName() :
+            s instanceof RegisteredType ? ((RegisteredType)s).getId() :
+            s.toString()).iterator());
         tags = t.getTags();
 
         deprecated = t.isDeprecated();
@@ -172,7 +178,7 @@ public class TypeSummary implements Comparable<TypeSummary> {
         return aliases;
     }
 
-    public Set<Object> getSupertypes() {
+    public Set<String> getSupertypes() {
         return supertypes;
     }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/206f744a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java
index a2a6532..b4664ea 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java
@@ -58,16 +58,18 @@ public class BundleResource extends AbstractBrooklynRestResource implements Bund
     private static final String LATEST = "latest";
 
     @Override
-    public List<BundleSummary> list(String versions) {
-        return list(TypeResource.isLatestOnly(versions, true), Predicates.alwaysTrue());
+    public List<BundleSummary> list(String versions, boolean detail) {
+        return list(Predicates.alwaysTrue(), TypeResource.isLatestOnly(versions, true), detail);
     }
     
-    private List<BundleSummary> list(boolean onlyLatest, Predicate<String> symbolicNameFilter) {
+    private List<BundleSummary> list(Predicate<String> symbolicNameFilter, boolean onlyLatest, boolean detail) {
         
         Map<VersionedName,ManagedBundle> bundles = new TreeMap<>(VersionedNameComparator.INSTANCE);
         for (ManagedBundle b: ((ManagementContextInternal)mgmt()).getOsgiManager().get().getManagedBundles().values()) {
+            if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, b.getId())) {
+                continue;
+            }
             if (symbolicNameFilter.apply(b.getSymbolicName())) {
-                // TODO entitlements for bundles
                 VersionedName key = onlyLatest ? new VersionedName(b.getSymbolicName(), LATEST) : b.getVersionedName();
                 ManagedBundle oldBundle = bundles.get(key);
                 if (oldBundle==null || oldBundle.getVersionedName().compareTo(b.getVersionedName()) > 0) {
@@ -75,20 +77,20 @@ public class BundleResource extends AbstractBrooklynRestResource implements Bund
                 }
             }
         }
-        return toBundleSummary(bundles.values());
+        return toBundleSummary(bundles.values(), detail);
     }
 
-    private List<BundleSummary> toBundleSummary(Iterable<ManagedBundle> sortedItems) {
+    private List<BundleSummary> toBundleSummary(Iterable<ManagedBundle> sortedItems, boolean detail) {
         List<BundleSummary> result = MutableList.of();
         for (ManagedBundle t: sortedItems) {
-            result.add(TypeTransformer.bundleSummary(brooklyn(), t, ui.getBaseUriBuilder(), mgmt()));
+            result.add(TypeTransformer.bundleSummary(brooklyn(), t, ui.getBaseUriBuilder(), mgmt(), detail));
         }
         return result;
     }
 
     @Override
-    public List<BundleSummary> listVersions(String symbolicName) {
-        return list(false, Predicates.equalTo(symbolicName));
+    public List<BundleSummary> listVersions(String symbolicName, boolean detail) {
+        return list(Predicates.equalTo(symbolicName), false, detail);
     }
 
     @Override
@@ -98,12 +100,13 @@ public class BundleResource extends AbstractBrooklynRestResource implements Bund
     }
 
     protected ManagedBundle lookup(String symbolicName, String version) {
-        // TODO entitlements for bundles
-
         ManagedBundle b = ((ManagementContextInternal)mgmt()).getOsgiManager().get().getManagedBundle(new VersionedName(symbolicName, version));
         if (b==null) {
             throw WebResourceUtils.notFound("Bundle with id '%s:%s' not found", symbolicName, version);
         }
+        if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, b.getId())) {
+            throw WebResourceUtils.notFound("Bundle with id '%s:%s' not found", symbolicName, version);
+        }
         return b;
     }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/206f744a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/TypeResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/TypeResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/TypeResource.java
index 86de57d..52627e3 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/TypeResource.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/TypeResource.java
@@ -48,6 +48,7 @@ import org.apache.brooklyn.util.text.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.FluentIterable;
@@ -55,6 +56,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.io.Files;
 
 @HaHotStateRequired
+@Beta
 public class TypeResource extends AbstractBrooklynRestResource implements TypeApi {
 
     private static final Logger log = LoggerFactory.getLogger(TypeResource.class);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/206f744a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
index fb3fdd3..4c9ccb2 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
@@ -79,10 +79,10 @@ public class TypeTransformer {
     }
 
     private static <T extends TypeSummary> T embellish(T result, RegisteredType item, boolean detail, BrooklynRestResourceUtils b, UriBuilder ub) {
-        result.getExtraFields().put("links", makeLinks(item, ub));
+        result.setExtraField("links", makeLinks(item, ub));
         
         if (RegisteredTypes.isTemplate(item)) {
-            result.getExtraFields().put("template", true);
+            result.setExtraField("template", true);
         }
         if (item.getIconUrl()!=null) {
             result.setIconUrl(tidyIconLink(b, item, item.getIconUrl(), ub));
@@ -103,7 +103,7 @@ public class TypeTransformer {
                         config.add(EntityTransformer.adjunctConfigSummary(input));
                     }
                     
-                    result.getExtraFields().put("config", config);
+                    result.setExtraField("config", config);
                 } catch (Exception e) {
                     Exceptions.propagateIfFatal(e);
                     log.trace("Unable to create spec for "+item+": "+e, e);
@@ -111,7 +111,7 @@ public class TypeTransformer {
                 
             } else if (RegisteredTypes.isSubtypeOf(item, Location.class)) {
                 // TODO include config on location specs?  (wasn't done previously so not needed, but good for completeness)
-                result.getExtraFields().put("config", Collections.emptyMap());
+                result.setExtraField("config", Collections.emptyMap());
             }
         }
         return result;
@@ -135,9 +135,9 @@ public class TypeTransformer {
             for (Effector<?> x: type.getEffectors())
                 effectors.add(EffectorTransformer.effectorSummaryForCatalog(x));
             
-            result.getExtraFields().put("config", config);
-            result.getExtraFields().put("sensors", sensors);
-            result.getExtraFields().put("effectors", effectors);
+            result.setExtraField("config", config);
+            result.setExtraField("sensors", sensors);
+            result.setExtraField("effectors", effectors);
         
         } catch (Exception e) {
             Exceptions.propagateIfFatal(e);
@@ -152,19 +152,22 @@ public class TypeTransformer {
         }
     }
 
-    public static BundleSummary bundleSummary(BrooklynRestResourceUtils brooklyn, ManagedBundle b, UriBuilder baseUriBuilder, ManagementContext mgmt) {
+    public static BundleSummary bundleSummary(BrooklynRestResourceUtils brooklyn, ManagedBundle b, UriBuilder baseUriBuilder, ManagementContext mgmt, boolean detail) {
         BundleSummary result = new BundleSummary(b);
-        for (RegisteredType t: mgmt.getTypeRegistry().getMatching(RegisteredTypePredicates.containingBundle(b))) {
-            result.addType(summary(brooklyn, t, baseUriBuilder));
+        if (detail) {
+            result.setExtraField("osgiVersion", b.getOsgiVersionString());
+            result.setExtraField("checksum", b.getChecksum());            
+        }
+        if (detail) {
+            for (RegisteredType t: mgmt.getTypeRegistry().getMatching(RegisteredTypePredicates.containingBundle(b))) {
+                result.addType(summary(brooklyn, t, baseUriBuilder));
+            }
         }
         return result;
     }
     
     public static BundleSummary bundleDetails(BrooklynRestResourceUtils brooklyn, ManagedBundle b, UriBuilder baseUriBuilder, ManagementContext mgmt) {
-        BundleSummary result = bundleSummary(brooklyn, b, baseUriBuilder, mgmt);
-        result.getExtraFields().put("osgiVersion", b.getOsgiVersionString());
-        result.getExtraFields().put("checksum", b.getChecksum());
-        return result;
+        return bundleSummary(brooklyn, b, baseUriBuilder, mgmt, true);
     }
 
     public static BundleInstallationRestResult bundleInstallationResult(OsgiBundleInstallationResult in, ManagementContext mgmt, BrooklynRestResourceUtils brooklynU, UriInfo ui) {


[02/11] brooklyn-server git commit: REST API for bundles, types, and subtypes

Posted by he...@apache.org.
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
index 01e8ad5..b3eb4e4 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
@@ -26,6 +26,7 @@ import org.apache.brooklyn.rest.resources.AccessResource;
 import org.apache.brooklyn.rest.resources.ActivityResource;
 import org.apache.brooklyn.rest.resources.ApidocResource;
 import org.apache.brooklyn.rest.resources.ApplicationResource;
+import org.apache.brooklyn.rest.resources.BundleResource;
 import org.apache.brooklyn.rest.resources.CatalogResource;
 import org.apache.brooklyn.rest.resources.EffectorResource;
 import org.apache.brooklyn.rest.resources.EntityConfigResource;
@@ -37,6 +38,8 @@ import org.apache.brooklyn.rest.resources.PolicyResource;
 import org.apache.brooklyn.rest.resources.ScriptResource;
 import org.apache.brooklyn.rest.resources.SensorResource;
 import org.apache.brooklyn.rest.resources.ServerResource;
+import org.apache.brooklyn.rest.resources.SubtypeResource;
+import org.apache.brooklyn.rest.resources.TypeResource;
 import org.apache.brooklyn.rest.resources.UsageResource;
 import org.apache.brooklyn.rest.util.DefaultExceptionMapper;
 import org.apache.brooklyn.rest.util.FormMapProvider;
@@ -46,14 +49,15 @@ import com.google.common.collect.Iterables;
 
 import io.swagger.jaxrs.listing.SwaggerSerializers;
 
-
-@SuppressWarnings("deprecation")
 public class BrooklynRestApi {
 
     public static Iterable<AbstractBrooklynRestResource> getBrooklynRestResources() {
         List<AbstractBrooklynRestResource> resources = new ArrayList<>();
         resources.add(new LocationResource());
         resources.add(new CatalogResource());
+        resources.add(new TypeResource());
+        resources.add(new SubtypeResource());
+        resources.add(new BundleResource());
         resources.add(new ApplicationResource());
         resources.add(new EntityResource());
         resources.add(new EntityConfigResource());

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java
new file mode 100644
index 0000000..a2a6532
--- /dev/null
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java
@@ -0,0 +1,186 @@
+/*
+ * 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.rest.resources;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.brooklyn.api.typereg.ManagedBundle;
+import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
+import org.apache.brooklyn.core.mgmt.ha.OsgiBundleInstallationResult;
+import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.rest.api.BundleApi;
+import org.apache.brooklyn.rest.domain.ApiError;
+import org.apache.brooklyn.rest.domain.BundleInstallationRestResult;
+import org.apache.brooklyn.rest.domain.BundleSummary;
+import org.apache.brooklyn.rest.filter.HaHotStateRequired;
+import org.apache.brooklyn.rest.transform.TypeTransformer;
+import org.apache.brooklyn.rest.util.WebResourceUtils;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.exceptions.ReferenceWithError;
+import org.apache.brooklyn.util.osgi.VersionedName;
+import org.apache.brooklyn.util.osgi.VersionedName.VersionedNameComparator;
+import org.apache.brooklyn.util.yaml.Yamls;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+
+@HaHotStateRequired
+public class BundleResource extends AbstractBrooklynRestResource implements BundleApi {
+
+    private static final Logger log = LoggerFactory.getLogger(BundleResource.class);
+    private static final String LATEST = "latest";
+
+    @Override
+    public List<BundleSummary> list(String versions) {
+        return list(TypeResource.isLatestOnly(versions, true), Predicates.alwaysTrue());
+    }
+    
+    private List<BundleSummary> list(boolean onlyLatest, Predicate<String> symbolicNameFilter) {
+        
+        Map<VersionedName,ManagedBundle> bundles = new TreeMap<>(VersionedNameComparator.INSTANCE);
+        for (ManagedBundle b: ((ManagementContextInternal)mgmt()).getOsgiManager().get().getManagedBundles().values()) {
+            if (symbolicNameFilter.apply(b.getSymbolicName())) {
+                // TODO entitlements for bundles
+                VersionedName key = onlyLatest ? new VersionedName(b.getSymbolicName(), LATEST) : b.getVersionedName();
+                ManagedBundle oldBundle = bundles.get(key);
+                if (oldBundle==null || oldBundle.getVersionedName().compareTo(b.getVersionedName()) > 0) {
+                    bundles.put(key, b);
+                }
+            }
+        }
+        return toBundleSummary(bundles.values());
+    }
+
+    private List<BundleSummary> toBundleSummary(Iterable<ManagedBundle> sortedItems) {
+        List<BundleSummary> result = MutableList.of();
+        for (ManagedBundle t: sortedItems) {
+            result.add(TypeTransformer.bundleSummary(brooklyn(), t, ui.getBaseUriBuilder(), mgmt()));
+        }
+        return result;
+    }
+
+    @Override
+    public List<BundleSummary> listVersions(String symbolicName) {
+        return list(false, Predicates.equalTo(symbolicName));
+    }
+
+    @Override
+    public BundleSummary detail(String symbolicName, String version) {
+        ManagedBundle b = lookup(symbolicName, version);
+        return TypeTransformer.bundleDetails(brooklyn(), b, ui.getBaseUriBuilder(), mgmt());
+    }
+
+    protected ManagedBundle lookup(String symbolicName, String version) {
+        // TODO entitlements for bundles
+
+        ManagedBundle b = ((ManagementContextInternal)mgmt()).getOsgiManager().get().getManagedBundle(new VersionedName(symbolicName, version));
+        if (b==null) {
+            throw WebResourceUtils.notFound("Bundle with id '%s:%s' not found", symbolicName, version);
+        }
+        return b;
+    }
+
+    @Override
+    public BundleInstallationRestResult remove(String symbolicName, String version, Boolean force) {
+        ManagedBundle b = lookup(symbolicName, version);
+        log.info("REST removing "+symbolicName+":"+version);
+        if (force==null) force = false;
+        ReferenceWithError<OsgiBundleInstallationResult> r = ((ManagementContextInternal)mgmt()).getOsgiManager().get().uninstallUploadedBundle(b, force);
+        return TypeTransformer.bundleInstallationResult(r.getWithoutError(), mgmt(), brooklyn(), ui);
+    }
+
+
+    @Override
+    public Response createFromYaml(String yaml, Boolean force) {
+        if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.ADD_CATALOG_ITEM, yaml)) {
+            throw WebResourceUtils.forbidden("User '%s' is not authorized to add catalog items",
+                Entitlements.getEntitlementContext().user());
+        }
+        if (force==null) force = false;
+
+        try {
+            return Response.status(Status.CREATED).entity(
+                    TypeTransformer.bundleInstallationResult(
+                        ((BasicBrooklynCatalog)brooklyn().getCatalog()).addItemsBundleResult(yaml, force), mgmt(), brooklyn(), ui)).build();
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            return badRequest(e);
+        }
+    }
+    
+    @Override
+    public Response createFromArchive(byte[] zipInput, Boolean force) {
+        if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.ROOT, null)) {
+            throw WebResourceUtils.forbidden("User '%s' is not authorized to add catalog items",
+                Entitlements.getEntitlementContext().user());
+        }
+        if (force==null) force = false;
+
+        ReferenceWithError<OsgiBundleInstallationResult> result = ((ManagementContextInternal)mgmt()).getOsgiManager().get()
+            .install(null, new ByteArrayInputStream(zipInput), true, true, force);
+
+        if (OsgiBundleInstallationResult.ResultCode.IGNORING_BUNDLE_AREADY_INSTALLED.equals(result.getWithoutError().getCode())) {
+            result = ReferenceWithError.newInstanceThrowingError(result.getWithoutError(), new IllegalStateException(
+                    "Cannot add bundle" + result.getWithoutError().getMetadata().getVersionedName() +
+                    "; different bundle with same name already installed"));
+        }
+        
+        if (result.hasError()) {
+            // (rollback already done as part of install, if necessary)
+            if (log.isTraceEnabled()) {
+                log.trace("Unable to create from archive, returning 400: "+result.getError().getMessage(), result.getError());
+            }
+            return ApiError.builder().errorCode(Status.BAD_REQUEST).message(result.getWithoutError().getMessage())
+                .data(TypeTransformer.bundleInstallationResult(result.getWithoutError(), mgmt(), brooklyn(), ui)).build().asJsonResponse();
+        }
+
+        BundleInstallationRestResult resultR = TypeTransformer.bundleInstallationResult(result.get(), mgmt(), brooklyn(), ui);
+        return Response.status(Status.CREATED).entity( resultR ).build();
+    }
+
+    @Override
+    public Response createAutodetecting(byte[] item, Boolean force) {
+        Throwable yamlException = null;
+        try {
+            MutableList.copyOf( Yamls.parseAll(new InputStreamReader(new ByteArrayInputStream(item))) );
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            yamlException = e;
+        }
+        
+        if (yamlException==null) {
+            // treat as yaml if it parsed
+            return createFromYaml(new String(item), force);
+        }
+        
+        return createFromArchive(item, force);
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/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 d327e40..65e6956 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
@@ -35,7 +35,6 @@ import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
 import org.apache.brooklyn.api.catalog.CatalogItem;
-import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
@@ -46,6 +45,7 @@ import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
 import org.apache.brooklyn.core.typereg.RegisteredTypes;
 import org.apache.brooklyn.rest.api.CatalogApi;
 import org.apache.brooklyn.rest.domain.ApiError;
+import org.apache.brooklyn.rest.domain.BundleInstallationRestResult;
 import org.apache.brooklyn.rest.domain.CatalogEnricherSummary;
 import org.apache.brooklyn.rest.domain.CatalogEntitySummary;
 import org.apache.brooklyn.rest.domain.CatalogItemSummary;
@@ -53,7 +53,7 @@ import org.apache.brooklyn.rest.domain.CatalogLocationSummary;
 import org.apache.brooklyn.rest.domain.CatalogPolicySummary;
 import org.apache.brooklyn.rest.filter.HaHotStateRequired;
 import org.apache.brooklyn.rest.transform.CatalogTransformer;
-import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
+import org.apache.brooklyn.rest.transform.TypeTransformer;
 import org.apache.brooklyn.rest.util.WebResourceUtils;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
@@ -149,36 +149,6 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
         }
     }
 
-    public static class BundleInstallationRestResult {
-        // as Osgi result, but without bundle, and with maps of catalog items installed
-        
-        String message;
-        String bundle;
-        OsgiBundleInstallationResult.ResultCode code;
-        
-        Map<String,Object> types;
-        
-        public String getMessage() {
-            return message;
-        }
-        
-        public static BundleInstallationRestResult of(OsgiBundleInstallationResult in, ManagementContext mgmt, BrooklynRestResourceUtils brooklynU, UriInfo ui) {
-            BundleInstallationRestResult result = new BundleInstallationRestResult();
-            result.message = in.getMessage();
-            result.bundle = in.getVersionedName() != null ? in.getVersionedName().toString() : "";
-            result.code = in.getCode();
-            if (in.getCatalogItemsInstalled()!=null) {
-                result.types = MutableMap.of();
-                for (String id: in.getCatalogItemsInstalled()) {
-                    RegisteredType ci = mgmt.getTypeRegistry().get(id);
-                    CatalogItemSummary summary = CatalogTransformer.catalogItemSummary(brooklynU, ci, ui.getBaseUriBuilder());
-                    result.types.put(id, summary);
-                }
-            }
-            return result;
-        }
-    }
-    
     @Override
     @Beta
     public Response createFromArchive(byte[] zipInput, boolean detail, boolean forceUpdate) {
@@ -202,11 +172,11 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
                 log.trace("Unable to create from archive, returning 400: "+result.getError().getMessage(), result.getError());
             }
             return ApiError.builder().errorCode(Status.BAD_REQUEST).message(result.getWithoutError().getMessage())
-                .data(BundleInstallationRestResult.of(result.getWithoutError(), mgmt(), brooklyn(), ui)).build().asJsonResponse();
+                .data(TypeTransformer.bundleInstallationResult(result.getWithoutError(), mgmt(), brooklyn(), ui)).build().asJsonResponse();
         }
 
-        BundleInstallationRestResult resultR = BundleInstallationRestResult.of(result.get(), mgmt(), brooklyn(), ui);
-        return Response.status(Status.CREATED).entity( detail ? resultR : resultR.types ).build();
+        BundleInstallationRestResult resultR = TypeTransformer.bundleInstallationResult(result.get(), mgmt(), brooklyn(), ui);
+        return Response.status(Status.CREATED).entity( detail ? resultR : resultR.getTypes() ).build();
     }
 
     private Response buildCreateResponse(Iterable<RegisteredType> catalogItems) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/SubtypeResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/SubtypeResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/SubtypeResource.java
new file mode 100644
index 0000000..12716b3
--- /dev/null
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/SubtypeResource.java
@@ -0,0 +1,82 @@
+/*
+ * 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.rest.resources;
+
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.api.sensor.Enricher;
+import org.apache.brooklyn.api.typereg.RegisteredType;
+import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
+import org.apache.brooklyn.core.typereg.RegisteredTypes;
+import org.apache.brooklyn.rest.api.SubtypeApi;
+import org.apache.brooklyn.rest.domain.TypeSummary;
+import org.apache.brooklyn.rest.filter.HaHotStateRequired;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.text.StringPredicates;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+
+@HaHotStateRequired
+public class SubtypeResource extends AbstractBrooklynRestResource implements SubtypeApi {
+
+    @SuppressWarnings("unused")
+    private static final Logger log = LoggerFactory.getLogger(SubtypeResource.class);
+
+    @Override
+    public List<TypeSummary> list(String supertype, String versions, String regex, String fragment) {
+        List<Predicate<RegisteredType>> filters = MutableList.<Predicate<RegisteredType>>of()
+            .append(RegisteredTypePredicates.entitledToSee(mgmt()))
+            .append(RegisteredTypePredicates.subtypeOf(supertype));
+        if (TypeResource.isLatestOnly(versions, true)) {
+            // TODO inefficient - does n^2 comparisons where n is sufficient
+            // create RegisteredTypes.filterBestVersions to do a list after the initial parse
+            // (and javadoc in predicate method below)
+            filters.add(RegisteredTypePredicates.isBestVersion(mgmt()));
+        }
+        if (Strings.isNonEmpty(regex)) {
+            filters.add(RegisteredTypePredicates.nameOrAlias(StringPredicates.containsRegex(regex)));
+        }
+        if (Strings.isNonEmpty(fragment)) {
+            filters.add(RegisteredTypePredicates.nameOrAlias(StringPredicates.containsLiteralIgnoreCase(fragment)));
+        }
+        Predicate<RegisteredType> filter = Predicates.and(filters);
+        ImmutableList<RegisteredType> sortedItems =
+            FluentIterable.from(brooklyn().getTypeRegistry().getMatching(filter))
+                .toSortedList(RegisteredTypes.RegisteredTypeNameThenBestFirstComparator.INSTANCE);
+        return TypeResource.toTypeSummary(brooklyn(), sortedItems, ui.getBaseUriBuilder());
+    }
+    
+    @Override public List<TypeSummary> listApplications(String versions, String regex, String fragment) { return list(Application.class.getName(), versions, regex, fragment); }
+    @Override public List<TypeSummary> listEntities(String versions, String regex, String fragment) { return list(Entity.class.getName(), versions, regex, fragment); }
+    @Override public List<TypeSummary> listPolicies(String versions, String regex, String fragment) { return list(Policy.class.getName(), versions, regex, fragment); }
+    @Override public List<TypeSummary> listEnrichers(String versions, String regex, String fragment) { return list(Enricher.class.getName(), versions, regex, fragment); }
+    @Override public List<TypeSummary> listLocations(String versions, String regex, String fragment) { return list(Location.class.getName(), versions, regex, fragment); }
+    
+}
+

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/TypeResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/TypeResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/TypeResource.java
new file mode 100644
index 0000000..86de57d
--- /dev/null
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/TypeResource.java
@@ -0,0 +1,186 @@
+/*
+ * 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.rest.resources;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.brooklyn.api.typereg.RegisteredType;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
+import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
+import org.apache.brooklyn.core.typereg.RegisteredTypes;
+import org.apache.brooklyn.rest.api.TypeApi;
+import org.apache.brooklyn.rest.domain.TypeDetail;
+import org.apache.brooklyn.rest.domain.TypeSummary;
+import org.apache.brooklyn.rest.filter.HaHotStateRequired;
+import org.apache.brooklyn.rest.transform.TypeTransformer;
+import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
+import org.apache.brooklyn.rest.util.WebResourceUtils;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.core.ResourceUtils;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.text.StringPredicates;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.Files;
+
+@HaHotStateRequired
+public class TypeResource extends AbstractBrooklynRestResource implements TypeApi {
+
+    private static final Logger log = LoggerFactory.getLogger(TypeResource.class);
+    private static final String LATEST = "latest";
+    private static final String ALL = "all";
+
+    private static Set<String> missingIcons = MutableSet.of();
+
+    static boolean isLatestOnly(String versions, boolean defaultValue) {
+        if (ALL.equalsIgnoreCase(versions)) return false;
+        if (LATEST.equalsIgnoreCase(versions)) return true;
+        if (Strings.isNonBlank(versions)) {
+            log.warn("Invalid 'versions' argument '"+versions+"' when listing types; should be 'all' or 'latest'");
+        }
+        return defaultValue;
+    }
+    
+    @Override
+    public List<TypeSummary> list(String versions, String regex, String fragment) {
+        List<Predicate<RegisteredType>> filters = MutableList.<Predicate<RegisteredType>>of()
+            .append(RegisteredTypePredicates.entitledToSee(mgmt()));
+        if (TypeResource.isLatestOnly(versions, true)) {
+            // TODO inefficient - does n^2 comparisons where n is sufficient
+            // create RegisteredTypes.filterBestVersions to do a list after the initial parse
+            // (and javadoc in predicate method below)
+            filters.add(RegisteredTypePredicates.isBestVersion(mgmt()));
+        }
+        if (Strings.isNonEmpty(regex)) {
+            filters.add(RegisteredTypePredicates.nameOrAlias(StringPredicates.containsRegex(regex)));
+        }
+        if (Strings.isNonEmpty(fragment)) {
+            filters.add(RegisteredTypePredicates.nameOrAlias(StringPredicates.containsLiteralIgnoreCase(fragment)));
+        }
+        Predicate<RegisteredType> filter = Predicates.and(filters);
+
+        ImmutableList<RegisteredType> sortedItems =
+            FluentIterable.from(brooklyn().getTypeRegistry().getMatching(filter))
+                .toSortedList(RegisteredTypes.RegisteredTypeNameThenBestFirstComparator.INSTANCE);
+        return toTypeSummary(brooklyn(), sortedItems, ui.getBaseUriBuilder());
+    }
+
+    static List<TypeSummary> toTypeSummary(BrooklynRestResourceUtils brooklyn, Iterable<RegisteredType> sortedItems, UriBuilder uriBuilder) {
+        List<TypeSummary> result = MutableList.of();
+        for (RegisteredType t: sortedItems) {
+            result.add(TypeTransformer.summary(brooklyn, t, uriBuilder));
+        }
+        return result;
+    }
+
+    @Override
+    public List<TypeSummary> listVersions(String nameOrAlias) {
+        Predicate<RegisteredType> filter = Predicates.and(RegisteredTypePredicates.entitledToSee(mgmt()), 
+            RegisteredTypePredicates.nameOrAlias(nameOrAlias));
+        ImmutableList<RegisteredType> sortedItems =
+            FluentIterable.from(brooklyn().getTypeRegistry().getMatching(filter))
+                .toSortedList(RegisteredTypes.RegisteredTypeNameThenBestFirstComparator.INSTANCE);
+        return toTypeSummary(brooklyn(), sortedItems, ui.getBaseUriBuilder());
+    }
+
+    @Override
+    public TypeDetail detail(String symbolicName, String version) {
+        RegisteredType item = lookup(symbolicName, version);
+        return TypeTransformer.detail(brooklyn(), item, ui.getBaseUriBuilder());
+    }
+
+    protected RegisteredType lookup(String symbolicName, String version) {
+        if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, symbolicName+":"+version)) {
+            // TODO best to default to "not found" - unless maybe they have permission to "see null"
+            throw WebResourceUtils.forbidden("User '%s' not permitted to see info on this type (including whether or not installed)",
+                Entitlements.getEntitlementContext().user());
+        }
+        RegisteredType item;
+        if (LATEST.equalsIgnoreCase(version)) {
+            item = brooklyn().getTypeRegistry().get(symbolicName);
+        } else {
+            item = brooklyn().getTypeRegistry().get(symbolicName, version);
+        }
+        if (item==null) {
+            throw WebResourceUtils.notFound("Entity with id '%s:%s' not found", symbolicName, version);
+        }
+        return item;
+    }
+
+    @Override
+    public Response icon(String symbolicName, String version) {
+        RegisteredType item = lookup(symbolicName, version);
+        return produceIcon(item);
+    }
+
+    private Response produceIcon(RegisteredType result) {
+        String url = result.getIconUrl();
+        if (url==null) {
+            log.debug("No icon available for "+result+"; returning "+Status.NO_CONTENT);
+            return Response.status(Status.NO_CONTENT).build();
+        }
+        
+        if (brooklyn().isUrlServerSideAndSafe(url)) {
+            // classpath URL's we will serve IF they end with a recognised image format;
+            // paths (ie non-protocol) and 
+            // NB, for security, file URL's are NOT served
+            log.debug("Loading and returning "+url+" as icon for "+result);
+            
+            MediaType mime = WebResourceUtils.getImageMediaTypeFromExtension(Files.getFileExtension(url));
+            try {
+                Object content = ResourceUtils.create(CatalogUtils.newClassLoadingContext(mgmt(), result)).getResourceFromUrl(url);
+                return Response.ok(content, mime).build();
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                synchronized (missingIcons) {
+                    if (missingIcons.add(url)) {
+                        // note: this can be quite common when running from an IDE, as resources may not be copied;
+                        // a mvn build should sort it out (the IDE will then find the resources, until you clean or maybe refresh...)
+                        log.warn("Missing icon data for "+result.getId()+", expected at: "+url+" (subsequent messages will log debug only)");
+                        log.debug("Trace for missing icon data at "+url+": "+e, e);
+                    } else {
+                        log.debug("Missing icon data for "+result.getId()+", expected at: "+url+" (already logged WARN and error details)");
+                    }
+                }
+                throw WebResourceUtils.notFound("Icon unavailable for %s", result.getId());
+            }
+        }
+        
+        log.debug("Returning redirect to "+url+" as icon for "+result);
+        
+        // for anything else we do a redirect (e.g. http / https; perhaps ftp)
+        return Response.temporaryRedirect(URI.create(url)).build();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
index 8a15020..6bf3f19 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
@@ -19,6 +19,7 @@
 package org.apache.brooklyn.rest.transform;
 
 import static com.google.common.collect.Iterables.transform;
+import static org.apache.brooklyn.rest.util.WebResourceUtils.serviceUriBuilder;
 
 import java.lang.reflect.Field;
 import java.net.URI;
@@ -26,13 +27,21 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import javax.ws.rs.core.UriBuilder;
+
 import org.apache.brooklyn.api.catalog.CatalogConfig;
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.objs.SpecParameter;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.config.render.RendererHints;
 import org.apache.brooklyn.core.typereg.RegisteredTypes;
+import org.apache.brooklyn.rest.api.ApplicationApi;
+import org.apache.brooklyn.rest.api.CatalogApi;
+import org.apache.brooklyn.rest.api.EntityApi;
+import org.apache.brooklyn.rest.api.EntityConfigApi;
+import org.apache.brooklyn.rest.domain.AdjunctConfigSummary;
 import org.apache.brooklyn.rest.domain.EnricherConfigSummary;
 import org.apache.brooklyn.rest.domain.EntityConfigSummary;
 import org.apache.brooklyn.rest.domain.EntitySummary;
@@ -43,13 +52,6 @@ import com.google.common.base.Function;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
-import javax.ws.rs.core.UriBuilder;
-import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
-import org.apache.brooklyn.rest.api.ApplicationApi;
-import org.apache.brooklyn.rest.api.CatalogApi;
-import org.apache.brooklyn.rest.api.EntityApi;
-import org.apache.brooklyn.rest.api.EntityConfigApi;
-import static org.apache.brooklyn.rest.util.WebResourceUtils.serviceUriBuilder;
 
 /**
  * @author Adam Lowe
@@ -123,6 +125,10 @@ public class EntityTransformer {
         return new EntityConfigSummary(config, label, priority, pinned, mapOfLinks);
     }
 
+    public static AdjunctConfigSummary adjunctConfigSummary(ConfigKey<?> config, String label, Double priority, Map<String, URI> links) {
+        return new AdjunctConfigSummary(config, label, priority, links);
+    }
+
     public static PolicyConfigSummary policyConfigSummary(ConfigKey<?> config, String label, Double priority, Map<String, URI> links) {
         return new PolicyConfigSummary(config, label, priority, links);
     }
@@ -192,6 +198,11 @@ public class EntityTransformer {
         return entityConfigSummary(input.getConfigKey(), input.getLabel(), priority, input.isPinned(), null);
     }
 
+    public static AdjunctConfigSummary adjunctConfigSummary(SpecParameter<?> input) {
+        Double priority = input.isPinned() ? Double.valueOf(1d) : null;
+        return policyConfigSummary(input.getConfigKey(), input.getLabel(), priority, null);
+    }
+
     public static PolicyConfigSummary policyConfigSummary(SpecParameter<?> input) {
         Double priority = input.isPinned() ? Double.valueOf(1d) : null;
         return policyConfigSummary(input.getConfigKey(), input.getLabel(), priority, null);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
new file mode 100644
index 0000000..fb3fdd3
--- /dev/null
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
@@ -0,0 +1,194 @@
+/*
+ * 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.rest.transform;
+
+import static org.apache.brooklyn.rest.util.WebResourceUtils.serviceUriBuilder;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.entity.EntityType;
+import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.objs.EntityAdjunct;
+import org.apache.brooklyn.api.objs.SpecParameter;
+import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.api.sensor.Enricher;
+import org.apache.brooklyn.api.sensor.Feed;
+import org.apache.brooklyn.api.sensor.Sensor;
+import org.apache.brooklyn.api.typereg.ManagedBundle;
+import org.apache.brooklyn.api.typereg.RegisteredType;
+import org.apache.brooklyn.core.entity.EntityDynamicType;
+import org.apache.brooklyn.core.mgmt.ha.OsgiBundleInstallationResult;
+import org.apache.brooklyn.core.objs.BrooklynTypes;
+import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
+import org.apache.brooklyn.core.typereg.RegisteredTypes;
+import org.apache.brooklyn.rest.api.TypeApi;
+import org.apache.brooklyn.rest.domain.AdjunctConfigSummary;
+import org.apache.brooklyn.rest.domain.BundleInstallationRestResult;
+import org.apache.brooklyn.rest.domain.BundleSummary;
+import org.apache.brooklyn.rest.domain.EffectorSummary;
+import org.apache.brooklyn.rest.domain.EntityConfigSummary;
+import org.apache.brooklyn.rest.domain.SensorSummary;
+import org.apache.brooklyn.rest.domain.SummaryComparators;
+import org.apache.brooklyn.rest.domain.TypeDetail;
+import org.apache.brooklyn.rest.domain.TypeSummary;
+import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Sets;
+
+public class TypeTransformer {
+
+    private static final org.slf4j.Logger log = LoggerFactory.getLogger(TypeTransformer.class);
+    
+    public static <T extends Entity> TypeSummary summary(BrooklynRestResourceUtils b, RegisteredType item, UriBuilder ub) {
+        return embellish(new TypeSummary(item), item, false, b, ub);
+    }
+
+    public static TypeDetail detail(BrooklynRestResourceUtils b, RegisteredType item, UriBuilder ub) {
+        return embellish(new TypeDetail(item), item, true, b, ub);
+    }
+
+    private static <T extends TypeSummary> T embellish(T result, RegisteredType item, boolean detail, BrooklynRestResourceUtils b, UriBuilder ub) {
+        result.getExtraFields().put("links", makeLinks(item, ub));
+        
+        if (RegisteredTypes.isTemplate(item)) {
+            result.getExtraFields().put("template", true);
+        }
+        if (item.getIconUrl()!=null) {
+            result.setIconUrl(tidyIconLink(b, item, item.getIconUrl(), ub));
+        }
+        
+        if (detail) {
+            if (RegisteredTypes.isSubtypeOf(item, Entity.class)) {
+                embellishEntity(result, item, b);
+            } else if (RegisteredTypes.isSubtypeOf(item, EntityAdjunct.class) ||
+                    // when implied supertypes are used we won't need the code below
+                    RegisteredTypes.isSubtypeOf(item, Policy.class) || RegisteredTypes.isSubtypeOf(item, Enricher.class) || RegisteredTypes.isSubtypeOf(item, Feed.class)
+                    ) {
+                try {
+                    Set<AdjunctConfigSummary> config = Sets.newLinkedHashSet();
+                    
+                    AbstractBrooklynObjectSpec<?,?> spec = b.getTypeRegistry().createSpec(item, null, null);
+                    for (final SpecParameter<?> input : spec.getParameters()){
+                        config.add(EntityTransformer.adjunctConfigSummary(input));
+                    }
+                    
+                    result.getExtraFields().put("config", config);
+                } catch (Exception e) {
+                    Exceptions.propagateIfFatal(e);
+                    log.trace("Unable to create spec for "+item+": "+e, e);
+                }
+                
+            } else if (RegisteredTypes.isSubtypeOf(item, Location.class)) {
+                // TODO include config on location specs?  (wasn't done previously so not needed, but good for completeness)
+                result.getExtraFields().put("config", Collections.emptyMap());
+            }
+        }
+        return result;
+    }
+
+    protected static <T extends TypeSummary> void embellishEntity(T result, RegisteredType item, BrooklynRestResourceUtils b) {
+        try {
+            Set<EntityConfigSummary> config = Sets.newLinkedHashSet();
+            Set<SensorSummary> sensors = Sets.newTreeSet(SummaryComparators.nameComparator());
+            Set<EffectorSummary> effectors = Sets.newTreeSet(SummaryComparators.nameComparator());
+      
+            EntitySpec<?> spec = b.getTypeRegistry().createSpec(item, null, EntitySpec.class);
+            EntityDynamicType typeMap = BrooklynTypes.getDefinedEntityType(spec.getType());
+            EntityType type = typeMap.getSnapshot();
+   
+            AtomicInteger paramPriorityCnt = new AtomicInteger();
+            for (SpecParameter<?> input: spec.getParameters())
+                config.add(EntityTransformer.entityConfigSummary(input, paramPriorityCnt));
+            for (Sensor<?> x: type.getSensors())
+                sensors.add(SensorTransformer.sensorSummaryForCatalog(x));
+            for (Effector<?> x: type.getEffectors())
+                effectors.add(EffectorTransformer.effectorSummaryForCatalog(x));
+            
+            result.getExtraFields().put("config", config);
+            result.getExtraFields().put("sensors", sensors);
+            result.getExtraFields().put("effectors", effectors);
+        
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+
+            // templates with multiple entities can't have spec created in the manner above; just ignore
+            if (item.getSuperTypes().contains(Entity.class)) {
+                log.warn("Unable to create spec for "+item+": "+e, e);
+            }
+            if (log.isTraceEnabled()) {
+                log.trace("Unable to create spec for "+item+": "+e, e);
+            }
+        }
+    }
+
+    public static BundleSummary bundleSummary(BrooklynRestResourceUtils brooklyn, ManagedBundle b, UriBuilder baseUriBuilder, ManagementContext mgmt) {
+        BundleSummary result = new BundleSummary(b);
+        for (RegisteredType t: mgmt.getTypeRegistry().getMatching(RegisteredTypePredicates.containingBundle(b))) {
+            result.addType(summary(brooklyn, t, baseUriBuilder));
+        }
+        return result;
+    }
+    
+    public static BundleSummary bundleDetails(BrooklynRestResourceUtils brooklyn, ManagedBundle b, UriBuilder baseUriBuilder, ManagementContext mgmt) {
+        BundleSummary result = bundleSummary(brooklyn, b, baseUriBuilder, mgmt);
+        result.getExtraFields().put("osgiVersion", b.getOsgiVersionString());
+        result.getExtraFields().put("checksum", b.getChecksum());
+        return result;
+    }
+
+    public static BundleInstallationRestResult bundleInstallationResult(OsgiBundleInstallationResult in, ManagementContext mgmt, BrooklynRestResourceUtils brooklynU, UriInfo ui) {
+        BundleInstallationRestResult result = new BundleInstallationRestResult(
+            in.getMessage(), in.getVersionedName() != null ? in.getVersionedName().toString() : "", in.getCode());
+        for (RegisteredType t: in.getTypesInstalled()) {
+            TypeSummary summary = TypeTransformer.summary(brooklynU, t, ui.getBaseUriBuilder());
+            result.getTypes().put(t.getId(), summary);
+        }
+        return result;
+    }
+
+    protected static Map<String, URI> makeLinks(RegisteredType item, UriBuilder ub) {
+        return MutableMap.<String, URI>of().addIfNotNull("self", getSelfLink(item, ub));
+    }
+    
+    private static URI getSelfLink(RegisteredType item, UriBuilder ub) {
+        return serviceUriBuilder(ub, TypeApi.class, "detail").build(item.getSymbolicName(), item.getVersion());
+    }
+    private static String tidyIconLink(BrooklynRestResourceUtils b, RegisteredType item, String iconUrl, UriBuilder ub) {
+        if (b.isUrlServerSideAndSafe(iconUrl)) {
+            return serviceUriBuilder(ub, TypeApi.class, "icon").build(item.getSymbolicName(), item.getVersion()).toString();
+        }
+        return iconUrl;
+    }
+
+}


[11/11] brooklyn-server git commit: This closes #810

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


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

Branch: refs/heads/master
Commit: 05ae90a71ce260c72e72ca059d3d4e1a659a73a8
Parents: 3ac961d 54fb9c6
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Sep 25 10:29:14 2017 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Mon Sep 25 10:29:14 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/api/catalog/BrooklynCatalog.java   |    3 +
 .../typereg/RegisteredTypeLoadingContext.java   |    2 +-
 .../catalog/internal/BasicBrooklynCatalog.java  |  106 +-
 .../core/mgmt/entitlement/Entitlements.java     |    1 +
 .../core/mgmt/ha/OsgiArchiveInstaller.java      |   18 +-
 .../mgmt/ha/OsgiBundleInstallationResult.java   |   21 +-
 .../brooklyn/core/mgmt/ha/OsgiManager.java      |   89 +-
 .../core/typereg/RegisteredTypePredicates.java  |   98 +-
 .../brooklyn/core/typereg/RegisteredTypes.java  |   43 +-
 .../org/apache/brooklyn/rest/api/BundleApi.java |  147 +++
 .../apache/brooklyn/rest/api/CatalogApi.java    |   53 +
 .../org/apache/brooklyn/rest/api/TypeApi.java   |  100 ++
 .../domain/BundleInstallationRestResult.java    |   67 ++
 .../brooklyn/rest/domain/BundleSummary.java     |  106 ++
 .../brooklyn/rest/domain/ConfigSummary.java     |   79 +-
 .../rest/domain/EnricherConfigSummary.java      |   52 +-
 .../rest/domain/EntityConfigSummary.java        |   67 +-
 .../rest/domain/LocationConfigSummary.java      |   40 +-
 .../rest/domain/PolicyConfigSummary.java        |   53 +-
 .../brooklyn/rest/domain/TaskSummary.java       |    9 +-
 .../apache/brooklyn/rest/domain/TypeDetail.java |   87 ++
 .../brooklyn/rest/domain/TypeSummary.java       |  293 +++++
 .../apache/brooklyn/rest/BrooklynRestApi.java   |    6 +-
 .../brooklyn/rest/resources/BundleResource.java |  169 +++
 .../rest/resources/CatalogResource.java         |   40 +-
 .../brooklyn/rest/resources/TypeResource.java   |  207 ++++
 .../rest/transform/EntityTransformer.java       |   26 +-
 .../rest/transform/PolicyTransformer.java       |    3 +-
 .../rest/transform/TypeTransformer.java         |  197 ++++
 .../resources/BundleAndTypeResourcesTest.java   | 1098 ++++++++++++++++++
 .../brooklyn/util/osgi/VersionedName.java       |   24 +-
 31 files changed, 2982 insertions(+), 322 deletions(-)
----------------------------------------------------------------------



[10/11] brooklyn-server git commit: address PR comments, mainly simplify ConfigSummary

Posted by he...@apache.org.
address PR comments, mainly simplify ConfigSummary


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

Branch: refs/heads/master
Commit: 54fb9c6a6f0ce07939df1ae417b4add17c9a786a
Parents: 8e69330
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Sep 25 10:00:01 2017 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Mon Sep 25 10:00:01 2017 +0100

----------------------------------------------------------------------
 .../core/typereg/RegisteredTypePredicates.java  | 78 ++++++++++++++----
 .../rest/domain/AdjunctConfigSummary.java       | 85 --------------------
 .../brooklyn/rest/domain/ConfigSummary.java     | 66 +++++++++++----
 .../rest/domain/EnricherConfigSummary.java      | 12 +--
 .../rest/domain/EntityConfigSummary.java        | 67 +--------------
 .../rest/domain/LocationConfigSummary.java      | 40 ++-------
 .../rest/domain/PolicyConfigSummary.java        | 30 +++++--
 .../rest/transform/EntityTransformer.java       | 11 +--
 .../rest/transform/PolicyTransformer.java       |  3 +-
 .../rest/transform/TypeTransformer.java         |  6 +-
 10 files changed, 159 insertions(+), 239 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/54fb9c6a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicates.java b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicates.java
index f0189e2..e6705ef 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicates.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicates.java
@@ -20,6 +20,8 @@ package org.apache.brooklyn.core.typereg;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.util.Set;
+
 import javax.annotation.Nullable;
 
 import org.apache.brooklyn.api.entity.Application;
@@ -220,40 +222,82 @@ public class RegisteredTypePredicates {
     }
 
     public static <T> Predicate<RegisteredType> anySuperType(final Predicate<Object> filter) {
-        return new AnySuperTypeMatches(filter);
+        return new AnySuperTypeSatisfies(filter);
+    }
+    /** True for any {@link RegisteredType} which has a type ancestor (or self)
+     * registered type which is equal to the given {@link RegisteredType} */
+    public static Predicate<RegisteredType> subtypeOf(final RegisteredType filter) {
+        return anySuperType(Predicates.equalTo(filter));
     }
+    /** True for any {@link RegisteredType} which has a type ancestor
+     * class which is equal to or a subtype of the given class */
     public static Predicate<RegisteredType> subtypeOf(final Class<?> filter) {
         // the assignableFrom predicate checks if this class is assignable from the subsequent *input*.
         // in other words, we're checking if any input is a subtype of this class
-        return anySuperType(new Predicate<Object>() {
-            @Override
-            public boolean apply(Object input) {
-                if (!(input instanceof Class)) return false;
-                return filter.isAssignableFrom((Class<?>)input);
-            }
-        });
+        return anySuperType(new IsSubtypeOfClass(filter));
     }
+    private static class IsSubtypeOfClass implements Predicate<Object> {
+        private Class<?> filter;
+        public IsSubtypeOfClass(Class<?> filter) {
+            this.filter = filter;
+        }
+        @Override
+        public boolean apply(Object input) {
+            if (!(input instanceof Class)) return false;
+            return filter.isAssignableFrom((Class<?>)input);
+        }
+    }
+    /** True for any {@link RegisteredType} which has a type ancestor (or self)
+     * whose registered type name or ID equals the string, or class name equals the string */
     public static Predicate<RegisteredType> subtypeOf(final String filter) {
-        // the assignableFrom predicate checks if this class is assignable from the subsequent *input*.
-        // in other words, we're checking if any input is a subtype of this class
-        return anySuperType(new Predicate<Object>() {
-            @Override
-            public boolean apply(Object input) {
-                if (input instanceof Class) input = ((Class<?>)input).getName();
-                return filter.equals(input);
+        return anySuperType(new EqualsClassOrTypeName(filter));
+    }
+    private static class EqualsClassOrTypeName implements Predicate<Object> {
+        private String filter;
+        public EqualsClassOrTypeName(String typeOrClassOrName) {
+            this.filter = typeOrClassOrName;
+        }
+        @Override
+        public boolean apply(Object input) {
+            if (input instanceof RegisteredType) {
+                return ((RegisteredType)input).getSymbolicName().equals(filter) || ((RegisteredType)input).getSymbolicName().equals(filter);
             }
-        });
+            if (input instanceof Class) input = ((Class<?>)input).getName();
+            return filter.equals(input);
+        }
     }
     
+    /** @deprecated since 0.13.0 use {@link AnySuperTypeSatisfies}, kept for persistence compatibility */
+    @SuppressWarnings("unused")
+    @Deprecated
     private static class AnySuperTypeMatches implements Predicate<RegisteredType> {
+        private final Predicate<Class<?>> filter;
+        
+        private AnySuperTypeMatches(Predicate<Class<?>> filter) {
+            this.filter = filter;
+        }
+        @Override
+        public boolean apply(@Nullable RegisteredType item) {
+            if (item==null) return false;
+            
+            Set<Object> candidateTypes = item.getSuperTypes();
+            for (Object st: candidateTypes) {
+                if (st instanceof Class && filter.apply((Class<?>)st)) return true;
+            }
+            return false;
+        }
+    }
+    
+    private static class AnySuperTypeSatisfies implements Predicate<RegisteredType> {
         private final Predicate<Object> filter;
         
-        private AnySuperTypeMatches(Predicate<Object> filter) {
+        private AnySuperTypeSatisfies(Predicate<Object> filter) {
             this.filter = filter;
         }
         @Override
         public boolean apply(@Nullable RegisteredType item) {
             if (item==null) return false;
+            if (filter.apply(item)) return true;
             return RegisteredTypes.isAnyTypeOrSuper(item.getSuperTypes(), filter);
         }
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/54fb9c6a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctConfigSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctConfigSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctConfigSummary.java
deleted file mode 100644
index 4bba4b2..0000000
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctConfigSummary.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.rest.domain;
-
-import java.net.URI;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.brooklyn.config.ConfigKey;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.common.collect.ImmutableMap;
-
-public class AdjunctConfigSummary extends ConfigSummary {
-
-    private static final long serialVersionUID = 4339330833863794513L;
-
-    @JsonInclude(Include.NON_NULL)
-    private final Map<String, URI> links;
-
-    // json deserialization
-    AdjunctConfigSummary() {
-        links = null;
-    }
-
-    public AdjunctConfigSummary(
-            @JsonProperty("name") String name,
-            @JsonProperty("type") String type,
-            @JsonProperty("description") String description,
-            @JsonProperty("defaultValue") Object defaultValue,
-            @JsonProperty("reconfigurable") boolean reconfigurable,
-            @JsonProperty("links") Map<String, URI> links) {
-        super(name, type, description, defaultValue, reconfigurable, null, null, null);
-        this.links = (links == null) ? ImmutableMap.<String, URI>of() : ImmutableMap.copyOf(links);
-    }
-
-    public AdjunctConfigSummary(ConfigKey<?> config, String label, Double priority, Map<String, URI> links) {
-        super(config, label, priority);
-        this.links = links != null ? ImmutableMap.copyOf(links) : null;
-    }
-
-    @Override
-    public Map<String, URI> getLinks() {
-        return links;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof AdjunctConfigSummary)) return false;
-        if (!super.equals(o)) return false;
-        AdjunctConfigSummary that = (AdjunctConfigSummary) o;
-        return Objects.equals(links, that.links);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(super.hashCode(), links);
-    }
-
-    @Override
-    public String toString() {
-        return "EnricherConfigSummary{" +
-                "links=" + links +
-                '}';
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/54fb9c6a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ConfigSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ConfigSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ConfigSummary.java
index 6d9ceb3..77bf5fd 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ConfigSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ConfigSummary.java
@@ -29,16 +29,19 @@ import javax.annotation.Nullable;
 
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.util.collections.Jsonya;
+import org.apache.brooklyn.util.text.StringPredicates;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.google.common.base.Function;
+import com.google.common.base.Predicates;
 import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
-public abstract class ConfigSummary implements HasName, Serializable {
+public class ConfigSummary implements HasName, Serializable {
 
     private static final long serialVersionUID = -2831796487073496730L;
 
@@ -54,12 +57,19 @@ public abstract class ConfigSummary implements HasName, Serializable {
     private final String label;
     @JsonInclude(Include.NON_NULL)
     private final Double priority;
-    @JsonInclude(Include.NON_NULL)
+    @JsonInclude(Include.NON_EMPTY)
     private final List<Map<String, String>> possibleValues;
+    @JsonInclude(Include.NON_NULL)
+    private final Boolean pinned;
+    @JsonInclude(Include.NON_EMPTY)
+    private final List<String> constraints;
+
+    @JsonInclude(Include.NON_EMPTY)
+    private final Map<String, URI> links;
 
     // json deserialization
     ConfigSummary() {
-        this(null, null, null, null, false, null, null, null);
+        this(null, null, null, null, false, null, null, null, null, null, null);
     }
     
     protected ConfigSummary(
@@ -70,7 +80,10 @@ public abstract class ConfigSummary implements HasName, Serializable {
             @JsonProperty("reconfigurable") boolean reconfigurable,
             @JsonProperty("label") String label,
             @JsonProperty("priority") Double priority,
-            @JsonProperty("possibleValues") List<Map<String, String>> possibleValues) {
+            @JsonProperty("possibleValues") List<Map<String, String>> possibleValues,
+            @JsonProperty("pinned") Boolean pinned,
+            @JsonProperty("constraints") List<String> constraints,
+            @JsonProperty("links") Map<String, URI> links) {
         this.name = name;
         this.type = type;
         this.description = description;
@@ -79,14 +92,12 @@ public abstract class ConfigSummary implements HasName, Serializable {
         this.label = label;
         this.priority = priority;
         this.possibleValues = possibleValues;
+        this.pinned = pinned;
+        this.constraints = (constraints == null) ? ImmutableList.<String>of() : ImmutableList.copyOf(constraints);
+        this.links = (links == null) ? ImmutableMap.<String, URI>of() : ImmutableMap.copyOf(links);
     }
 
-    protected ConfigSummary(ConfigKey<?> config) {
-        this(config, null, null);
-    }
-
-    @SuppressWarnings("rawtypes")
-    protected ConfigSummary(ConfigKey<?> config, String label, Double priority) {
+    public ConfigSummary(ConfigKey<?> config, String label, Double priority, Boolean pinned, Map<String, URI> links) {
         this.name = config.getName();
         this.description = config.getDescription();
         this.reconfigurable = config.isReconfigurable();
@@ -97,15 +108,19 @@ public abstract class ConfigSummary implements HasName, Serializable {
          */
         this.label = label;
         this.priority = priority;
+        this.pinned = pinned;
+        this.constraints = !config.getConstraint().equals(Predicates.alwaysTrue())
+                ? ImmutableList.of((config.getConstraint().getClass().equals(StringPredicates.isNonBlank().getClass()) ? "required" : config.getConstraint().toString()))
+                : ImmutableList.<String>of();
         if (config.getType().isEnum()) {
             this.type = Enum.class.getName();
-            this.defaultValue = (config.getDefaultValue() == null) ? null : ((Enum) config.getDefaultValue()).name();
+            this.defaultValue = (config.getDefaultValue() == null) ? null : ((Enum<?>) config.getDefaultValue()).name();
             this.possibleValues = FluentIterable
-                    .from(Arrays.asList((Enum[])(config.getType().getEnumConstants())))
-                    .transform(new Function<Enum, Map<String, String>>() {
+                    .from(Arrays.asList((Enum<?>[])(config.getType().getEnumConstants())))
+                    .transform(new Function<Enum<?>, Map<String, String>>() {
                         @Nullable
                         @Override
-                        public Map<String, String> apply(@Nullable Enum input) {
+                        public Map<String, String> apply(@Nullable Enum<?> input) {
                             return ImmutableMap.of(
                                     "value", input != null ? input.name() : null,
                                    "description", input != null ? input.toString() : null);
@@ -116,6 +131,7 @@ public abstract class ConfigSummary implements HasName, Serializable {
             this.defaultValue = Jsonya.convertToJsonPrimitive(config.getDefaultValue());
             this.possibleValues = null;
         }
+        this.links = (links == null) ? ImmutableMap.<String, URI>of() : ImmutableMap.copyOf(links);
     }
 
     @Override
@@ -152,7 +168,17 @@ public abstract class ConfigSummary implements HasName, Serializable {
         return possibleValues;
     }
 
-    public abstract Map<String, URI> getLinks();
+    public Boolean isPinned() {
+        return pinned;
+    }
+
+    public List<String> getConstraints() {
+        return constraints;
+    }
+
+    public Map<String, URI> getLinks() {
+        return links;
+    }
 
     @Override
     public boolean equals(Object o) {
@@ -166,12 +192,16 @@ public abstract class ConfigSummary implements HasName, Serializable {
                 Objects.equals(description, that.description) &&
                 Objects.equals(label, that.label) &&
                 Objects.equals(priority, that.priority) &&
-                Objects.equals(possibleValues, that.possibleValues);
+                Objects.equals(possibleValues, that.possibleValues) &&
+                Objects.equals(pinned, that.pinned) &&
+                Objects.equals(constraints, that.constraints) &&
+                Objects.equals(links, that.links);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(name, type, defaultValue, description, reconfigurable, label, priority, possibleValues);
+        return Objects.hash(name, type, defaultValue, description, reconfigurable, label, priority, 
+            possibleValues, pinned, constraints);
     }
 
     @Override
@@ -185,6 +215,8 @@ public abstract class ConfigSummary implements HasName, Serializable {
                 ", label='" + label + '\'' +
                 ", priority=" + priority +
                 ", possibleValues=" + possibleValues +
+                ", pinned=" + pinned +
+                ", constraints=" + constraints +
                 '}';
     }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/54fb9c6a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java
index c868fb8..de8e4d3 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java
@@ -23,8 +23,9 @@ import java.util.Map;
 
 import org.apache.brooklyn.config.ConfigKey;
 
-// TODO remove? this class has no value over its super
-public class EnricherConfigSummary extends AdjunctConfigSummary {
+/** @deprecated since 0.13.0 no different to ConfigSummary, use that */
+@Deprecated
+public class EnricherConfigSummary extends ConfigSummary {
 
     private static final long serialVersionUID = 4339330833863794513L;
 
@@ -32,12 +33,7 @@ public class EnricherConfigSummary extends AdjunctConfigSummary {
     private EnricherConfigSummary() {}
     
     public EnricherConfigSummary(ConfigKey<?> config, String label, Double priority, Map<String, URI> links) {
-        super(config, label, priority, links);
-    }
-
-    public EnricherConfigSummary(String name, String type, String description, Object defaultValue, boolean reconfigurable,
-        Map<String, URI> links) {
-        super(name, type, description, defaultValue, reconfigurable, links);
+        super(config, label, priority, null, links);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/54fb9c6a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EntityConfigSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EntityConfigSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EntityConfigSummary.java
index aeba294..131ae65 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EntityConfigSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EntityConfigSummary.java
@@ -21,30 +21,17 @@ package org.apache.brooklyn.rest.domain;
 import java.net.URI;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 
 import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.util.text.StringPredicates;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 
+/** @deprecated since 0.13.0 no different to {@link ConfigSummary}, use that */
+@Deprecated
 public class EntityConfigSummary extends ConfigSummary {
 
     private static final long serialVersionUID = -1336134336883426030L;
 
-    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
-    private final Boolean pinned;
-
-    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
-    private final List<String> constraints;
-
-    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
-    private final Map<String, URI> links;
-
     public EntityConfigSummary(
             @JsonProperty("name") String name,
             @JsonProperty("type") String type,
@@ -57,57 +44,11 @@ public class EntityConfigSummary extends ConfigSummary {
             @JsonProperty("pinned") Boolean pinned,
             @JsonProperty("constraints") List<String> constraints,
             @JsonProperty("links") Map<String, URI> links) {
-        super(name, type, description, defaultValue, reconfigurable, label, priority, possibleValues);
-        this.pinned = pinned;
-        this.constraints = (constraints == null) ? ImmutableList.<String>of() : ImmutableList.copyOf(constraints);
-        this.links = (links == null) ? ImmutableMap.<String, URI>of() : ImmutableMap.copyOf(links);
+        super(name, type, description, defaultValue, reconfigurable, label, priority, possibleValues, pinned, constraints, links);
     }
 
     public EntityConfigSummary(ConfigKey<?> config, String label, Double priority, Boolean pinned, Map<String, URI> links) {
-        super(config, label, priority);
-        this.pinned = pinned;
-        this.constraints = !config.getConstraint().equals(Predicates.alwaysTrue())
-                ? ImmutableList.of((config.getConstraint().getClass().equals(StringPredicates.isNonBlank().getClass()) ? "required" : config.getConstraint().toString()))
-                : ImmutableList.<String>of();
-        this.links = links != null ? ImmutableMap.copyOf(links) : null;
-    }
-
-    public Boolean isPinned() {
-        return pinned;
-    }
-
-    public List<String> getConstraints() {
-        return constraints;
+        super(config, label, priority, pinned, links);
     }
 
-    @Override
-    public Map<String, URI> getLinks() {
-        return links;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        if (!super.equals(o)) return false;
-        EntityConfigSummary that = (EntityConfigSummary) o;
-        if (pinned != that.pinned) return false;
-        if (constraints != null ? !constraints.equals(that.constraints) : that.constraints != null) return false;
-        return links != null ? links.equals(that.links) : that.links == null;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(super.hashCode(), links);
-    }
-
-    @Override
-    public String toString() {
-        return "EntityConfigSummary{" +
-                "name='" + getName() + '\'' +
-                ", type='" + getType() + '\'' +
-                ", description='" + getDescription() + '\'' +
-                "links=" + links +
-                '}';
-    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/54fb9c6a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/LocationConfigSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/LocationConfigSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/LocationConfigSummary.java
index 21d5861..240921b 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/LocationConfigSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/LocationConfigSummary.java
@@ -21,18 +21,17 @@ package org.apache.brooklyn.rest.domain;
 import java.net.URI;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.google.common.collect.ImmutableMap;
 
+/** @deprecated since 0.13.0 no different to ConfigSummary, use that */
+@Deprecated
 public class LocationConfigSummary extends ConfigSummary {
 
     private static final long serialVersionUID = 2232321501735217002L;
 
-    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
-    private final Map<String, URI> links;
+    @SuppressWarnings("unused") // json deserialization
+    private LocationConfigSummary() {}
 
     public LocationConfigSummary(
             @JsonProperty("name") String name,
@@ -43,34 +42,11 @@ public class LocationConfigSummary extends ConfigSummary {
             @JsonProperty("label") String label,
             @JsonProperty("priority") Double priority,
             @JsonProperty("possibleValues") List<Map<String, String>> possibleValues,
+            @JsonProperty("pinned") Boolean pinned,
+            @JsonProperty("constraints") List<String> constraints,
             @JsonProperty("links") Map<String, URI> links) {
-        super(name, type, description, defaultValue, reconfigurable, label, priority, possibleValues);
-        this.links = (links == null) ? ImmutableMap.<String, URI>of() : ImmutableMap.copyOf(links);
+        super(name, type, description, defaultValue, reconfigurable, label, priority, possibleValues,
+            pinned, constraints, links);
     }
 
-    @Override
-    public Map<String, URI> getLinks() {
-        return links;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof LocationConfigSummary)) return false;
-        if (!super.equals(o)) return false;
-        LocationConfigSummary that = (LocationConfigSummary) o;
-        return Objects.equals(links, that.links);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(super.hashCode(), links);
-    }
-
-    @Override
-    public String toString() {
-        return "LocationConfigSummary{" +
-                "links=" + links +
-                '}';
-    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/54fb9c6a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java
index 5dfb898..3386045 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java
@@ -19,25 +19,39 @@
 package org.apache.brooklyn.rest.domain;
 
 import java.net.URI;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.brooklyn.config.ConfigKey;
 
-//TODO remove? this class has no value over its super
-public class PolicyConfigSummary extends AdjunctConfigSummary {
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/** @deprecated since 0.13.0 no different to ConfigSummary, use that */
+@Deprecated
+public class PolicyConfigSummary extends ConfigSummary {
 
     private static final long serialVersionUID = 4339330833863794513L;
 
     @SuppressWarnings("unused") // json deserialization
     private PolicyConfigSummary() {}
     
-    public PolicyConfigSummary(ConfigKey<?> config, String label, Double priority, Map<String, URI> links) {
-        super(config, label, priority, links);
+    public PolicyConfigSummary(
+            @JsonProperty("name") String name,
+            @JsonProperty("type") String type,
+            @JsonProperty("description") String description,
+            @JsonProperty("defaultValue") Object defaultValue,
+            @JsonProperty("reconfigurable") boolean reconfigurable,
+            @JsonProperty("label") String label,
+            @JsonProperty("priority") Double priority,
+            @JsonProperty("possibleValues") List<Map<String, String>> possibleValues,
+            @JsonProperty("pinned") Boolean pinned,
+            @JsonProperty("constraints") List<String> constraints,
+            @JsonProperty("links") Map<String, URI> links) {
+        super(name, type, description, defaultValue, reconfigurable, label, priority, possibleValues, pinned, constraints, links);
     }
-
-    public PolicyConfigSummary(String name, String type, String description, Object defaultValue, boolean reconfigurable,
-        Map<String, URI> links) {
-        super(name, type, description, defaultValue, reconfigurable, links);
+    
+    public PolicyConfigSummary(ConfigKey<?> config, String label, Double priority, Map<String, URI> links) {
+        super(config, label, priority, null, links);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/54fb9c6a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
index 6bf3f19..73b2831 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
@@ -41,7 +41,7 @@ import org.apache.brooklyn.rest.api.ApplicationApi;
 import org.apache.brooklyn.rest.api.CatalogApi;
 import org.apache.brooklyn.rest.api.EntityApi;
 import org.apache.brooklyn.rest.api.EntityConfigApi;
-import org.apache.brooklyn.rest.domain.AdjunctConfigSummary;
+import org.apache.brooklyn.rest.domain.ConfigSummary;
 import org.apache.brooklyn.rest.domain.EnricherConfigSummary;
 import org.apache.brooklyn.rest.domain.EntityConfigSummary;
 import org.apache.brooklyn.rest.domain.EntitySummary;
@@ -125,8 +125,8 @@ public class EntityTransformer {
         return new EntityConfigSummary(config, label, priority, pinned, mapOfLinks);
     }
 
-    public static AdjunctConfigSummary adjunctConfigSummary(ConfigKey<?> config, String label, Double priority, Map<String, URI> links) {
-        return new AdjunctConfigSummary(config, label, priority, links);
+    public static ConfigSummary configSummary(ConfigKey<?> config, String label, Double priority, Boolean pinned, Map<String, URI> links) {
+        return new ConfigSummary(config, label, priority, pinned, links);
     }
 
     public static PolicyConfigSummary policyConfigSummary(ConfigKey<?> config, String label, Double priority, Map<String, URI> links) {
@@ -198,9 +198,10 @@ public class EntityTransformer {
         return entityConfigSummary(input.getConfigKey(), input.getLabel(), priority, input.isPinned(), null);
     }
 
-    public static AdjunctConfigSummary adjunctConfigSummary(SpecParameter<?> input) {
+    public static ConfigSummary configSummary(SpecParameter<?> input) {
+        // could increment priority, or take from annotation, or introduce new field
         Double priority = input.isPinned() ? Double.valueOf(1d) : null;
-        return policyConfigSummary(input.getConfigKey(), input.getLabel(), priority, null);
+        return configSummary(input.getConfigKey(), input.getLabel(), priority, input.isPinned(), null);
     }
 
     public static PolicyConfigSummary policyConfigSummary(SpecParameter<?> input) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/54fb9c6a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java
index 8dad949..d6ba1d3 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/PolicyTransformer.java
@@ -96,7 +96,8 @@ public class PolicyTransformer {
 
         return new PolicyConfigSummary(config.getName(), config.getTypeName(), config.getDescription(), 
                 PolicyConfigResource.getStringValueForDisplay(utils, policy, config.getDefaultValue()), 
-                config.isReconfigurable(), 
+                config.isReconfigurable(),
+                null, null, null, null, null, 
                 links);
     }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/54fb9c6a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
index 4c9ccb2..e37ee6a 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/transform/TypeTransformer.java
@@ -50,9 +50,9 @@ import org.apache.brooklyn.core.objs.BrooklynTypes;
 import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
 import org.apache.brooklyn.core.typereg.RegisteredTypes;
 import org.apache.brooklyn.rest.api.TypeApi;
-import org.apache.brooklyn.rest.domain.AdjunctConfigSummary;
 import org.apache.brooklyn.rest.domain.BundleInstallationRestResult;
 import org.apache.brooklyn.rest.domain.BundleSummary;
+import org.apache.brooklyn.rest.domain.ConfigSummary;
 import org.apache.brooklyn.rest.domain.EffectorSummary;
 import org.apache.brooklyn.rest.domain.EntityConfigSummary;
 import org.apache.brooklyn.rest.domain.SensorSummary;
@@ -96,11 +96,11 @@ public class TypeTransformer {
                     RegisteredTypes.isSubtypeOf(item, Policy.class) || RegisteredTypes.isSubtypeOf(item, Enricher.class) || RegisteredTypes.isSubtypeOf(item, Feed.class)
                     ) {
                 try {
-                    Set<AdjunctConfigSummary> config = Sets.newLinkedHashSet();
+                    Set<ConfigSummary> config = Sets.newLinkedHashSet();
                     
                     AbstractBrooklynObjectSpec<?,?> spec = b.getTypeRegistry().createSpec(item, null, null);
                     for (final SpecParameter<?> input : spec.getParameters()){
-                        config.add(EntityTransformer.adjunctConfigSummary(input));
+                        config.add(EntityTransformer.configSummary(input));
                     }
                     
                     result.setExtraField("config", config);


[05/11] brooklyn-server git commit: Merge branch 'master' into bundle-rest-api

Posted by he...@apache.org.
Merge branch 'master' into bundle-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/831db4ac
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/831db4ac
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/831db4ac

Branch: refs/heads/master
Commit: 831db4ac800b7418bc258609d5838949818814ff
Parents: 1cd6bf4 3ac961d
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri Sep 22 10:20:17 2017 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Sep 22 10:20:17 2017 +0100

----------------------------------------------------------------------
 api/pom.xml                                     |   2 +-
 .../brooklyn/api/objs/HighlightTuple.java       |  23 +-
 .../brooklyn/api/typereg/RegisteredType.java    |   5 +-
 camp/camp-base/pom.xml                          |   2 +-
 camp/camp-brooklyn/pom.xml                      |   2 +-
 .../camp/brooklyn/AbstractYamlTest.java         |  31 +-
 .../brooklyn/EmptySoftwareProcessYamlTest.java  |  94 +++---
 .../camp/brooklyn/FunctionSensorYamlTest.java   | 123 ++++++++
 .../VanillaSoftwareProcessYamlTest.java         | 299 +++++++++++++++++++
 .../CatalogOsgiVersionMoreEntityRebindTest.java |  10 +-
 .../CatalogOsgiVersionMoreEntityTest.java       |   3 +
 .../java-web-app-and-db-with-function-2.yaml    |   9 +-
 .../java-web-app-and-db-with-function.yaml      |   7 +-
 ...-java-web-app-spec-and-db-with-function.yaml |   7 +-
 .../test/resources/visitors-creation-script.sql |  19 +-
 camp/camp-server/pom.xml                        |   2 +-
 camp/pom.xml                                    |   2 +-
 core/pom.xml                                    |   2 +-
 .../core/BrooklynFeatureEnablement.java         |   2 +-
 .../apache/brooklyn/core/BrooklynVersion.java   |   2 +-
 .../catalog/internal/CatalogInitialization.java |   1 +
 .../brooklyn/core/config/BasicConfigKey.java    |  41 ++-
 .../core/enricher/AbstractEnricher.java         |  19 +-
 .../brooklyn/core/entity/AbstractEntity.java    |   6 +-
 .../apache/brooklyn/core/entity/Attributes.java |  10 +-
 .../core/entity/BrooklynConfigKeys.java         | 124 ++++++--
 .../entity/lifecycle/ServiceStateLogic.java     |   3 +
 .../apache/brooklyn/core/feed/AbstractFeed.java |  16 +-
 .../core/feed/AttributePollHandler.java         |   2 +
 .../org/apache/brooklyn/core/feed/Poller.java   |  20 +-
 .../BasicExternalConfigSupplierRegistry.java    |  12 +-
 .../mgmt/persist/BrooklynPersistenceUtils.java  |   2 +-
 .../core/mgmt/rebind/RebindIteration.java       |   5 +-
 .../core/objs/AbstractEntityAdjunct.java        | 103 ++++++-
 .../core/sensor/BasicAttributeSensor.java       |  11 +-
 .../brooklyn/core/sensor/BasicSensor.java       |  19 +-
 .../apache/brooklyn/core/sensor/Sensors.java    |   9 +-
 .../core/sensor/function/FunctionSensor.java    |  89 ++++++
 .../core/typereg/BasicBrooklynTypeRegistry.java |  20 +-
 .../typereg/BasicTypeImplementationPlan.java    |   4 +-
 .../typereg/RegisteredTypeLoadingContexts.java  |   6 +-
 .../enricher/stock/AbstractAggregator.java      |  49 ++-
 .../stock/AbstractMultipleSensorAggregator.java |   2 -
 .../enricher/stock/AbstractTransformer.java     |  21 +-
 .../stock/AbstractTypeTransformingEnricher.java |   3 +
 .../brooklyn/enricher/stock/Aggregator.java     |  20 +-
 .../brooklyn/enricher/stock/Combiner.java       |  35 ++-
 .../apache/brooklyn/enricher/stock/Joiner.java  |  18 +-
 .../brooklyn/enricher/stock/MapAggregator.java  |  11 +-
 .../enricher/stock/PercentageEnricher.java      |   7 +-
 .../brooklyn/enricher/stock/Propagator.java     |   6 +
 .../brooklyn/enricher/stock/Transformer.java    |  23 +-
 .../brooklyn/enricher/stock/UpdatingMap.java    |  25 +-
 .../YamlRollingTimeWindowMeanEnricher.java      |  13 +-
 .../stock/YamlTimeWeightedDeltaEnricher.java    |   7 +-
 .../enricher/stock/reducer/Reducer.java         |  27 +-
 .../brooklyn/entity/group/AbstractGroup.java    |  13 +-
 .../group/AbstractMembershipTrackingPolicy.java |  23 +-
 .../apache/brooklyn/entity/group/Cluster.java   |   2 +
 .../brooklyn/entity/group/DynamicCluster.java   |  21 +-
 .../brooklyn/entity/group/DynamicFabric.java    |  23 +-
 .../brooklyn/entity/group/DynamicGroup.java     |   6 +-
 .../SshCommandMembershipTrackingPolicy.java     |  15 +-
 .../brooklyn/entity/stock/BasicStartable.java   |   6 +-
 .../brooklyn/entity/stock/DelegateEntity.java   |   7 +-
 core/src/main/resources/catalog.bom             |  57 +++-
 .../entity/RecordingSensorEventListener.java    |  12 +
 .../core/mgmt/osgi/OsgiStandaloneTest.java      |   4 +-
 .../core/policy/basic/BasicPolicyTest.java      |   9 +-
 .../sensor/function/FunctionSensorTest.java     |  77 +++++
 .../core/test/entity/TestEntityImpl.java        |   2 +-
 .../stock/TransformingEnricherTest.java         |   7 +-
 .../util/core/internal/ssh/ExecCmdAsserts.java  |  12 +-
 core/src/test/resources/catalog.bom             |   2 +-
 karaf/commands/pom.xml                          |   2 +-
 karaf/features/pom.xml                          |   2 +-
 karaf/httpcomponent-extension/pom.xml           |   2 +-
 karaf/init/pom.xml                              |   2 +-
 .../init/src/main/resources/catalog-classes.bom | 107 ++++++-
 karaf/jetty-config/pom.xml                      |   2 +-
 karaf/pom.xml                                   |   2 +-
 karaf/start/pom.xml                             |   2 +-
 launcher-common/pom.xml                         |   2 +-
 launcher/pom.xml                                |   2 +-
 .../java-web-app-and-db-with-function.yaml      |   3 +-
 .../test/resources/visitors-creation-script.sql |  19 +-
 locations/container/pom.xml                     |   2 +-
 .../resources/generic-application.tests.bom     |   8 +-
 .../src/test/resources/generic.tests.bom        |   2 +-
 locations/jclouds/pom.xml                       |   2 +-
 .../location/jclouds/JcloudsLocation.java       |  73 ++++-
 .../api/JcloudsLocationConfigPublic.java        |   6 +
 .../JcloudsMaxConcurrencyStubbedTest.java       | 223 ++++++++++++++
 logging/logback-includes/pom.xml                |   2 +-
 logging/logback-xml/pom.xml                     |   2 +-
 parent/pom.xml                                  |   2 +-
 policy/pom.xml                                  |   2 +-
 .../action/AbstractScheduledEffectorPolicy.java | 292 ++++++++++++++++++
 .../policy/action/PeriodicEffectorPolicy.java   | 116 +++++++
 .../policy/action/ScheduledEffectorPolicy.java  | 100 +++++++
 .../policy/autoscaling/AutoScalerPolicy.java    |  87 ++++--
 .../policy/ha/ConnectionFailureDetector.java    |  14 +-
 .../policy/ha/ServiceFailureDetector.java       |  16 +-
 .../brooklyn/policy/ha/ServiceReplacer.java     |  66 ++--
 .../brooklyn/policy/ha/ServiceRestarter.java    |  25 +-
 .../policy/ha/SshMachineFailureDetector.java    |   4 +-
 policy/src/main/resources/catalog.bom           |  92 ++++--
 .../action/AbstractEffectorPolicyTest.java      |  81 +++++
 .../action/PeriodicEffectorPolicyTest.java      | 123 ++++++++
 .../action/ScheduledEffectorPolicyTest.java     | 121 ++++++++
 .../action/ScheduledPolicyRebindTest.java       | 137 +++++++++
 .../AutoScalerPolicyPoolSizeTest.java           |  18 +-
 .../autoscaling/AutoScalerPolicyRebindTest.java |   5 +-
 pom.xml                                         |   4 +-
 rest/rest-api/pom.xml                           |   2 +-
 rest/rest-resources/pom.xml                     |   2 +-
 .../resources/EntityConfigResourceTest.java     |   2 +-
 .../testing/mocks/RestMockSimplePolicy.java     |   4 +-
 rest/rest-server/pom.xml                        |   2 +-
 server-cli/pom.xml                              |   2 +-
 .../main/java/org/apache/brooklyn/cli/Main.java |  28 +-
 .../main/resources/brooklyn/default.catalog.bom |   2 +-
 server-cli/src/main/resources/catalog.bom       |   2 +-
 software/base/pom.xml                           |   2 +-
 .../entity/brooklynnode/BrooklynNode.java       |   2 +-
 .../brooklynnode/BrooklynNodeSshDriver.java     |   2 +-
 .../brooklyn/entity/java/JmxmpSslSupport.java   |   2 +-
 .../apache/brooklyn/entity/java/UsesJava.java   |  10 +-
 .../apache/brooklyn/entity/java/UsesJmx.java    |  37 ++-
 .../entity/machine/pool/ServerPoolImpl.java     |   1 +
 .../software/base/AbstractVanillaProcess.java   |  21 +-
 .../software/base/EmptySoftwareProcessImpl.java |   4 +-
 .../entity/software/base/SoftwareProcess.java   |  37 ++-
 .../software/base/SoftwareProcessImpl.java      |   4 +-
 .../software/base/VanillaSoftwareProcess.java   |   5 +-
 .../base/VanillaSoftwareProcessImpl.java        |  16 +-
 .../base/VanillaSoftwareProcessSshDriver.java   |  12 +-
 .../software/base/VanillaWindowsProcess.java    |  22 +-
 software/base/src/main/resources/catalog.bom    |  17 +-
 .../brooklynnode/SelectMasterEffectorTest.java  |   2 +-
 software/winrm/pom.xml                          |   2 +-
 test-framework/pom.xml                          |   2 +-
 test-framework/src/main/resources/catalog.bom   |  12 +-
 test-support/pom.xml                            |   2 +-
 .../test/performance/PerformanceTestUtils.java  |  50 +++-
 .../osgi/com-example-entities/pom.xml           |   2 +-
 utils/common/dependencies/osgi/entities/pom.xml |   2 +-
 .../dependencies/osgi/more-entities-v1/pom.xml  |   2 +-
 .../osgi/more-entities-v2-evil-twin/pom.xml     |   2 +-
 .../dependencies/osgi/more-entities-v2/pom.xml  |   2 +-
 utils/common/pom.xml                            |   2 +-
 .../apache/brooklyn/util/guava/TypeTokens.java  |  38 ++-
 .../javalang/coerce/TypeCoercerExtensible.java  |  15 +-
 .../brooklyn/util/maven/MavenArtifactTest.java  |   4 +-
 utils/groovy/pom.xml                            |   2 +-
 utils/jmx/jmxmp-ssl-agent/pom.xml               |   2 +-
 utils/jmx/jmxrmi-agent/pom.xml                  |   2 +-
 utils/rest-swagger/pom.xml                      |   2 +-
 utils/rt-felix/pom.xml                          |   2 +-
 utils/test-support/pom.xml                      |   2 +-
 160 files changed, 3358 insertions(+), 538 deletions(-)
----------------------------------------------------------------------



[04/11] brooklyn-server git commit: Merge branch 'master' into bundle-rest-api

Posted by he...@apache.org.
Merge branch 'master' into bundle-rest-api

BrooklynCatalog api tidy minor conflict


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

Branch: refs/heads/master
Commit: 1cd6bf455d4cd0004a98881cc2d9a045bf2ffd1e
Parents: 9b33703 4795aac
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed Sep 13 12:53:30 2017 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Wed Sep 13 12:53:30 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/api/catalog/BrooklynCatalog.java   |  37 ++-
 .../api/mgmt/rebind/mementos/PolicyMemento.java |   3 +
 .../apache/brooklyn/api/objs/EntityAdjunct.java |   3 +
 .../brooklyn/api/objs/HighlightTuple.java       |  81 ++++++
 .../apache/brooklyn/core/BrooklynVersion.java   |  38 ++-
 .../catalog/internal/BasicBrooklynCatalog.java  |  70 ++++-
 .../core/catalog/internal/CatalogUtils.java     |   2 +-
 .../internal/AbstractManagementContext.java     |   2 +-
 .../persist/FileBasedStoreObjectAccessor.java   |  33 ++-
 .../core/mgmt/rebind/RebindIteration.java       |   1 +
 .../mgmt/rebind/dto/BasicPolicyMemento.java     |  17 +-
 .../mgmt/rebind/dto/MementosGenerators.java     |   4 +-
 .../core/objs/AbstractEntityAdjunct.java        |  30 +++
 .../core/typereg/BasicBrooklynTypeRegistry.java |  16 +-
 .../brooklyn/core/typereg/RegisteredTypes.java  |   9 +-
 .../core/policy/basic/BasicPolicyTest.java      |  18 ++
 karaf/features/src/main/feature/feature.xml     |   1 +
 .../brooklyn/launcher/osgi/OsgiLauncher.java    | 259 ++++---------------
 .../launcher/osgi/OsgiLauncherImpl.java         | 225 ++++++++++++++++
 .../resources/OSGI-INF/blueprint/blueprint.xml  |  12 +-
 karaf/pom.xml                                   |   1 +
 karaf/start/pom.xml                             |  75 ++++++
 .../osgi/start/OsgiLauncherCompleter.java       |  42 +++
 .../resources/OSGI-INF/blueprint/blueprint.xml  |  36 +++
 .../brooklyn/launcher/common/BasicLauncher.java |  35 ++-
 .../location/jclouds/JcloudsLocation.java       |  13 +-
 .../jclouds/JcloudsWinRmMachineLocation.java    |   5 +-
 .../jclouds/StubbedComputeServiceRegistry.java  |  26 +-
 .../autoscaling/AutoScalerPolicyRebindTest.java |  34 +++
 .../brooklyn/rest/domain/PolicySummary.java     |  15 +-
 .../rest/transform/PolicyTransformer.java       |   7 +-
 .../rest/resources/PolicyResourceTest.java      |  24 +-
 .../testing/mocks/RestMockSimplePolicy.java     |   3 +
 33 files changed, 914 insertions(+), 263 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1cd6bf45/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java
----------------------------------------------------------------------
diff --cc api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java
index 9e19a81,9c7ed66..fa1d55e
--- a/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java
+++ b/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java
@@@ -54,15 -63,34 +63,33 @@@ public interface BrooklynCatalog 
      void deleteCatalogItem(String symbolicName, String version);
  
      /** variant of {@link #getCatalogItem(String, String)} which checks (and casts) type for convenience
-      * (returns null if type does not match) */
+      * (returns null if type does not match)
+      * @deprecated since 0.12.0 use {@link BrooklynTypeRegistry} instead */ 
+     @Deprecated
      <T,SpecT> CatalogItem<T,SpecT> getCatalogItem(Class<T> type, String symbolicName, String version);
+     
+     /** As non-legacy method but only looking in legacy catalog
+      * @deprecated since 0.12.0 only provided to allow TypeRegistry to see the legacy items */
+     <T,SpecT> CatalogItem<T,SpecT> getCatalogItemLegacy(Class<T> type, String symbolicName, String version);
  
-     /** @return All items in the catalog */
+     /** @return All items in the catalog
+      * @deprecated since 0.12.0 use {@link BrooklynTypeRegistry} instead */ 
+     @Deprecated
      <T,SpecT> Iterable<CatalogItem<T,SpecT>> getCatalogItems();
  
-     /** convenience for filtering items in the catalog; see CatalogPredicates for useful filters */
+     /** As non-legacy method but only looking in legacy catalog
+      * @deprecated since 0.12.0 only provided to allow TypeRegistry to see the legacy items */
+     @Deprecated
+     <T,SpecT> Iterable<CatalogItem<T,SpecT>> getCatalogItemsLegacy();
+ 
+     /** convenience for filtering items in the catalog; see CatalogPredicates for useful filters
+      * @deprecated since 0.12.0 use {@link BrooklynTypeRegistry} instead */ 
 -    @Deprecated
      <T,SpecT> Iterable<CatalogItem<T,SpecT>> getCatalogItems(Predicate<? super CatalogItem<T,SpecT>> filter);
  
+     /** As non-legacy method but only looking in legacy catalog
+      * @deprecated since 0.12.0 only provided to allow TypeRegistry to see the legacy items */
+     <T,SpecT> Iterable<CatalogItem<T,SpecT>> getCatalogItemsLegacy(Predicate<? super CatalogItem<T,SpecT>> filter);
+ 
      /** persists the catalog item to the object store, if persistence is enabled */
      public void persist(CatalogItem<?, ?> catalogItem);
  

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1cd6bf45/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1cd6bf45/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
----------------------------------------------------------------------


[03/11] brooklyn-server git commit: REST API for bundles, types, and subtypes

Posted by he...@apache.org.
REST API for bundles, types, and subtypes

also tweaks to other classes to make them more usable, eg VersionedName comparables and bundle removal giving an OsgiBundleInstallationResult

test in BundleAndTypeAndSubtypeResourcesTest based on CatalogResourceTest, basically giving parity in terms of functionality and test coverage


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

Branch: refs/heads/master
Commit: 9b337035992f77c7e156bba4d3b3ce35438ca180
Parents: 36de666
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Sep 7 16:43:45 2017 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu Sep 7 17:15:00 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/api/catalog/BrooklynCatalog.java   |    1 -
 .../typereg/RegisteredTypeLoadingContext.java   |    2 +-
 .../catalog/internal/BasicBrooklynCatalog.java  |  106 +-
 .../core/mgmt/ha/OsgiArchiveInstaller.java      |   18 +-
 .../mgmt/ha/OsgiBundleInstallationResult.java   |   21 +-
 .../brooklyn/core/mgmt/ha/OsgiManager.java      |   89 +-
 .../core/typereg/RegisteredTypePredicates.java  |   54 +-
 .../brooklyn/core/typereg/RegisteredTypes.java  |   43 +-
 .../org/apache/brooklyn/rest/api/BundleApi.java |  158 +++
 .../apache/brooklyn/rest/api/CatalogApi.java    |    3 +
 .../apache/brooklyn/rest/api/SubtypeApi.java    |   96 ++
 .../org/apache/brooklyn/rest/api/TypeApi.java   |   94 ++
 .../rest/domain/AdjunctConfigSummary.java       |   85 ++
 .../domain/BundleInstallationRestResult.java    |   67 ++
 .../brooklyn/rest/domain/BundleSummary.java     |   99 ++
 .../brooklyn/rest/domain/ConfigSummary.java     |   17 +-
 .../rest/domain/EnricherConfigSummary.java      |   54 +-
 .../rest/domain/PolicyConfigSummary.java        |   53 +-
 .../brooklyn/rest/domain/TaskSummary.java       |    9 +-
 .../apache/brooklyn/rest/domain/TypeDetail.java |   86 ++
 .../brooklyn/rest/domain/TypeSummary.java       |  287 +++++
 .../apache/brooklyn/rest/BrooklynRestApi.java   |    8 +-
 .../brooklyn/rest/resources/BundleResource.java |  186 +++
 .../rest/resources/CatalogResource.java         |   40 +-
 .../rest/resources/SubtypeResource.java         |   82 ++
 .../brooklyn/rest/resources/TypeResource.java   |  186 +++
 .../rest/transform/EntityTransformer.java       |   25 +-
 .../rest/transform/TypeTransformer.java         |  194 ++++
 .../BundleAndTypeAndSubtypeResourcesTest.java   | 1093 ++++++++++++++++++
 .../brooklyn/util/osgi/VersionedName.java       |   24 +-
 30 files changed, 3062 insertions(+), 218 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java b/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java
index cbc5b2b..9e19a81 100644
--- a/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java
+++ b/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java
@@ -61,7 +61,6 @@ public interface BrooklynCatalog {
     <T,SpecT> Iterable<CatalogItem<T,SpecT>> getCatalogItems();
 
     /** convenience for filtering items in the catalog; see CatalogPredicates for useful filters */
-//    XXX
     <T,SpecT> Iterable<CatalogItem<T,SpecT>> getCatalogItems(Predicate<? super CatalogItem<T,SpecT>> filter);
 
     /** persists the catalog item to the object store, if persistence is enabled */

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredTypeLoadingContext.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredTypeLoadingContext.java b/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredTypeLoadingContext.java
index d37666e..925b6b1 100644
--- a/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredTypeLoadingContext.java
+++ b/api/src/main/java/org/apache/brooklyn/api/typereg/RegisteredTypeLoadingContext.java
@@ -44,7 +44,7 @@ public interface RegisteredTypeLoadingContext {
      * the instantiator can avoid recursive cycles */
     @Nonnull public Set<String> getAlreadyEncounteredTypes();
     
-    /** A loader to use, supplying additional search paths */
+    /** A loader to use, supplying preferred or additional bundles and search paths */
     @Nullable public BrooklynClassLoadingContext getLoader();
     
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/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 bfca5ca..0de825e 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
@@ -1381,43 +1381,8 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         Maybe<OsgiManager> osgiManager = ((ManagementContextInternal)mgmt).getOsgiManager();
         if (osgiManager.isPresent() && AUTO_WRAP_CATALOG_YAML_AS_BUNDLE) {
             // wrap in a bundle to be managed; need to get bundle and version from yaml
-            Map<?, ?> cm = BasicBrooklynCatalog.getCatalogMetadata(yaml);
-            VersionedName vn = BasicBrooklynCatalog.getVersionedName( cm, false );
-            if (vn==null) {
-                // for better legacy compatibiity, if id specified at root use that
-                String id = (String) cm.get("id");
-                if (Strings.isNonBlank(id)) {
-                    vn = VersionedName.fromString(id);
-                }
-                vn = new VersionedName(vn!=null && Strings.isNonBlank(vn.getSymbolicName()) ? vn.getSymbolicName() : "brooklyn-catalog-bom-"+Identifiers.makeRandomId(8), 
-                    vn!=null && vn.getVersionString()!=null ? vn.getVersionString() : getFirstAs(cm, String.class, "version").or(NO_VERSION));
-            }
-            log.debug("Wrapping supplied BOM as "+vn);
-            Manifest mf = new Manifest();
-            mf.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, vn.getSymbolicName());
-            mf.getMainAttributes().putValue(Constants.BUNDLE_VERSION, vn.getOsgiVersionString());
-            mf.getMainAttributes().putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
-            mf.getMainAttributes().putValue(Attributes.Name.MANIFEST_VERSION.toString(), OSGI_MANIFEST_VERSION_VALUE);
-            mf.getMainAttributes().putValue(BROOKLYN_WRAPPED_BOM_BUNDLE, Boolean.TRUE.toString());
-            
-            BundleMaker bm = new BundleMaker(mgmt);
-            File bf = bm.createTempBundle(vn.getSymbolicName(), mf, MutableMap.of(
-                new ZipEntry(CATALOG_BOM), (InputStream) new ByteArrayInputStream(yaml.getBytes())) );
-
-            OsgiBundleInstallationResult result = null;
-            try {
-                result = osgiManager.get().install(null, new FileInputStream(bf), true, true, forceUpdate).get();
-            } catch (FileNotFoundException e) {
-                throw Exceptions.propagate(e);
-            } finally {
-                bf.delete();
-            }
-            if (result.getCode().isError()) {
-                // rollback done by install call above
-                throw new IllegalStateException(result.getMessage());
-            }
-            uninstallEmptyWrapperBundles();
-            return toLegacyCatalogItems(result.getCatalogItemsInstalled());
+            OsgiBundleInstallationResult result = addItemsOsgi(yaml, forceUpdate, osgiManager);
+            return toLegacyCatalogItems(result.getTypesInstalled());
 
             // if all items pertaining to an older anonymous catalog.bom bundle have been overridden
             // we delete those later; see list of wrapper bundles kept in OsgiManager
@@ -1426,10 +1391,73 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         return addItems(yaml, null, forceUpdate);
     }
     
+    /** Like {@link #addItems(String, boolean)} but returning the {@link OsgiBundleInstallationResult} for use from new environments.
+     * If not using OSGi the bundle/code/etc fields are null but the types will always be set. */
+    @SuppressWarnings("deprecation")
+    public OsgiBundleInstallationResult addItemsBundleResult(String yaml, boolean forceUpdate) {
+        Maybe<OsgiManager> osgiManager = ((ManagementContextInternal)mgmt).getOsgiManager();
+        if (osgiManager.isPresent() && AUTO_WRAP_CATALOG_YAML_AS_BUNDLE) {
+            // wrap in a bundle to be managed; need to get bundle and version from yaml
+            return addItemsOsgi(yaml, forceUpdate, osgiManager);
+
+            // if all items pertaining to an older anonymous catalog.bom bundle have been overridden
+            // we delete those later; see list of wrapper bundles kept in OsgiManager
+        }
+        // fallback to non-OSGi for tests and other environments
+        List<? extends CatalogItem<?, ?>> items = addItems(yaml, null, forceUpdate);
+        OsgiBundleInstallationResult result = new OsgiBundleInstallationResult();
+        for (CatalogItem<?, ?> ci: items) {
+            RegisteredType rt = mgmt.getTypeRegistry().get(ci.getId());
+            result.getTypesInstalled().add(rt!=null ? rt : RegisteredTypes.of(ci));
+        }
+        return result;
+    }
+
+    protected OsgiBundleInstallationResult addItemsOsgi(String yaml, boolean forceUpdate, Maybe<OsgiManager> osgiManager) {
+        Map<?, ?> cm = BasicBrooklynCatalog.getCatalogMetadata(yaml);
+        VersionedName vn = BasicBrooklynCatalog.getVersionedName( cm, false );
+        if (vn==null) {
+            // for better legacy compatibiity, if id specified at root use that
+            String id = (String) cm.get("id");
+            if (Strings.isNonBlank(id)) {
+                vn = VersionedName.fromString(id);
+            }
+            vn = new VersionedName(vn!=null && Strings.isNonBlank(vn.getSymbolicName()) ? vn.getSymbolicName() : "brooklyn-catalog-bom-"+Identifiers.makeRandomId(8), 
+                vn!=null && vn.getVersionString()!=null ? vn.getVersionString() : getFirstAs(cm, String.class, "version").or(NO_VERSION));
+        }
+        log.debug("Wrapping supplied BOM as "+vn);
+        Manifest mf = new Manifest();
+        mf.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, vn.getSymbolicName());
+        mf.getMainAttributes().putValue(Constants.BUNDLE_VERSION, vn.getOsgiVersionString());
+        mf.getMainAttributes().putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
+        mf.getMainAttributes().putValue(Attributes.Name.MANIFEST_VERSION.toString(), OSGI_MANIFEST_VERSION_VALUE);
+        mf.getMainAttributes().putValue(BROOKLYN_WRAPPED_BOM_BUNDLE, Boolean.TRUE.toString());
+        
+        BundleMaker bm = new BundleMaker(mgmt);
+        File bf = bm.createTempBundle(vn.getSymbolicName(), mf, MutableMap.of(
+            new ZipEntry(CATALOG_BOM), (InputStream) new ByteArrayInputStream(yaml.getBytes())) );
+
+        OsgiBundleInstallationResult result = null;
+        try {
+            result = osgiManager.get().install(null, new FileInputStream(bf), true, true, forceUpdate).get();
+        } catch (FileNotFoundException e) {
+            throw Exceptions.propagate(e);
+        } finally {
+            bf.delete();
+        }
+        if (result.getCode().isError()) {
+            // rollback done by install call above
+            throw new IllegalStateException(result.getMessage());
+        }
+        uninstallEmptyWrapperBundles();
+        return result;
+    }
+    
     @SuppressWarnings("deprecation")
-    private List<CatalogItem<?,?>> toLegacyCatalogItems(Iterable<String> itemIds) {
+    private List<CatalogItem<?,?>> toLegacyCatalogItems(Iterable<RegisteredType> list) {
         List<CatalogItem<?,?>> result = MutableList.of();
-        for (String id: itemIds) {
+        for (RegisteredType t: list) {
+            String id = t.getId();
             CatalogItem<?, ?> item = CatalogUtils.getCatalogItemOptionalVersion(mgmt, id);
             if (item==null) {
                 // using new Type Registry (OSGi addition); 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiArchiveInstaller.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiArchiveInstaller.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiArchiveInstaller.java
index 2b837ba..15430e5 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiArchiveInstaller.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiArchiveInstaller.java
@@ -60,6 +60,7 @@ import org.osgi.framework.Constants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Function;
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Iterables;
@@ -446,7 +447,7 @@ class OsgiArchiveInstaller {
                             Iterable<RegisteredType> items = mgmt().getTypeRegistry().getMatching(RegisteredTypePredicates.containingBundle(result.getMetadata()));
                             log.debug("Adding items from bundle "+result.getVersionedName()+": "+items);
                             for (RegisteredType ci: items) {
-                                result.catalogItemsInstalled.add(ci.getId());
+                                result.addType(ci);
                             }
                         } catch (Exception e) {
                             // unable to install new items; rollback bundles
@@ -494,15 +495,20 @@ class OsgiArchiveInstaller {
                 log.debug(result.message+" (Brooklyn load deferred)");
             } else {
                 startRunnable.run();
-                if (!result.catalogItemsInstalled.isEmpty()) {
+                if (!result.typesInstalled.isEmpty()) {
                     // show fewer info messages, only for 'interesting' and non-deferred installations
                     // (rebind is deferred, as are tests, but REST is not)
                     final int MAX_TO_LIST_EXPLICITLY = 5;
-                    MutableList<String> firstN = MutableList.copyOf(Iterables.limit(result.catalogItemsInstalled, MAX_TO_LIST_EXPLICITLY));
+                    Iterable<String> firstN = Iterables.transform(MutableList.copyOf(Iterables.limit(result.typesInstalled, MAX_TO_LIST_EXPLICITLY)),
+                        new Function<RegisteredType,String>() {
+                            @Override public String apply(RegisteredType input) {
+                                return input.getVersionedName().toString();
+                            }
+                        });
                     log.info(result.message+", items: "+firstN+
-                        (result.catalogItemsInstalled.size() > MAX_TO_LIST_EXPLICITLY ? " (and others, "+result.catalogItemsInstalled.size()+" total)" : "") );
-                    if (log.isDebugEnabled() && result.catalogItemsInstalled.size()>MAX_TO_LIST_EXPLICITLY) {
-                        log.debug(result.message+", all items: "+result.catalogItemsInstalled);
+                        (result.typesInstalled.size() > MAX_TO_LIST_EXPLICITLY ? " (and others, "+result.typesInstalled.size()+" total)" : "") );
+                    if (log.isDebugEnabled() && result.typesInstalled.size()>MAX_TO_LIST_EXPLICITLY) {
+                        log.debug(result.message+", all items: "+result.typesInstalled);
                     }
                 } else {
                     log.debug(result.message+" (into Brooklyn), with no catalog items");

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiBundleInstallationResult.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiBundleInstallationResult.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiBundleInstallationResult.java
index c611553..d65f105 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiBundleInstallationResult.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiBundleInstallationResult.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.core.mgmt.ha;
 import java.util.List;
 
 import org.apache.brooklyn.api.typereg.ManagedBundle;
+import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.osgi.VersionedName;
 import org.osgi.framework.Bundle;
@@ -48,14 +49,21 @@ public class OsgiBundleInstallationResult {
         /** bundle successfully installed to OSGi container but there was an error launching it, 
          * either the OSGi bundle start, catalog items load, or (most commonly) validating the catalog items;
          * bundle may be installed (currently it is in most/all places, but behaviour TBC) so caller may have to uninstall it */
-        ERROR_LAUNCHING_BUNDLE(true);
+        ERROR_LAUNCHING_BUNDLE(true),
+        // codes below used for deletion
+        BUNDLE_REMOVED(false),
+        ERROR_REMOVING_BUNDLE_IN_USE(true),
+        ERROR_REMOVING_BUNDLE_OTHER(true);
         
         final boolean isError;
         ResultCode(boolean isError) { this.isError = isError; }
         
         public boolean isError() { return isError; }
     }
-    final List<String> catalogItemsInstalled = MutableList.of();
+    final List<RegisteredType> typesInstalled = MutableList.of();
+    /** @deprecated since 0.12.0 use {@link #typesInstalled} */
+    @Deprecated
+    private final List<String> catalogItemsInstalled = MutableList.of();
     
     public String getMessage() {
         return message;
@@ -69,6 +77,11 @@ public class OsgiBundleInstallationResult {
     public ResultCode getCode() {
         return code;
     }
+    public List<RegisteredType> getTypesInstalled() {
+        return typesInstalled;
+    }
+    /** @deprecated since 0.12.0 use {@link #getTypesInstalled()} */
+    @Deprecated
     public List<String> getCatalogItemsInstalled() {
         return ImmutableList.copyOf(catalogItemsInstalled);
     }
@@ -89,4 +102,8 @@ public class OsgiBundleInstallationResult {
     public String toString() {
         return OsgiBundleInstallationResult.class.getSimpleName()+"["+code+", "+metadata+", "+message+"]";
     }
+    public void addType(RegisteredType ci) {
+        typesInstalled.add(ci);
+        catalogItemsInstalled.add(ci.getId());        
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java
index d0c7800..603927a 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java
@@ -46,6 +46,7 @@ import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.BrooklynVersion;
 import org.apache.brooklyn.core.catalog.internal.CatalogBundleLoader;
 import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.mgmt.ha.OsgiBundleInstallationResult.ResultCode;
 import org.apache.brooklyn.core.server.BrooklynServerConfig;
 import org.apache.brooklyn.core.server.BrooklynServerPaths;
 import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
@@ -373,6 +374,11 @@ public class OsgiManager {
         return installer.install();
     }
     
+    /** Convenience for {@link #uninstallUploadedBundle(ManagedBundle, boolean)} without forcing, and throwing on error */
+    public OsgiBundleInstallationResult uninstallUploadedBundle(ManagedBundle bundleMetadata) {
+        return uninstallUploadedBundle(bundleMetadata, false).get();
+    }
+    
     /**
      * Removes this bundle from Brooklyn management, 
      * removes all catalog items it defined,
@@ -382,27 +388,78 @@ public class OsgiManager {
      * behaviour of such things is not guaranteed. They will work for many things
      * but attempts to load new classes may fail.
      * <p>
-     * Callers should typically fail if anything from this bundle is in use.
+     * Callers should typically fail prior to invoking if anything from this bundle is in use.
+     * <p>
+     * This does not throw but returns a reference containing errors and result for caller to inspect and handle. 
      */
-    public void uninstallUploadedBundle(ManagedBundle bundleMetadata) {
-        uninstallCatalogItemsFromBundle( bundleMetadata.getVersionedName() );
+    public ReferenceWithError<OsgiBundleInstallationResult> uninstallUploadedBundle(ManagedBundle bundleMetadata, boolean force) {
+        OsgiBundleInstallationResult result = new OsgiBundleInstallationResult();
+        result.metadata = bundleMetadata;
+        List<Throwable> errors = MutableList.of();
+        boolean uninstalledItems = false;
         
-        if (!managedBundlesRecord.remove(bundleMetadata)) {
-            throw new IllegalStateException("No such bundle registered: "+bundleMetadata);
+        try {
+            try {
+                Iterable<RegisteredType> itemsRemoved = uninstallCatalogItemsFromBundle( bundleMetadata.getVersionedName() );
+                for (RegisteredType t: itemsRemoved) result.addType(t);
+                uninstalledItems = true;
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                if (!force) Exceptions.propagate(e);
+                log.warn("Error uninstalling catalog items of "+bundleMetadata+": "+e);
+                errors.add(e);
+            }
+            
+            if (!managedBundlesRecord.remove(bundleMetadata)) {
+                Exception e = new IllegalStateException("No such bundle registered with Brooklyn when uninstalling: "+bundleMetadata);
+                if (!force) Exceptions.propagate(e);
+                log.warn(e.getMessage());
+                errors.add(e);
+            }
+            try {
+                mgmt.getRebindManager().getChangeListener().onUnmanaged(bundleMetadata);
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                if (!force) Exceptions.propagate(e);
+                log.warn("Error handling unmanagement of "+bundleMetadata+": "+e);
+                errors.add(e);            
+            }
+            
+            Bundle bundle = framework.getBundleContext().getBundle(bundleMetadata.getOsgiUniqueUrl());
+            result.bundle = bundle;
+            if (bundle==null) {
+                Exception e = new IllegalStateException("No such bundle installed in OSGi when uninstalling: "+bundleMetadata);
+                if (!force) Exceptions.propagate(e);
+                log.warn(e.getMessage());
+                errors.add(e);
+            } else {
+                try {
+                    bundle.stop();
+                    bundle.uninstall();
+                } catch (BundleException e) {
+                    Exceptions.propagateIfFatal(e);
+                    if (!force) Exceptions.propagate(e);
+                    log.warn("Error stopping and uninstalling "+bundleMetadata+": "+e);
+                    errors.add(e);            
+                }
+            }
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            if (!force) Exceptions.propagate(e);
+            log.warn("Error removing "+bundleMetadata+": "+e);
+            errors.add(e);            
         }
-        mgmt.getRebindManager().getChangeListener().onUnmanaged(bundleMetadata);
-
         
-        Bundle bundle = framework.getBundleContext().getBundle(bundleMetadata.getOsgiUniqueUrl());
-        if (bundle==null) {
-            throw new IllegalStateException("No such bundle installed: "+bundleMetadata);
-        }
-        try {
-            bundle.stop();
-            bundle.uninstall();
-        } catch (BundleException e) {
-            throw Exceptions.propagate(e);
+        if (errors.isEmpty()) {
+            result.message = "Uninstalled "+bundleMetadata+" (type count "+result.typesInstalled.size()+", OSGi "+result.bundle+")";
+            result.code = ResultCode.BUNDLE_REMOVED;
+            return ReferenceWithError.newInstanceWithoutError(result);
         }
+        
+        RuntimeException e = Exceptions.create("Error removing bundle "+bundleMetadata, errors);
+        result.message = Exceptions.collapseText(e);
+        result.code = uninstalledItems ? ResultCode.ERROR_REMOVING_BUNDLE_OTHER : ResultCode.ERROR_REMOVING_BUNDLE_IN_USE;
+        return ReferenceWithError.newInstanceThrowingError(result, e);
     }
 
     @Beta

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicates.java b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicates.java
index 0bf3490..f0189e2 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicates.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypePredicates.java
@@ -175,6 +175,31 @@ public class RegisteredTypePredicates {
         }
     }
 
+    /** Filters for the symbolic name or alias matching the given typeName. */
+    public static Predicate<RegisteredType> nameOrAlias(final String typeName) {
+        return nameOrAlias(Predicates.equalTo(typeName));
+    }
+    public static Predicate<RegisteredType> nameOrAlias(final Predicate<? super String> filter) {
+        return new NameOrAliasMatches(filter);
+    }
+    
+    private static class NameOrAliasMatches implements Predicate<RegisteredType> {
+        private final Predicate<? super String> filter;
+        
+        public NameOrAliasMatches(Predicate<? super String> filter) {
+            this.filter = filter;
+        }
+        @Override
+        public boolean apply(@Nullable RegisteredType item) {
+            if (item==null) return false;
+            if (filter.apply(item.getSymbolicName())) return true;
+            for (String alias: item.getAliases()) {
+                if (filter.apply(alias)) return true;
+            }
+            return false;
+        }
+    }
+
     public static Predicate<RegisteredType> tag(final Object tag) {
         return tags(CollectionFunctionals.any(Predicates.equalTo(tag)));
     }
@@ -194,27 +219,42 @@ public class RegisteredTypePredicates {
         }
     }
 
-    public static <T> Predicate<RegisteredType> anySuperType(final Predicate<Class<T>> filter) {
+    public static <T> Predicate<RegisteredType> anySuperType(final Predicate<Object> filter) {
         return new AnySuperTypeMatches(filter);
     }
-    @SuppressWarnings({ "unchecked", "rawtypes" })
     public static Predicate<RegisteredType> subtypeOf(final Class<?> filter) {
         // the assignableFrom predicate checks if this class is assignable from the subsequent *input*.
         // in other words, we're checking if any input is a subtype of this class
-        return anySuperType((Predicate)Predicates.assignableFrom(filter));
+        return anySuperType(new Predicate<Object>() {
+            @Override
+            public boolean apply(Object input) {
+                if (!(input instanceof Class)) return false;
+                return filter.isAssignableFrom((Class<?>)input);
+            }
+        });
+    }
+    public static Predicate<RegisteredType> subtypeOf(final String filter) {
+        // the assignableFrom predicate checks if this class is assignable from the subsequent *input*.
+        // in other words, we're checking if any input is a subtype of this class
+        return anySuperType(new Predicate<Object>() {
+            @Override
+            public boolean apply(Object input) {
+                if (input instanceof Class) input = ((Class<?>)input).getName();
+                return filter.equals(input);
+            }
+        });
     }
     
     private static class AnySuperTypeMatches implements Predicate<RegisteredType> {
-        private final Predicate<Class<?>> filter;
+        private final Predicate<Object> filter;
         
-        @SuppressWarnings({ "rawtypes", "unchecked" })
-        private AnySuperTypeMatches(Predicate filter) {
+        private AnySuperTypeMatches(Predicate<Object> filter) {
             this.filter = filter;
         }
         @Override
         public boolean apply(@Nullable RegisteredType item) {
             if (item==null) return false;
-            return RegisteredTypes.isAnyTypeOrSuperSatisfying(item.getSuperTypes(), filter);
+            return RegisteredTypes.isAnyTypeOrSuper(item.getSuperTypes(), filter);
         }
     }
     

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
index 6ec5983..7ee137c 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
@@ -65,7 +65,6 @@ import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
 import com.google.common.collect.ComparisonChain;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Ordering;
@@ -406,12 +405,50 @@ public class RegisteredTypes {
      * to see whether any inherit from the given {@link Class} */
     public static boolean isAnyTypeSubtypeOf(Set<Object> candidateTypes, Class<?> superType) {
         if (superType == Object.class) return true;
-        return isAnyTypeOrSuperSatisfying(candidateTypes, Predicates.assignableFrom(superType));
+        return isAnyTypeOrSuper(candidateTypes, new Predicate<Object>() {
+            @Override
+            public boolean apply(Object input) {
+                return input instanceof Class && superType.isAssignableFrom( (Class<?>)input );
+            }
+        });
+    }
+
+    /** 
+     * Queries recursively the given types (either {@link Class} or {@link RegisteredType}) 
+     * to see whether any inherit from the given type either in the registry or a java class  */
+    public static boolean isAnyTypeSubtypeOf(Set<Object> candidateTypes, String superType) {
+        if (Object.class.getName().equals(superType)) return true;
+        return isAnyTypeOrSuper(candidateTypes, new Predicate<Object>() {
+            @Override
+            public boolean apply(Object input) {
+                if (input instanceof Class) input = ((Class<?>)input).getName();
+                return superType.equals(input);
+            }
+        });
+    }
+
+    /** 
+     * Queries recursively the given types (either {@link Class} or {@link RegisteredType}) 
+     * to see whether any superclasses satisfy the given {@link Predicate} comparing as string or class */
+    public static boolean isAnyTypeOrSuper(Set<Object> candidateTypes, Predicate<Object> filter) {
+        for (Object st: candidateTypes) {
+            if (filter.apply(st)) return true;
+        }
+        for (Object st: candidateTypes) {
+            if (st instanceof RegisteredType) {
+                if (isAnyTypeOrSuper(((RegisteredType)st).getSuperTypes(), filter)) return true;
+            }
+        }
+        return false;
     }
 
     /** 
      * Queries recursively the given types (either {@link Class} or {@link RegisteredType}) 
-     * to see whether any java superclasses satisfy the given {@link Predicate} */
+     * to see whether any java superclasses satisfy the given {@link Predicate} on the {@link Class} 
+     * @deprecated since 0.12.0 use {@link #isAnyTypeOrSuper(Set, Predicate)} accepting any object in the predicate,
+     * typically allowing string equivalence although it is valid to restrict to {@link Class} comparison
+     * (might be stricter in some OSGi cases) */
+    @Deprecated
     public static boolean isAnyTypeOrSuperSatisfying(Set<Object> candidateTypes, Predicate<Class<?>> filter) {
         for (Object st: candidateTypes) {
             if (st instanceof Class) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java
new file mode 100644
index 0000000..2c2eeae
--- /dev/null
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java
@@ -0,0 +1,158 @@
+/*
+ * 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.rest.api;
+
+import java.util.List;
+
+import javax.validation.Valid;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.brooklyn.rest.domain.BundleInstallationRestResult;
+import org.apache.brooklyn.rest.domain.BundleSummary;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+@Path("/bundles")
+@Api("Bundles")
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public interface BundleApi {
+
+    @GET
+    @ApiOperation(value = "List bundles registered in the system including their types", 
+            response = BundleSummary.class,
+            responseContainer = "List")
+    public List<BundleSummary> list(
+        @ApiParam(name = "versions", value = "Whether to list 'latest' for each symbolic-name or 'all' versions", 
+            required = false, defaultValue = "latest")
+        @QueryParam("versions")
+        String versions);
+
+    @Path("/{symbolicName}")
+    @GET
+    @ApiOperation(value = "Get summaries for all versions of the given bundle, with more recent ones first (preferring non-SNAPSHOTs)", 
+            response = BundleSummary.class,
+            responseContainer = "List")
+    public List<BundleSummary> listVersions(
+        @ApiParam(name = "symbolicName", value = "Bundle name to query", required = true)
+        @PathParam("symbolicName")
+        String symbolicName);
+
+    @Path("/{symbolicName}/{version}")
+    @GET
+    @ApiOperation(value = "Get detail on a specific bundle given its symbolic name and version", 
+            response = BundleSummary.class)
+    public BundleSummary detail(
+        @ApiParam(name = "symbolicName", value = "Bundle name to query", required = true)
+        @PathParam("symbolicName")
+        String symbolicName,
+        @ApiParam(name = "version", value = "Version to query", required = true)
+        @PathParam("version")
+        String version);
+
+    
+    @Path("/{symbolicName}/{version}")
+    @DELETE
+    @ApiOperation(value = "Removes a bundle, unregistering all the types it declares", 
+            response = BundleInstallationRestResult.class)
+    public BundleInstallationRestResult remove(
+        @ApiParam(name = "symbolicName", value = "Bundle name to query", required = true)
+        @PathParam("symbolicName")
+        String symbolicName,
+        @ApiParam(name = "version", value = "Version to query", required = true)
+        @PathParam("version")
+        String version,
+        @ApiParam(name = "force", value = "Whether to forcibly remove it, even if in use and/or errors", required = false, defaultValue = "false")
+        @QueryParam("force") @DefaultValue("false")
+        Boolean force);
+
+    @POST
+    @Consumes({MediaType.APPLICATION_JSON, "application/x-yaml",
+        // see http://stackoverflow.com/questions/332129/yaml-mime-type
+        "text/yaml", "text/x-yaml", "application/yaml"})
+    @ApiOperation(
+            value = "Adds types to the registry from a given BOM YAML/JSON descriptor (creating a bundle with just this file in it)",
+            response = BundleInstallationRestResult.class
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 400, message = "Error processing the given YAML"),
+            @ApiResponse(code = 201, message = "Items added successfully")
+    })
+    public Response createFromYaml(
+            @ApiParam(name = "yaml", value = "BOM YAML declaring the types to be installed", required = true)
+            @Valid String yaml,
+            @ApiParam(name="force", value="Force installation including replacing any different bundle of the same name and version")
+            @QueryParam("force") @DefaultValue("false")
+            Boolean forceUpdate);
+
+    @POST
+    @Consumes({"application/x-zip", "application/x-jar"})
+    @ApiOperation(
+            value = "Adds types to the registry from a given JAR or ZIP",
+            notes = "Accepts either an OSGi bundle JAR, or ZIP which will be turned into bundle JAR. Either format must "
+                    + "contain a catalog.bom at the root of the archive, which must contain the bundle and version key.",
+            response = BundleInstallationRestResult.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 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,
+            @ApiParam(name = "force", value = "Whether to forcibly remove it, even if in use and/or errors", required = false, defaultValue = "false")
+            @QueryParam("force") @DefaultValue("false")
+            Boolean force);
+
+    @POST
+    @Consumes // anything (if doesn't match other methods with specific content types
+    @ApiOperation(
+            value = "Adds types to the registry from the given item, autodetecting type as ZIP/JAR or BOM YAML",
+            response = BundleInstallationRestResult.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 createAutodetecting(
+            @ApiParam(
+                    name = "item",
+                    value = "Item to install, as JAR/ZIP or Catalog YAML (autodetected)",
+                    required = true)
+                    byte[] item,
+            @ApiParam(name = "force", value = "Whether to forcibly remove it, even if in use and/or errors", required = false, defaultValue = "false")
+            @QueryParam("force") @DefaultValue("false")
+            Boolean force);
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/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 698f97f..3cbae5b 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
@@ -47,6 +47,9 @@ import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 
+/** @deprecated since 0.12.0 use /bundle, /type, and /subtype */
+// but we will probably keep this around for a while as many places use it
+@Deprecated
 @Path("/catalog")
 @Api("Catalog")
 @Consumes(MediaType.APPLICATION_JSON)

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/SubtypeApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/SubtypeApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/SubtypeApi.java
new file mode 100644
index 0000000..01890c5
--- /dev/null
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/SubtypeApi.java
@@ -0,0 +1,96 @@
+/*
+ * 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.rest.api;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.brooklyn.rest.domain.TypeSummary;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+
+@Path("/subtypes")
+@Api("Subtypes")
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public interface SubtypeApi {
+
+    @Path("/{supertype}")
+    @GET
+    @ApiOperation(value = "Get all known types which declare the given argument as a supertype", 
+            response = TypeSummary.class, responseContainer = "List")
+    public List<TypeSummary> list(
+        @ApiParam(name = "supertype", value = "Supertype to query", required = true)
+        @PathParam("supertype")
+        String supertype,
+        @ApiParam(name = "versions", value = "Whether to list 'latest' of each symbolic-name or 'all' versions", required = false, defaultValue = "latest")
+        @QueryParam("versions")
+        String versions,
+        @ApiParam(name = "regex", value = "Regular expression to search for")
+        @QueryParam("regex") @DefaultValue("") String regex,
+        @ApiParam(name = "fragment", value = "Substring case-insensitive to search for")
+        @QueryParam("fragment") @DefaultValue("") String fragment);
+
+    // conveniences for common items where internally it uses java class name
+    // caller can of course use /subtypes/org.apache.brooklyn.api.Entity
+    
+    @GET @Path("/application")
+    @ApiOperation(value = "Get all applications", response = TypeSummary.class, responseContainer = "List")
+    public List<TypeSummary> listApplications(@ApiParam(name = "versions", value = "Whether to list 'latest' of each symbolic-name or 'all' versions", required = false, defaultValue = "latest") @QueryParam("versions") String versions,
+        @ApiParam(name = "regex", value = "Regular expression to search for") @QueryParam("regex") @DefaultValue("") String regex,
+        @ApiParam(name = "fragment", value = "Substring case-insensitive to search for") @QueryParam("fragment") @DefaultValue("") String fragment);
+    
+    @GET @Path("/entity")
+    @ApiOperation(value = "Get all entities", response = TypeSummary.class, responseContainer = "List")
+    public List<TypeSummary> listEntities(@ApiParam(name = "versions", value = "Whether to list 'latest' of each symbolic-name or 'all' versions", required = false, defaultValue = "latest") @QueryParam("versions") String versions,
+        @ApiParam(name = "regex", value = "Regular expression to search for") @QueryParam("regex") @DefaultValue("") String regex,
+        @ApiParam(name = "fragment", value = "Substring case-insensitive to search for") @QueryParam("fragment") @DefaultValue("") String fragment);
+    
+    @GET @Path("/policy")
+    @ApiOperation(value = "Get all policies", response = TypeSummary.class, responseContainer = "List")
+    public List<TypeSummary> listPolicies(@ApiParam(name = "versions", value = "Whether to list 'latest' of each symbolic-name or 'all' versions", required = false, defaultValue = "latest") @QueryParam("versions") String versions,
+        @ApiParam(name = "regex", value = "Regular expression to search for") @QueryParam("regex") @DefaultValue("") String regex,
+        @ApiParam(name = "fragment", value = "Substring case-insensitive to search for") @QueryParam("fragment") @DefaultValue("") String fragment);
+    
+    @GET @Path("/enricher")
+    @ApiOperation(value = "Get all enrichers", response = TypeSummary.class, responseContainer = "List")
+    public List<TypeSummary> listEnrichers(@ApiParam(name = "versions", value = "Whether to list 'latest' of each symbolic-name or 'all' versions", required = false, defaultValue = "latest") @QueryParam("versions") String versions,
+        @ApiParam(name = "regex", value = "Regular expression to search for") @QueryParam("regex") @DefaultValue("") String regex,
+        @ApiParam(name = "fragment", value = "Substring case-insensitive to search for") @QueryParam("fragment") @DefaultValue("") String fragment);
+
+    @GET @Path("/location")
+    // note some (deprecated) locations are only available on location manager 
+    @ApiOperation(value = "Get all locations stored in the registry", response = TypeSummary.class, responseContainer = "List")
+    public List<TypeSummary> listLocations(@ApiParam(name = "versions", value = "Whether to list 'latest' of each symbolic-name or 'all' versions", required = false, defaultValue = "latest") @QueryParam("versions") String versions,
+        @ApiParam(name = "regex", value = "Regular expression to search for") @QueryParam("regex") @DefaultValue("") String regex,
+        @ApiParam(name = "fragment", value = "Substring case-insensitive to search for") @QueryParam("fragment") @DefaultValue("") String fragment);
+
+    // in future could have others, eg: tasks, feeds, etc
+    
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/TypeApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/TypeApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/TypeApi.java
new file mode 100644
index 0000000..c39d5ef
--- /dev/null
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/TypeApi.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.rest.api;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.brooklyn.rest.domain.TypeDetail;
+import org.apache.brooklyn.rest.domain.TypeSummary;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+
+@Path("/types")
+@Api("Types")
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public interface TypeApi {
+
+    @GET
+    @ApiOperation(value = "List types registered in the system", 
+            response = TypeSummary.class,
+            responseContainer = "List")
+    public List<TypeSummary> list(
+        @ApiParam(name = "versions", value = "Whether to list 'latest' of each symbolic-name or 'all' versions", 
+            required = false, defaultValue = "latest")
+        @QueryParam("versions")
+        String versions,
+        @ApiParam(name = "regex", value = "Regular expression to search for")
+        @QueryParam("regex") @DefaultValue("") String regex,
+        @ApiParam(name = "fragment", value = "Substring case-insensitive to search for")
+        @QueryParam("fragment") @DefaultValue("") String fragment);
+
+    @Path("/{nameOrAlias}")
+    @GET
+    @ApiOperation(value = "Get summaries for all versions and instances of a given type or alias, with best match first", 
+            response = TypeSummary.class,
+            responseContainer = "List")
+    public List<TypeSummary> listVersions(
+        @ApiParam(name = "nameOrAlias", value = "Type name to query", required = true)
+        @PathParam("nameOrAlias")
+        String nameOrAlias);
+
+    @Path("/{symbolicName}/{version}")
+    @GET
+    @ApiOperation(value = "Get detail on a given type and version, allowing 'latest' to match the most recent version (preferring non-SNAPSHOTs)", 
+            response = TypeDetail.class)
+    public TypeDetail detail(
+        @ApiParam(name = "symbolicName", value = "Type name to query", required = true)
+        @PathParam("symbolicName")
+        String symbolicName,
+        @ApiParam(name = "version", value = "Version to query", required = true)
+        @PathParam("version")
+        String version);
+    
+    @Path("/{symbolicName}/{version}/icon")
+    @GET
+    @ApiOperation(value = "Returns the icon image registered for this item")
+    @Produces("application/image")
+    public Response icon(
+        @ApiParam(name = "symbolicName", value = "Type name to query", required = true)
+        @PathParam("symbolicName")
+        String symbolicName,
+        @ApiParam(name = "version", value = "Version to query", required = true)
+        @PathParam("version")
+        String version);
+    
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctConfigSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctConfigSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctConfigSummary.java
new file mode 100644
index 0000000..4bba4b2
--- /dev/null
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/AdjunctConfigSummary.java
@@ -0,0 +1,85 @@
+/*
+ * 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.rest.domain;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.brooklyn.config.ConfigKey;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.ImmutableMap;
+
+public class AdjunctConfigSummary extends ConfigSummary {
+
+    private static final long serialVersionUID = 4339330833863794513L;
+
+    @JsonInclude(Include.NON_NULL)
+    private final Map<String, URI> links;
+
+    // json deserialization
+    AdjunctConfigSummary() {
+        links = null;
+    }
+
+    public AdjunctConfigSummary(
+            @JsonProperty("name") String name,
+            @JsonProperty("type") String type,
+            @JsonProperty("description") String description,
+            @JsonProperty("defaultValue") Object defaultValue,
+            @JsonProperty("reconfigurable") boolean reconfigurable,
+            @JsonProperty("links") Map<String, URI> links) {
+        super(name, type, description, defaultValue, reconfigurable, null, null, null);
+        this.links = (links == null) ? ImmutableMap.<String, URI>of() : ImmutableMap.copyOf(links);
+    }
+
+    public AdjunctConfigSummary(ConfigKey<?> config, String label, Double priority, Map<String, URI> links) {
+        super(config, label, priority);
+        this.links = links != null ? ImmutableMap.copyOf(links) : null;
+    }
+
+    @Override
+    public Map<String, URI> getLinks() {
+        return links;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AdjunctConfigSummary)) return false;
+        if (!super.equals(o)) return false;
+        AdjunctConfigSummary that = (AdjunctConfigSummary) o;
+        return Objects.equals(links, that.links);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), links);
+    }
+
+    @Override
+    public String toString() {
+        return "EnricherConfigSummary{" +
+                "links=" + links +
+                '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/BundleInstallationRestResult.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/BundleInstallationRestResult.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/BundleInstallationRestResult.java
new file mode 100644
index 0000000..48f15ee
--- /dev/null
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/BundleInstallationRestResult.java
@@ -0,0 +1,67 @@
+/*
+ * 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.rest.domain;
+
+import java.util.Map;
+
+import org.apache.brooklyn.core.mgmt.ha.OsgiBundleInstallationResult;
+import org.apache.brooklyn.core.mgmt.ha.OsgiBundleInstallationResult.ResultCode;
+import org.apache.brooklyn.util.collections.MutableMap;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+
+public class BundleInstallationRestResult {
+    // as Osgi result, but without bundle, and with maps of catalog items installed
+    
+    private final String message;
+    private final String bundle;
+    private final OsgiBundleInstallationResult.ResultCode code;
+    @JsonInclude(Include.NON_EMPTY)
+    private Map<String,TypeSummary> types = MutableMap.of();
+    
+    /** json internal only */
+    @SuppressWarnings("unused")
+    private BundleInstallationRestResult() {
+        this.message = null;
+        this.bundle = null;
+        this.code = null;
+    }
+    
+    public BundleInstallationRestResult(String message, String bundle, ResultCode code) {
+        this.message = message;
+        this.bundle = bundle;
+        this.code = code;
+    }
+    
+    public String getMessage() {
+        return message;
+    }
+    public String getBundle() {
+        return bundle;
+    }
+    public OsgiBundleInstallationResult.ResultCode getCode() {
+        return code;
+    }
+    
+    public Map<String, TypeSummary> getTypes() {
+        return types;
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/BundleSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/BundleSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/BundleSummary.java
new file mode 100644
index 0000000..f14ee07
--- /dev/null
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/BundleSummary.java
@@ -0,0 +1,99 @@
+/*
+ * 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.rest.domain;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.api.typereg.OsgiBundleWithUrl;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.javalang.JavaClassNames;
+import org.apache.brooklyn.util.text.NaturalOrderComparator;
+import org.apache.brooklyn.util.text.VersionComparator;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.google.common.collect.ComparisonChain;
+
+public class BundleSummary implements Comparable<BundleSummary> {
+
+    private final String symbolicName;
+    private final String version;
+
+    @JsonInclude(value=Include.ALWAYS)
+    private final List<TypeSummary> types = MutableList.of();
+    
+    // not exported directly, but used to provide other top-level json fields
+    // for specific types
+    @JsonIgnore
+    private final Map<String,Object> others = MutableMap.of();
+    
+    /** for json deserialization */
+    BundleSummary() {
+        symbolicName = null;
+        version = null;
+    }
+    
+    public BundleSummary(OsgiBundleWithUrl bundle) {
+        symbolicName = bundle.getSymbolicName();
+        version = bundle.getSuppliedVersionString();
+    }
+    
+    /** Mutable map of other top-level metadata included on this DTO (eg listing config keys or effectors) */ 
+    @JsonAnyGetter
+    public Map<String,Object> getExtraFields() {
+        return others;
+    }
+    @JsonAnySetter
+    public void setExtraField(String name, Object value) {
+        others.put(name, value);
+    }
+    
+    public void addType(TypeSummary type) { types.add(type); }
+    
+    @Override
+    public int compareTo(BundleSummary o2) {
+        BundleSummary o1 = this;
+        return ComparisonChain.start()
+            .compare(o1.symbolicName, o2.symbolicName, NaturalOrderComparator.INSTANCE)
+            .compare(o2.version, o1.version, VersionComparator.INSTANCE)
+            .result();
+    }
+    
+    public String getSymbolicName() {
+        return symbolicName;
+    }
+    
+    public String getVersion() {
+        return version;
+    }
+    
+    public List<TypeSummary> getTypes() {
+        return types;
+    }
+    
+    @Override
+    public String toString() {
+        return JavaClassNames.cleanSimpleClassName(this)+"["+symbolicName+":"+version+"]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ConfigSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ConfigSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ConfigSummary.java
index 22e88d6..6d9ceb3 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ConfigSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/ConfigSummary.java
@@ -30,6 +30,8 @@ import javax.annotation.Nullable;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.util.collections.Jsonya;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.google.common.base.Function;
@@ -42,19 +44,24 @@ public abstract class ConfigSummary implements HasName, Serializable {
 
     private final String name;
     private final String type;
-    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+    @JsonInclude(Include.NON_NULL)
     private final Object defaultValue;
-    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+    @JsonInclude(Include.NON_NULL)
     private final String description;
     @JsonSerialize
     private final boolean reconfigurable;
-    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+    @JsonInclude(Include.NON_NULL)
     private final String label;
-    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+    @JsonInclude(Include.NON_NULL)
     private final Double priority;
-    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+    @JsonInclude(Include.NON_NULL)
     private final List<Map<String, String>> possibleValues;
 
+    // json deserialization
+    ConfigSummary() {
+        this(null, null, null, null, false, null, null, null);
+    }
+    
     protected ConfigSummary(
             @JsonProperty("name") String name,
             @JsonProperty("type") String type,

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java
index 276cd6b..c868fb8 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/EnricherConfigSummary.java
@@ -20,60 +20,24 @@ package org.apache.brooklyn.rest.domain;
 
 import java.net.URI;
 import java.util.Map;
-import java.util.Objects;
 
 import org.apache.brooklyn.config.ConfigKey;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.google.common.collect.ImmutableMap;
-
-public class EnricherConfigSummary extends ConfigSummary {
+// TODO remove? this class has no value over its super
+public class EnricherConfigSummary extends AdjunctConfigSummary {
 
     private static final long serialVersionUID = 4339330833863794513L;
 
-    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
-    private final Map<String, URI> links;
-
-    public EnricherConfigSummary(
-            @JsonProperty("name") String name,
-            @JsonProperty("type") String type,
-            @JsonProperty("description") String description,
-            @JsonProperty("defaultValue") Object defaultValue,
-            @JsonProperty("reconfigurable") boolean reconfigurable,
-            @JsonProperty("links") Map<String, URI> links) {
-        super(name, type, description, defaultValue, reconfigurable, null, null, null);
-        this.links = (links == null) ? ImmutableMap.<String, URI>of() : ImmutableMap.copyOf(links);
-    }
-
+    @SuppressWarnings("unused") // json deserialization
+    private EnricherConfigSummary() {}
+    
     public EnricherConfigSummary(ConfigKey<?> config, String label, Double priority, Map<String, URI> links) {
-        super(config, label, priority);
-        this.links = links != null ? ImmutableMap.copyOf(links) : null;
+        super(config, label, priority, links);
     }
 
-    @Override
-    public Map<String, URI> getLinks() {
-        return links;
+    public EnricherConfigSummary(String name, String type, String description, Object defaultValue, boolean reconfigurable,
+        Map<String, URI> links) {
+        super(name, type, description, defaultValue, reconfigurable, links);
     }
 
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof EnricherConfigSummary)) return false;
-        if (!super.equals(o)) return false;
-        EnricherConfigSummary that = (EnricherConfigSummary) o;
-        return Objects.equals(links, that.links);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(super.hashCode(), links);
-    }
-
-    @Override
-    public String toString() {
-        return "EnricherConfigSummary{" +
-                "links=" + links +
-                '}';
-    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java
index 8115ab9..5dfb898 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/PolicyConfigSummary.java
@@ -20,59 +20,24 @@ package org.apache.brooklyn.rest.domain;
 
 import java.net.URI;
 import java.util.Map;
-import java.util.Objects;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.google.common.collect.ImmutableMap;
 import org.apache.brooklyn.config.ConfigKey;
 
-public class PolicyConfigSummary extends ConfigSummary {
+//TODO remove? this class has no value over its super
+public class PolicyConfigSummary extends AdjunctConfigSummary {
 
     private static final long serialVersionUID = 4339330833863794513L;
 
-    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
-    private final Map<String, URI> links;
-
-    public PolicyConfigSummary(
-            @JsonProperty("name") String name,
-            @JsonProperty("type") String type,
-            @JsonProperty("description") String description,
-            @JsonProperty("defaultValue") Object defaultValue,
-            @JsonProperty("reconfigurable") boolean reconfigurable,
-            @JsonProperty("links") Map<String, URI> links) {
-        super(name, type, description, defaultValue, reconfigurable, null, null, null);
-        this.links = (links == null) ? ImmutableMap.<String, URI>of() : ImmutableMap.copyOf(links);
-    }
-
+    @SuppressWarnings("unused") // json deserialization
+    private PolicyConfigSummary() {}
+    
     public PolicyConfigSummary(ConfigKey<?> config, String label, Double priority, Map<String, URI> links) {
-        super(config, label, priority);
-        this.links = links != null ? ImmutableMap.copyOf(links) : null;
-    }
-
-    @Override
-    public Map<String, URI> getLinks() {
-        return links;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof PolicyConfigSummary)) return false;
-        if (!super.equals(o)) return false;
-        PolicyConfigSummary that = (PolicyConfigSummary) o;
-        return Objects.equals(links, that.links);
+        super(config, label, priority, links);
     }
 
-    @Override
-    public int hashCode() {
-        return Objects.hash(super.hashCode(), links);
+    public PolicyConfigSummary(String name, String type, String description, Object defaultValue, boolean reconfigurable,
+        Map<String, URI> links) {
+        super(name, type, description, defaultValue, reconfigurable, links);
     }
 
-    @Override
-    public String toString() {
-        return "PolicyConfigSummary{" +
-                "links=" + links +
-                '}';
-    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TaskSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TaskSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TaskSummary.java
index eef57e6..e3b0e95 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TaskSummary.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TaskSummary.java
@@ -30,8 +30,9 @@ import java.util.Set;
 import org.apache.brooklyn.util.collections.Jsonya;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
@@ -58,14 +59,14 @@ public class TaskSummary implements HasId, Serializable {
     private final List<LinkWithMetadata> children;
     private final LinkWithMetadata submittedByTask;
 
-    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+    @JsonInclude(Include.NON_NULL)
     private final LinkWithMetadata blockingTask;
-    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+    @JsonInclude(Include.NON_NULL)
     private final String blockingDetails;
 
     private final String detailedStatus;
 
-    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+    @JsonInclude(Include.NON_NULL)
     private final Map<String, LinkWithMetadata> streams;
 
     private final Map<String, URI> links;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeDetail.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeDetail.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeDetail.java
new file mode 100644
index 0000000..2f9e32f
--- /dev/null
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeDetail.java
@@ -0,0 +1,86 @@
+/*
+ * 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.rest.domain;
+
+import org.apache.brooklyn.api.typereg.RegisteredType;
+import org.apache.brooklyn.api.typereg.RegisteredType.TypeImplementationPlan;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+
+public class TypeDetail extends TypeSummary {
+
+    public static class TypeImplementationPlanSummary {
+        @JsonInclude(value=Include.NON_EMPTY)
+        private String format;
+        private Object data;
+        private TypeImplementationPlanSummary() {}
+        private TypeImplementationPlanSummary(TypeImplementationPlan p) {
+            format = p.getPlanFormat();
+            data = p.getPlanData();
+        }
+        public String getFormat() {
+            return format;
+        }
+        public Object getData() {
+            return data;
+        }
+    }
+    private final TypeImplementationPlanSummary plan;
+    
+    /** Constructor for JSON deserialization use only. */
+    TypeDetail() {
+        plan = null;
+    }
+    
+    public TypeDetail(RegisteredType t) {
+        super(t);
+        plan = new TypeImplementationPlanSummary(t.getPlan());
+    }
+    
+    public TypeImplementationPlanSummary getPlan() {
+        return plan;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = super.hashCode();
+        result = prime * result + ((plan == null) ? 0 : plan.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (!super.equals(obj))
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        TypeDetail other = (TypeDetail) obj;
+        if (plan == null) {
+            if (other.plan != null)
+                return false;
+        } else if (!plan.equals(other.plan))
+            return false;
+        return true;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9b337035/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeSummary.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeSummary.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeSummary.java
new file mode 100644
index 0000000..0a045d4
--- /dev/null
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/domain/TypeSummary.java
@@ -0,0 +1,287 @@
+/*
+ * 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.rest.domain;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry.RegisteredTypeKind;
+import org.apache.brooklyn.api.typereg.RegisteredType;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.javalang.JavaClassNames;
+import org.apache.brooklyn.util.text.NaturalOrderComparator;
+import org.apache.brooklyn.util.text.VersionComparator;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.google.common.collect.ComparisonChain;
+
+public class TypeSummary implements Comparable<TypeSummary> {
+
+    private final String symbolicName;
+    private final String version;
+    private final String containingBundle;
+    private final RegisteredTypeKind kind;
+    
+    @JsonInclude(value=Include.NON_EMPTY)
+    private final String displayName;
+    @JsonInclude(value=Include.NON_EMPTY)
+    private final String description;
+    @JsonInclude(value=Include.NON_EMPTY)
+    private String iconUrl;
+
+    @JsonInclude(value=Include.NON_EMPTY)
+    private Set<String> aliases;
+    @JsonInclude(value=Include.NON_EMPTY)
+    private Set<Object> supertypes;
+    @JsonInclude(value=Include.NON_EMPTY)
+    private Set<Object> tags;
+
+    @JsonInclude(value=Include.NON_DEFAULT)
+    private boolean disabled = false;
+    @JsonInclude(value=Include.NON_DEFAULT)
+    private boolean deprecated = false;
+    
+    // not exported directly, but used to provide other top-level json fields
+    // for specific types
+    @JsonIgnore
+    private final Map<String,Object> others = MutableMap.of();
+    
+    /** Constructor for JSON deserialization use only. */
+    TypeSummary() {
+        symbolicName = null;
+        version = null;
+        containingBundle = null;
+        kind = null;
+        
+        displayName = null;
+        description = null;
+    }
+    
+    public TypeSummary(RegisteredType t) {
+        symbolicName = t.getSymbolicName();
+        version = t.getVersion();
+        containingBundle = t.getContainingBundle();
+        kind = t.getKind();
+        
+        displayName = t.getDisplayName();
+        description = t.getDescription();
+        iconUrl = t.getIconUrl();
+        
+        aliases = t.getAliases();
+        supertypes = t.getSuperTypes();
+        tags = t.getTags();
+
+        deprecated = t.isDeprecated();
+        disabled = t.isDisabled();
+    }
+    
+    public TypeSummary(TypeSummary t) {
+        symbolicName = t.getSymbolicName();
+        version = t.getVersion();
+        containingBundle = t.getContainingBundle();
+        kind = t.getKind();
+        
+        displayName = t.getDisplayName();
+        description = t.getDescription();
+        iconUrl = t.getIconUrl();
+        
+        aliases = t.getAliases();
+        supertypes = t.getSupertypes();
+        tags = t.getTags();
+
+        deprecated = t.isDeprecated();
+        disabled = t.isDisabled();
+        
+        others.putAll(t.getExtraFields());
+    }
+
+    /** Mutable map of other top-level metadata included on this DTO (eg listing config keys or effectors) */ 
+    @JsonAnyGetter 
+    public Map<String,Object> getExtraFields() {
+        return others;
+    }
+    @JsonAnySetter
+    public void setExtraField(String name, Object value) {
+        others.put(name, value);
+    }
+    
+    public void setIconUrl(String iconUrl) {
+        this.iconUrl = iconUrl;
+    }
+    
+    @Override
+    public int compareTo(TypeSummary o2) {
+        TypeSummary o1 = this;
+        return ComparisonChain.start()
+            .compare(o1.symbolicName, o2.symbolicName, NaturalOrderComparator.INSTANCE)
+            .compareFalseFirst(o1.disabled, o2.disabled)
+            .compareFalseFirst(o1.deprecated, o2.deprecated)
+            .compare(o2.version, o1.version, VersionComparator.INSTANCE)
+            .result();
+    }
+
+    public String getSymbolicName() {
+        return symbolicName;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public String getContainingBundle() {
+        return containingBundle;
+    }
+
+    public RegisteredTypeKind getKind() {
+        return kind;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public String getIconUrl() {
+        return iconUrl;
+    }
+
+    public Set<String> getAliases() {
+        return aliases;
+    }
+
+    public Set<Object> getSupertypes() {
+        return supertypes;
+    }
+
+    public Set<Object> getTags() {
+        return tags;
+    }
+
+    public boolean isDisabled() {
+        return disabled;
+    }
+
+    public boolean isDeprecated() {
+        return deprecated;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((aliases == null) ? 0 : aliases.hashCode());
+        result = prime * result + ((containingBundle == null) ? 0 : containingBundle.hashCode());
+        result = prime * result + (deprecated ? 1231 : 1237);
+        result = prime * result + ((description == null) ? 0 : description.hashCode());
+        result = prime * result + (disabled ? 1231 : 1237);
+        result = prime * result + ((displayName == null) ? 0 : displayName.hashCode());
+        result = prime * result + ((iconUrl == null) ? 0 : iconUrl.hashCode());
+        result = prime * result + ((kind == null) ? 0 : kind.hashCode());
+        // don't use 'others' - see equals comment
+        // result = prime * result + ((others == null) ? 0 : others.hashCode());
+        result = prime * result + ((supertypes == null) ? 0 : supertypes.hashCode());
+        result = prime * result + ((symbolicName == null) ? 0 : symbolicName.hashCode());
+        result = prime * result + ((tags == null) ? 0 : tags.hashCode());
+        result = prime * result + ((version == null) ? 0 : version.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        TypeSummary other = (TypeSummary) obj;
+        if (aliases == null) {
+            if (other.aliases != null)
+                return false;
+        } else if (!aliases.equals(other.aliases))
+            return false;
+        if (containingBundle == null) {
+            if (other.containingBundle != null)
+                return false;
+        } else if (!containingBundle.equals(other.containingBundle))
+            return false;
+        if (deprecated != other.deprecated)
+            return false;
+        if (description == null) {
+            if (other.description != null)
+                return false;
+        } else if (!description.equals(other.description))
+            return false;
+        if (disabled != other.disabled)
+            return false;
+        if (displayName == null) {
+            if (other.displayName != null)
+                return false;
+        } else if (!displayName.equals(other.displayName))
+            return false;
+        if (iconUrl == null) {
+            if (other.iconUrl != null)
+                return false;
+        } else if (!iconUrl.equals(other.iconUrl))
+            return false;
+        if (kind != other.kind)
+            return false;
+        // don't compare "others" -- eg if "links" are set, we don't care
+//        if (others == null) {
+//            if (other.others != null)
+//                return false;
+//        } else if (!others.equals(other.others))
+//            return false;
+        if (supertypes == null) {
+            if (other.supertypes != null)
+                return false;
+        } else if (!supertypes.equals(other.supertypes))
+            return false;
+        if (symbolicName == null) {
+            if (other.symbolicName != null)
+                return false;
+        } else if (!symbolicName.equals(other.symbolicName))
+            return false;
+        if (tags == null) {
+            if (other.tags != null)
+                return false;
+        } else if (!tags.equals(other.tags))
+            return false;
+        if (version == null) {
+            if (other.version != null)
+                return false;
+        } else if (!version.equals(other.version))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return JavaClassNames.cleanSimpleClassName(this)+"["+symbolicName+":"+version+
+            ", containingBundle=" + containingBundle + ", kind=" + kind + ", displayName=" + displayName + "]";
+    }
+    
+}


[09/11] brooklyn-server git commit: deprecation is since 0.13.0 now (12 is out)

Posted by he...@apache.org.
deprecation is since 0.13.0 now (12 is out)


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

Branch: refs/heads/master
Commit: 8e693305d50a82ffa4567e900ed2686187ccf7fb
Parents: 05ad45e
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri Sep 22 13:19:11 2017 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Sep 22 13:28:43 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/api/catalog/BrooklynCatalog.java   |  4 +++
 .../mgmt/ha/OsgiBundleInstallationResult.java   |  4 +--
 .../brooklyn/core/typereg/RegisteredTypes.java  |  2 +-
 .../apache/brooklyn/rest/api/CatalogApi.java    | 34 ++++++++++----------
 4 files changed, 24 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e693305/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java b/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java
index fa1d55e..e03adbf 100644
--- a/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java
+++ b/api/src/main/java/org/apache/brooklyn/api/catalog/BrooklynCatalog.java
@@ -53,6 +53,7 @@ public interface BrooklynCatalog {
     
     /** As {@link #getCatalogItem(String, String)} but only looking in legacy catalog
      * @deprecated since 0.12.0 only provided to allow TypeRegistry to see the legacy items */
+    @Deprecated
     CatalogItem<?,?> getCatalogItemLegacy(String symbolicName, String version);
 
     /** @return Deletes the item with the given {@link CatalogItem#getSymbolicName()
@@ -70,6 +71,7 @@ public interface BrooklynCatalog {
     
     /** As non-legacy method but only looking in legacy catalog
      * @deprecated since 0.12.0 only provided to allow TypeRegistry to see the legacy items */
+    @Deprecated
     <T,SpecT> CatalogItem<T,SpecT> getCatalogItemLegacy(Class<T> type, String symbolicName, String version);
 
     /** @return All items in the catalog
@@ -84,10 +86,12 @@ public interface BrooklynCatalog {
 
     /** convenience for filtering items in the catalog; see CatalogPredicates for useful filters
      * @deprecated since 0.12.0 use {@link BrooklynTypeRegistry} instead */ 
+    @Deprecated
     <T,SpecT> Iterable<CatalogItem<T,SpecT>> getCatalogItems(Predicate<? super CatalogItem<T,SpecT>> filter);
 
     /** As non-legacy method but only looking in legacy catalog
      * @deprecated since 0.12.0 only provided to allow TypeRegistry to see the legacy items */
+    @Deprecated
     <T,SpecT> Iterable<CatalogItem<T,SpecT>> getCatalogItemsLegacy(Predicate<? super CatalogItem<T,SpecT>> filter);
 
     /** persists the catalog item to the object store, if persistence is enabled */

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e693305/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiBundleInstallationResult.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiBundleInstallationResult.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiBundleInstallationResult.java
index d65f105..05221a6 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiBundleInstallationResult.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiBundleInstallationResult.java
@@ -61,7 +61,7 @@ public class OsgiBundleInstallationResult {
         public boolean isError() { return isError; }
     }
     final List<RegisteredType> typesInstalled = MutableList.of();
-    /** @deprecated since 0.12.0 use {@link #typesInstalled} */
+    /** @deprecated since 0.13.0 use {@link #typesInstalled} */
     @Deprecated
     private final List<String> catalogItemsInstalled = MutableList.of();
     
@@ -80,7 +80,7 @@ public class OsgiBundleInstallationResult {
     public List<RegisteredType> getTypesInstalled() {
         return typesInstalled;
     }
-    /** @deprecated since 0.12.0 use {@link #getTypesInstalled()} */
+    /** @deprecated since 0.13.0 use {@link #getTypesInstalled()} */
     @Deprecated
     public List<String> getCatalogItemsInstalled() {
         return ImmutableList.copyOf(catalogItemsInstalled);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e693305/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
index 9e6d610..49ea861 100644
--- a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
+++ b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java
@@ -446,7 +446,7 @@ public class RegisteredTypes {
     /** 
      * Queries recursively the given types (either {@link Class} or {@link RegisteredType}) 
      * to see whether any java superclasses satisfy the given {@link Predicate} on the {@link Class} 
-     * @deprecated since 0.12.0 use {@link #isAnyTypeOrSuper(Set, Predicate)} accepting any object in the predicate,
+     * @deprecated since 0.13.0 use {@link #isAnyTypeOrSuper(Set, Predicate)} accepting any object in the predicate,
      * typically allowing string equivalence although it is valid to restrict to {@link Class} comparison
      * (might be stricter in some OSGi cases) */
     @Deprecated

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/8e693305/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 cb4298f..6499f14 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
@@ -153,7 +153,7 @@ public interface CatalogApi {
             @QueryParam("forceUpdate") @DefaultValue("false")
                     boolean forceUpdate);
     
-    /** @deprecated since 0.12.0 delete the bundle via DELETE /catalog/bundles/xxx */
+    /** @deprecated since 0.13.0 delete the bundle via DELETE /catalog/bundles/xxx */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
     @DELETE
@@ -173,7 +173,7 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "The version identifier of the application or template to delete", required = true)
         @PathParam("version") String version) throws Exception;
 
-    /** @deprecated since 0.12.0 delete the bundle via DELETE /catalog/bundles/xxx */
+    /** @deprecated since 0.13.0 delete the bundle via DELETE /catalog/bundles/xxx */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
     @DELETE
@@ -193,7 +193,7 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "The version identifier of the entity or template to delete", required = true)
         @PathParam("version") String version) throws Exception;
 
-    /** @deprecated since 0.12.0 delete the bundle via DELETE /catalog/bundles/xxx */
+    /** @deprecated since 0.13.0 delete the bundle via DELETE /catalog/bundles/xxx */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
     @DELETE
@@ -212,7 +212,7 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "The version identifier of the policy to delete", required = true)
         @PathParam("version") String version) throws Exception;
 
-    /** @deprecated since 0.12.0 delete the bundle via DELETE /catalog/bundles/xxx */
+    /** @deprecated since 0.13.0 delete the bundle via DELETE /catalog/bundles/xxx */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
     @DELETE
@@ -232,7 +232,7 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "The version identifier of the location to delete", required = true)
         @PathParam("version") String version) throws Exception;
 
-    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    /** @deprecated since 0.13.0 use /catalog/bundles and /catalog/types?supertype=... */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
     @GET
@@ -249,7 +249,7 @@ public interface CatalogApi {
         @QueryParam("allVersions") @DefaultValue("false") boolean includeAllVersions);
 
     // bad name - it is just templates
-    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    /** @deprecated since 0.13.0 use /catalog/bundles and /catalog/types?supertype=... */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
     @GET
@@ -265,7 +265,7 @@ public interface CatalogApi {
             @ApiParam(name = "allVersions", value = "Include all versions (defaults false, only returning the best version)")
             @QueryParam("allVersions") @DefaultValue("false") boolean includeAllVersions);
 
-    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    /** @deprecated since 0.13.0 use /catalog/bundles and /catalog/types?supertype=... */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
     @GET
@@ -286,7 +286,7 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "The version identifier of the entity or template to retrieve", required = true)
         @PathParam("version") String version) throws Exception;
 
-    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    /** @deprecated since 0.13.0 use /catalog/bundles and /catalog/types?supertype=... */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
     @GET
@@ -307,7 +307,7 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "The version identifier of the application to retrieve", required = true)
         @PathParam("version") String version) throws Exception;
 
-    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    /** @deprecated since 0.13.0 use /catalog/bundles and /catalog/types?supertype=... */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
     @GET
@@ -323,7 +323,7 @@ public interface CatalogApi {
             @ApiParam(name = "allVersions", value = "Include all versions (defaults false, only returning the best version)")
             @QueryParam("allVersions") @DefaultValue("false") boolean includeAllVersions);
 
-    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    /** @deprecated since 0.13.0 use /catalog/bundles and /catalog/types?supertype=... */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
     @GET
@@ -343,7 +343,7 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "The version identifier of the application to retrieve", required = true)
         @PathParam("version") String version) throws Exception;
 
-    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    /** @deprecated since 0.13.0 use /catalog/bundles and /catalog/types?supertype=... */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
     @GET
@@ -359,7 +359,7 @@ public interface CatalogApi {
             @ApiParam(name = "allVersions", value = "Include all versions (defaults false, only returning the best version)")
             @QueryParam("allVersions") @DefaultValue("false") boolean includeAllVersions);
 
-    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    /** @deprecated since 0.13.0 use /catalog/bundles and /catalog/types?supertype=... */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
     @GET
@@ -397,7 +397,7 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "version identifier of catalog item (application, entity, policy, location)", required=true)
         @PathParam("version") String version);
     
-    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=...;
+    /** @deprecated since 0.13.0 use /catalog/bundles and /catalog/types?supertype=...;
      * deprecation/disabling needs to be done in the bundle, and we might support deprecating/disabling bundles */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
@@ -413,7 +413,7 @@ public interface CatalogApi {
         @ApiParam(name = "deprecated", value = "Whether or not the catalog item is deprecated", required = true)
         boolean deprecated);
     
-    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=...;
+    /** @deprecated since 0.13.0 use /catalog/bundles and /catalog/types?supertype=...;
      * deprecation/disabling needs to be done in the bundle, and we might support deprecating/disabling bundles */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
@@ -429,7 +429,7 @@ public interface CatalogApi {
         @ApiParam(name = "disabled", value = "Whether or not the catalog item is disabled", required = true)
         boolean disabled);
 
-    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    /** @deprecated since 0.13.0 use /catalog/bundles and /catalog/types?supertype=... */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
     @GET
@@ -445,7 +445,7 @@ public interface CatalogApi {
             @ApiParam(name = "allVersions", value = "Include all versions (defaults false, only returning the best version)")
             @QueryParam("allVersions") @DefaultValue("false") boolean includeAllVersions);
 
-    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    /** @deprecated since 0.13.0 use /catalog/bundles and /catalog/types?supertype=... */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
     @GET
@@ -462,7 +462,7 @@ public interface CatalogApi {
             @ApiParam(name = "version", value = "The version identifier of the enricher to retrieve", required = true)
             @PathParam("version") String version) throws Exception;
 
-    /** @deprecated since 0.12.0 delete the bundle via DELETE /catalog/bundles/xxx */
+    /** @deprecated since 0.13.0 delete the bundle via DELETE /catalog/bundles/xxx */
     // but we will probably keep this around for a while as many places use it
     @Deprecated
     @DELETE


[07/11] brooklyn-server git commit: remaining PR comments

Posted by he...@apache.org.
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/05ad45e7/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeResourcesTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeResourcesTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeResourcesTest.java
new file mode 100644
index 0000000..87fa378
--- /dev/null
+++ b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeResourcesTest.java
@@ -0,0 +1,1098 @@
+/*
+ * 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.rest.resources;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+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.policy.Policy;
+import org.apache.brooklyn.api.typereg.ManagedBundle;
+import org.apache.brooklyn.api.typereg.OsgiBundleWithUrl;
+import org.apache.brooklyn.api.typereg.RegisteredType;
+import org.apache.brooklyn.core.entity.EntityPredicates;
+import org.apache.brooklyn.core.mgmt.ha.OsgiManager;
+import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.mgmt.osgi.OsgiStandaloneTest;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.enricher.stock.Aggregator;
+import org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy;
+import org.apache.brooklyn.rest.domain.BundleInstallationRestResult;
+import org.apache.brooklyn.rest.domain.BundleSummary;
+import org.apache.brooklyn.rest.domain.TypeDetail;
+import org.apache.brooklyn.rest.domain.TypeSummary;
+import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.test.support.TestResourceUnavailableException;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.core.ResourceUtils;
+import org.apache.brooklyn.util.core.osgi.BundleMaker;
+import org.apache.brooklyn.util.javalang.JavaClassNames;
+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.HttpHeaders;
+import org.eclipse.jetty.http.HttpStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+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;
+
+public class BundleAndTypeResourcesTest extends BrooklynRestResourceTest {
+
+    private static final Logger log = LoggerFactory.getLogger(BundleAndTypeResourcesTest.class);
+    
+    private static String TEST_VERSION = "0.1.2";
+    private static String TEST_LASTEST_VERSION = "0.1.3";
+
+    private Collection<ManagedBundle> initialBundles;
+
+    @Override
+    protected boolean useLocalScannedCatalog() {
+        return true;
+    }
+
+    @Override
+    protected void initClass() throws Exception {
+        super.initClass();
+        // cache initially installed bundles
+        OsgiManager osgi = ((ManagementContextInternal)getManagementContext()).getOsgiManager().get();
+        initialBundles = osgi.getManagedBundles().values();
+    }
+    
+    protected void initMethod() throws Exception {
+        super.initMethod();
+        
+        // and reset OSGi container
+        OsgiManager osgi = ((ManagementContextInternal)getManagementContext()).getOsgiManager().get();
+        for (ManagedBundle b: osgi.getManagedBundles().values()) {
+            if (!initialBundles.contains(b)) {
+                osgi.uninstallUploadedBundle(b);
+            }
+        }
+    }
+    
+    @Test
+    /** based on CampYamlLiteTest */
+    public void testRegisterCustomEntityTopLevelSyntaxWithBundleWhereEntityIsFromCoreAndIconFromBundle() {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+
+        String symbolicName = "my.catalog.entity.id";
+        String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: entity",
+                "  name: My Catalog App",
+                "  description: My description",
+                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  libraries:",
+                "  - url: " + bundleUrl,
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
+
+        Response response = client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/yaml")
+                .post(yaml);
+
+        assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
+        BundleInstallationRestResult installed = response.readEntity(BundleInstallationRestResult.class);
+        Asserts.assertSize(installed.getTypes().values(), 1);
+        TypeSummary installedItem = installed.getTypes().get(symbolicName+":"+TEST_VERSION);
+        Assert.assertNotNull(installedItem, ""+installed.getTypes());
+
+        TypeDetail entityItem = client().path("/catalog/types/"+symbolicName + "/" + TEST_VERSION)
+                .get(TypeDetail.class);
+
+        Assert.assertEquals(new TypeSummary(entityItem), installedItem);
+        Assert.assertNotNull(entityItem.getPlan());
+        Assert.assertTrue(((String)entityItem.getPlan().getData()).contains("org.apache.brooklyn.core.test.entity.TestEntity"));
+
+        assertEquals(entityItem.getSymbolicName(), symbolicName);
+        assertEquals(entityItem.getVersion(), TEST_VERSION);
+
+        // also check it's included in various lists
+        List<TypeSummary> list1 = client().path("/catalog/types/"+symbolicName).get(new GenericType<List<TypeSummary>>() {});
+        assertEquals(list1, MutableList.of(installedItem));
+        List<TypeSummary> list2 = client().path("/catalog/types").get(new GenericType<List<TypeSummary>>() {});
+        Assert.assertTrue(list2.contains(installedItem), ""+list2);
+        List<TypeSummary> list3 = client().path("/catalog/types").query("supertype", "entity").get(new GenericType<List<TypeSummary>>() {});
+        Assert.assertTrue(list3.contains(installedItem), ""+list3);
+        
+        // and internally let's check we have libraries
+        RegisteredType item = getManagementContext().getTypeRegistry().get(symbolicName, TEST_VERSION);
+        Assert.assertNotNull(item);
+        Collection<OsgiBundleWithUrl> libs = item.getLibraries();
+        assertEquals(libs.size(), 1);
+        assertEquals(Iterables.getOnlyElement(libs).getUrl(), bundleUrl);
+
+        // now let's check other things on the item
+        URI expectedIconUrl = URI.create(getEndpointAddress() + "/catalog/types/" + symbolicName + "/" + entityItem.getVersion()+"/icon").normalize();
+        assertEquals(entityItem.getDisplayName(), "My Catalog App");
+        assertEquals(entityItem.getDescription(), "My description");
+        assertEquals(entityItem.getIconUrl(), expectedIconUrl.getPath());
+        assertEquals(item.getIconUrl(), "classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif");
+
+        // an InterfacesTag should be created for every catalog item
+        if (checkTraits(false)) {
+            @SuppressWarnings("unchecked")
+            Map<String, List<String>> traitsMapTag = Iterables.getOnlyElement(Iterables.filter(entityItem.getTags(), Map.class));
+            List<String> actualInterfaces = traitsMapTag.get("traits");
+            List<Class<?>> expectedInterfaces = Reflections.getAllInterfaces(TestEntity.class);
+            assertEquals(actualInterfaces.size(), expectedInterfaces.size());
+            for (Class<?> expectedInterface : expectedInterfaces) {
+                assertTrue(actualInterfaces.contains(expectedInterface.getName()));
+            }
+        }
+
+        byte[] iconData = client().path("/catalog/types/" + symbolicName + "/" + TEST_VERSION+"/icon").get(byte[].class);
+        assertEquals(iconData.length, 43);
+    }
+
+    @Test
+    public void testRegisterOsgiPolicyTopLevelSyntax() {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+
+        String symbolicName = "my.catalog.entity.id."+JavaClassNames.niceClassAndMethod();
+        String policyType = "org.apache.brooklyn.test.osgi.entities.SimplePolicy";
+        String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
+
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: policy",
+                "  name: My Catalog App",
+                "  description: My description",
+                "  libraries:",
+                "  - url: " + bundleUrl,
+                "  item:",
+                "    type: " + policyType);
+
+        TypeSummary installedItem = Iterables.getOnlyElement( client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/yaml")
+                .post(yaml, BundleInstallationRestResult.class).getTypes().values() );
+
+        assertEquals(installedItem.getSymbolicName(), symbolicName);
+        assertEquals(installedItem.getVersion(), TEST_VERSION);
+        Assert.assertTrue(installedItem.getSupertypes().contains(Policy.class.getName()), ""+installedItem.getSupertypes());
+    }
+
+    @Test
+    public void testFilterListOfEntitiesByName() {
+        List<TypeSummary> entities = client().path("/catalog/types")
+                .query("fragment", "vaNIllasOFTWAREpROCESS").get(new GenericType<List<TypeSummary>>() {});
+        log.info("Matching entities: " + entities);
+        assertEquals(entities.size(), 1);
+
+        entities = client().path("/catalog/types").query("supertype", "entity")
+                .query("fragment", "vaNIllasOFTWAREpROCESS").get(new GenericType<List<TypeSummary>>() {});
+        log.info("Matching entities: " + entities);
+        assertEquals(entities.size(), 1);
+
+        List<TypeSummary> entities2 = client().path("/catalog/types")
+                .query("regex", "[Vv]an.[alS]+oftware\\w+").get(new GenericType<List<TypeSummary>>() {});
+        assertEquals(entities2.size(), 1);
+
+        assertEquals(entities, entities2);
+    
+        entities = client().path("/catalog/types").query("supertype", "entity")
+                .query("fragment", "bweqQzZ").get(new GenericType<List<TypeSummary>>() {});
+        Asserts.assertSize(entities, 0);
+
+        entities = client().path("/catalog/types").query("supertype", "entity")
+                .query("regex", "bweq+z+").get(new GenericType<List<TypeSummary>>() {});
+        Asserts.assertSize(entities, 0);
+    }
+
+    @Test
+    public void testGetCatalogEntityIconDetails() throws IOException {
+        String catalogItemId = "testGetCatalogEntityIconDetails";
+        addTestCatalogItemAsEntity(catalogItemId);
+        Response response = client().path(URI.create("/catalog/types/" + catalogItemId + "/" + TEST_VERSION + "/icon"))
+                .get();
+        response.bufferEntity();
+        Assert.assertEquals(response.getStatus(), 200);
+        Assert.assertEquals(response.getMediaType(), MediaType.valueOf("image/png"));
+        Image image = Toolkit.getDefaultToolkit().createImage(Files.readFile(response.readEntity(InputStream.class)));
+        Assert.assertNotNull(image);
+    }
+
+    private void addTestCatalogItemAsEntity(String catalogItemId) {
+        addTestCatalogItem(catalogItemId, "entity", TEST_VERSION, "org.apache.brooklyn.rest.resources.DummyIconEntity");
+    }
+
+    private void addTestCatalogItem(String catalogItemId, String itemType, String version, String service) {
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + catalogItemId,
+                "  version: " + TEST_VERSION,
+                "  itemType: " + checkNotNull(itemType),
+                "  name: My Catalog App",
+                "  description: My description",
+                "  icon_url: classpath:///bridge-small.png",
+                "  version: " + version,
+                "  item:",
+                "    type: " + service);
+
+        client().path("/catalog/bundles").header(HttpHeaders.CONTENT_TYPE, "application/yaml").post(yaml);
+    }
+
+    @Test
+    public void testListPolicies() {
+        Set<TypeSummary> policies = client().path("/catalog/types").query("supertype", "policy")
+                .get(new GenericType<Set<TypeSummary>>() {});
+
+        assertTrue(policies.size() > 0);
+        TypeSummary asp = null;
+        for (TypeSummary p : policies) {
+            if (AutoScalerPolicy.class.getName().equals(p.getSymbolicName()))
+                asp = p;
+        }
+        Assert.assertNotNull(asp, "didn't find AutoScalerPolicy");
+    }
+
+    @Test
+    public void testLocationAddGetAndRemove() {
+        String symbolicName = "my.catalog.location.id";
+        String locationType = "localhost";
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: location",
+                "  name: My Catalog Location",
+                "  description: My description",
+                "  item:",
+                "    type: " + locationType);
+
+        // Create location item
+        Map<String, TypeSummary> items = client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/yaml")
+                .post(yaml, BundleInstallationRestResult.class).getTypes();
+        TypeSummary locationItem = Iterables.getOnlyElement(items.values());
+
+        assertEquals(locationItem.getSymbolicName(), symbolicName);
+        assertEquals(locationItem.getVersion(), TEST_VERSION);
+
+        // Retrieve location item
+        TypeDetail location = client().path("/catalog/types/"+symbolicName+"/"+TEST_VERSION).get(TypeDetail.class);
+        assertEquals(location.getSymbolicName(), symbolicName);
+
+        // Retrieve all locations
+        Set<TypeSummary> locations = client().path("/catalog/types").query("supertype", "location")
+                .get(new GenericType<Set<TypeSummary>>() {});
+        boolean found = false;
+        for (TypeSummary contender : locations) {
+            if (contender.getSymbolicName().equals(symbolicName)) {
+                found = true;
+                break;
+            }
+        }
+        Assert.assertTrue(found, "contenders="+locations);
+        
+        // Delete
+        Response deleteResponse = client().path("/catalog/bundles/"+locationItem.getContainingBundle().replaceAll(":", "/"))
+                .delete();
+        assertEquals(deleteResponse.getStatus(), Response.Status.OK.getStatusCode());
+        BundleInstallationRestResult deletionResponse = deleteResponse.readEntity(BundleInstallationRestResult.class);
+        Assert.assertEquals(deletionResponse.getBundle(), symbolicName+":"+TEST_VERSION);
+        Assert.assertEquals(deletionResponse.getTypes().keySet(), MutableSet.of(symbolicName+":"+TEST_VERSION));
+
+        Response getPostDeleteResponse = client().path("/catalog/types/"+symbolicName+"/"+TEST_VERSION)
+                .get();
+        assertEquals(getPostDeleteResponse.getStatus(), Response.Status.NOT_FOUND.getStatusCode());
+    }
+
+    @Test
+    public void testListEnrichers() {
+        Set<TypeSummary> enrichers = client().path("/catalog/types").query("supertype", "enricher")
+                .get(new GenericType<Set<TypeSummary>>() {});
+
+        assertTrue(enrichers.size() > 0);
+        TypeSummary asp = null;
+        for (TypeSummary p : enrichers) {
+            if (Aggregator.class.getName().equals(p.getSymbolicName()))
+                asp = p;
+        }
+        Assert.assertNotNull(asp, "didn't find Aggregator");
+    }
+
+    @Test
+    public void testEnricherAddGet() {
+        String symbolicName = "my.catalog.enricher.id";
+        String enricherType = "org.apache.brooklyn.enricher.stock.Aggregator";
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: enricher",
+                "  name: My Catalog Enricher",
+                "  description: My description",
+                "  item:",
+                "    type: " + enricherType);
+
+        // Create location item
+        Map<String, TypeSummary> items = client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/yaml")
+                .post(yaml, BundleInstallationRestResult.class).getTypes();
+        TypeSummary enricherItem = Iterables.getOnlyElement(items.values());
+
+        assertEquals(enricherItem.getSymbolicName(), symbolicName);
+        assertEquals(enricherItem.getVersion(), TEST_VERSION);
+
+        // Retrieve location item
+        TypeSummary enricher = client().path("/catalog/types/"+symbolicName+"/"+TEST_VERSION)
+                .get(TypeSummary.class);
+        assertEquals(enricher.getSymbolicName(), symbolicName);
+
+        // Retrieve all locations
+        Set<TypeSummary> enrichers = client().path("/catalog/types").query("supertype", "enricher")
+                .get(new GenericType<Set<TypeSummary>>() {});
+        boolean found = false;
+        for (TypeSummary contender : enrichers) {
+            if (contender.getSymbolicName().equals(symbolicName)) {
+                found = true;
+                break;
+            }
+        }
+        Assert.assertTrue(found, "contenders="+enrichers);
+    }
+
+    @Test
+    // osgi may fail in IDE, typically works on mvn CLI though
+    public void testRegisterOsgiEnricherTopLevelSyntax() {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+
+        String symbolicName = "my.catalog.enricher.id";
+        String enricherType = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENRICHER;
+        String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
+
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: enricher",
+                "  name: My Catalog Enricher",
+                "  description: My description",
+                "  libraries:",
+                "  - url: " + bundleUrl,
+                "  item:",
+                "    type: " + enricherType);
+
+        TypeSummary installedItem = Iterables.getOnlyElement( client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/yaml")
+                .post(yaml, BundleInstallationRestResult.class).getTypes().values() );
+
+        assertEquals(installedItem.getSymbolicName(), symbolicName);
+        assertEquals(installedItem.getVersion(), TEST_VERSION);
+    }
+
+    @Test
+    public void testDeleteCustomEntityFromCatalog() {
+        String symbolicName = "my.catalog.app.id.to.subsequently.delete";
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: entity",
+                "  name: My Catalog App To Be Deleted",
+                "  description: My description",
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
+
+        client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/yaml")
+                .post(yaml);
+
+        BundleSummary getInstalledBundle = client().path("/catalog/bundles/"+symbolicName+"/"+TEST_VERSION)
+            .get(BundleSummary.class);
+        assertEquals(getInstalledBundle.getSymbolicName(), symbolicName);
+        assertEquals(getInstalledBundle.getVersion(), TEST_VERSION);
+        Asserts.assertNotNull(getInstalledBundle.getTypes(), "expected 'types' in: "+getInstalledBundle.getExtraFields());
+        Asserts.assertStringContains(""+getInstalledBundle.getTypes(), "My Catalog App");
+
+        Response deleteResponse = client().path("/catalog/bundles/"+symbolicName+"/"+TEST_VERSION)
+                .delete();
+
+        assertEquals(deleteResponse.getStatus(), Response.Status.OK.getStatusCode());
+        // contents of delete tested in delete location method
+
+        Response getPostDeleteResponse = client().path("/catalog/bundles/"+symbolicName+"/"+TEST_VERSION)
+                .get();
+        assertEquals(getPostDeleteResponse.getStatus(), Response.Status.NOT_FOUND.getStatusCode());
+    }
+
+    private void addCatalogItemWithInvalidBundleUrl(String bundleUrl) {
+        String symbolicName = "my.catalog.entity.id";
+        String yaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: entity",
+                "  name: My Catalog App",
+                "  description: My description",
+                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  libraries:",
+                "  - url: " + bundleUrl,
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
+
+        Response response = client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-yaml")
+                .post(yaml);
+
+        assertEquals(response.getStatus(), HttpStatus.BAD_REQUEST_400);
+    }
+    
+    @Test
+    public void testAddUnreachableItem() {
+        addCatalogItemWithInvalidBundleUrl("http://0.0.0.0/can-not-connect");
+    }
+
+    @Test
+    public void testAddInvalidItem() {
+        //equivalent to HTTP response 200 text/html
+        addCatalogItemWithInvalidBundleUrl("classpath://not-a-jar-file.txt");
+    }
+
+    @Test
+    public void testAddMissingItem() {
+        //equivalent to HTTP response 404 text/html
+        addCatalogItemWithInvalidBundleUrl("classpath://missing-jar-file.txt");
+    }
+
+    @Test
+    public void testInvalidArchive() throws Exception {
+        File f = Os.newTempFile("osgi", "zip");
+
+        Response response = client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), "zip file is empty");
+    }
+
+    @Test
+    public void testArchiveWithoutBom() throws Exception {
+        File f = createZip(ImmutableMap.<String, String>of());
+
+        Response response = client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), "Missing bundle symbolic name in BOM or MANIFEST");
+    }
+
+    @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/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), "Missing bundle symbolic name in BOM or MANIFEST");
+    }
+
+    @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/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), 
+            "Missing bundle symbolic name in BOM or MANIFEST");
+    }
+
+    @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/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), "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";
+        String wrongBundleName = "org.apache.brooklyn.test2";
+        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: "+wrongBundleName,
+                        "Bundle-Version: " + version,
+                        "Bundle-ManifestVersion: " + version)));
+
+        Response response = client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), 
+            "symbolic name mismatch",
+            wrongBundleName, bundle);
+    }
+
+    @Test
+    public void testJarWithoutMatchingVersion() throws Exception {
+        String name = "My Catalog App";
+        String bundle = "org.apache.brooklyn.test";
+        String version = "0.1.0";
+        String wrongVersion = "0.3.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: " + wrongVersion,
+                        "Bundle-ManifestVersion: " + wrongVersion)));
+
+        Response response = client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), 
+            "version mismatch",
+            wrongVersion, version);
+    }
+
+    @Test
+    public void testOsgiBundleWithBom() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+        final String symbolicName = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_SYMBOLIC_NAME_FULL;
+        final String version = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_VERSION;
+        final String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
+        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:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
+        
+        f = bm.copyAdding(f, MutableMap.of(new ZipEntry("catalog.bom"), (InputStream) new ByteArrayInputStream(bom.getBytes())));
+
+        Response response = client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
+                .post(Streams.readFully(new FileInputStream(f)));
+        
+        assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
+
+        TypeSummary entityItem = client().path("/catalog/types/"+symbolicName + "/" + version)
+                .get(TypeSummary.class);
+
+        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(), "org.apache.brooklyn.test.resources.osgi.brooklyn-test-osgi-entities");
+        assertEquals(lib.getSuppliedVersionString(), version);
+
+        // now let's check other things on the item
+        URI expectedIconUrl = URI.create(getEndpointAddress() + "/catalog/types/" + symbolicName + "/" + entityItem.getVersion()+"/icon").normalize();
+        assertEquals(entityItem.getDisplayName(), "My Catalog App");
+        assertEquals(entityItem.getDescription(), "My description");
+        assertEquals(entityItem.getIconUrl(), expectedIconUrl.getPath());
+        assertEquals(item.getIconUrl(), "classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif");
+
+        if (checkTraits(false)) {
+            // an InterfacesTag should be created for every catalog item
+            @SuppressWarnings("unchecked")
+            Map<String, List<String>> traitsMapTag = Iterables.getOnlyElement(Iterables.filter(entityItem.getTags(), Map.class));
+            List<String> actualInterfaces = traitsMapTag.get("traits");
+            List<Class<?>> expectedInterfaces = Reflections.getAllInterfaces(TestEntity.class);
+            assertEquals(actualInterfaces.size(), expectedInterfaces.size());
+            for (Class<?> expectedInterface : expectedInterfaces) {
+                assertTrue(actualInterfaces.contains(expectedInterface.getName()));
+            }
+        }
+
+        byte[] iconData = client().path("/catalog/types/" + symbolicName + "/" + version + "/icon").get(byte[].class);
+        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/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(f)));
+
+
+        assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
+
+        TypeDetail entityItem = client().path("/catalog/types/"+symbolicName + "/" + version)
+                .get(TypeDetail.class);
+
+        Assert.assertNotNull(entityItem.getPlan().getData());
+        Assert.assertTrue(entityItem.getPlan().getData().toString().contains(entityType));
+
+        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.getSuppliedVersionString(), version);
+
+        // now let's check other things on the item
+        assertEquals(entityItem.getDescription(), "My description");
+        URI expectedIconUrl = URI.create(getEndpointAddress() + "/catalog/types/" + symbolicName + "/" + entityItem.getVersion() + "/icon").normalize();
+        assertEquals(entityItem.getIconUrl(), expectedIconUrl.getPath());
+        assertEquals(item.getIconUrl(), "classpath:" + iconPath);
+
+        if (checkTraits(false)) {
+            // an InterfacesTag should be created for every catalog item
+            @SuppressWarnings("unchecked")
+            Map<String, List<String>> traitsMapTag = Iterables.getOnlyElement(Iterables.filter(entityItem.getTags(), Map.class));
+            List<String> actualInterfaces = traitsMapTag.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/types/" + symbolicName + "/" + version + "/icon").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 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;
+    }
+
+    @Test
+    public void testGetOnlyLatestApplication() {
+        String symbolicName = "latest.catalog.application.id";
+        String itemType = "template";
+        String serviceType = "org.apache.brooklyn.core.test.entity.TestEntity";
+
+        addTestCatalogItem(symbolicName, itemType, TEST_VERSION, serviceType);
+        addTestCatalogItem(symbolicName, itemType, TEST_LASTEST_VERSION, serviceType);
+
+        TypeSummary application = client().path("/catalog/types/" + symbolicName + "/latest")
+                .get(TypeSummary.class);
+        assertEquals(application.getVersion(), TEST_LASTEST_VERSION);
+    }
+
+    @Test
+    public void testGetOnlyLatestDifferentCases() {
+        // depends on installation of this
+        testGetOnlyLatestApplication();
+        
+        String symbolicName = "latest.catalog.application.id";
+
+        TypeSummary application = client().path("/catalog/types/" + symbolicName + "/LaTeSt")
+                .get(TypeSummary.class);
+        assertEquals(application.getVersion(), TEST_LASTEST_VERSION);
+
+        application = client().path("/catalog/types/" + symbolicName + "/LATEST")
+                .get(TypeSummary.class);
+        assertEquals(application.getVersion(), TEST_LASTEST_VERSION);
+    }
+
+    @Test
+    public void testGetOnlyLatestEntity() {
+        String symbolicName = "latest.catalog.entity.id";
+        String itemType = "entity";
+        String serviceType = "org.apache.brooklyn.core.test.entity.TestEntity";
+
+        addTestCatalogItem(symbolicName, itemType, TEST_VERSION, serviceType);
+        addTestCatalogItem(symbolicName, itemType, TEST_LASTEST_VERSION, serviceType);
+
+        TypeSummary application = client().path("/catalog/types/" + symbolicName + "/latest")
+                .get(TypeSummary.class);
+        assertEquals(application.getVersion(), TEST_LASTEST_VERSION);
+    }
+
+    @Test
+    public void testGetOnlyLatestLocation() {
+        String symbolicName = "latest.catalog.location.id";
+        String itemType = "location";
+        String serviceType = "localhost";
+
+        addTestCatalogItem(symbolicName, itemType, TEST_VERSION, serviceType);
+        addTestCatalogItem(symbolicName, itemType, TEST_LASTEST_VERSION, serviceType);
+
+        TypeSummary application = client().path("/catalog/types/" + symbolicName + "/latest")
+                .get(TypeSummary.class);
+        assertEquals(application.getVersion(), TEST_LASTEST_VERSION);
+    }
+
+
+    @Test
+    public void testForceUpdateForYAML() {
+        String symbolicName = "force.update.catalog.application.id";
+        String itemType = "template";
+        String initialName = "My Catalog App";
+        String initialDescription = "My description";
+        String updatedName = initialName + " 2";
+        String updatedDescription = initialDescription + " 2";
+
+        String initialYaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: " + itemType,
+                "  name: " + initialName,
+                "  description: " + initialDescription,
+                "  icon_url: classpath:///bridge-small.png",
+                "  version: " + TEST_VERSION,
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
+        String updatedYaml = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  itemType: " + itemType,
+                "  name: " + updatedName,
+                "  description: " + updatedDescription,
+                "  icon_url: classpath:///bridge-small.png",
+                "  version: " + TEST_VERSION,
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
+
+        client().path("/catalog/bundles").header(HttpHeaders.CONTENT_TYPE, "application/yaml").post(initialYaml);
+
+        TypeDetail initialApplication = client().path("/catalog/types/" + symbolicName + "/" + TEST_VERSION)
+                .get(TypeDetail.class);
+        assertEquals(initialApplication.getDisplayName(), initialName);
+        assertEquals(initialApplication.getDescription(), initialDescription);
+
+        Response invalidResponse = client().path("/catalog/bundles").header(HttpHeaders.CONTENT_TYPE, "application/yaml").post(updatedYaml);
+
+        assertEquals(invalidResponse.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+
+        Response validResponse = client().path("/catalog/bundles").query("force", true).header(HttpHeaders.CONTENT_TYPE, "application/yaml").post(updatedYaml);
+
+        assertEquals(validResponse.getStatus(), Response.Status.CREATED.getStatusCode());
+
+        TypeSummary application = client().path("/catalog/types/" + symbolicName + "/" + TEST_VERSION)
+                .get(TypeSummary.class);
+        assertEquals(application.getDisplayName(), updatedName);
+        assertEquals(application.getDescription(), updatedDescription);
+    }
+
+    @Test
+    public void testForceUpdateForZip() throws Exception {
+        final String symbolicName = "force.update.zip.catalog.application.id";
+        final String initialName = "My Catalog App";
+        final String initialDescription = "My Description";
+        final String updatedName = initialName + " 2";
+        final String updatedDescription = initialDescription  +" 2";
+
+        File initialZip = createZip(ImmutableMap.<String, String>of("catalog.bom", Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  bundle: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  id: " + symbolicName,
+                "  itemType: entity",
+                "  name: " + initialName,
+                "  description: " + initialDescription,
+                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity")));
+        File updatedZip = createZip(ImmutableMap.<String, String>of("catalog.bom", Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  bundle: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  id: " + symbolicName,
+                "  itemType: entity",
+                "  name: " + updatedName,
+                "  description: " + updatedDescription,
+                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity")));
+
+        client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(initialZip)));
+
+        TypeSummary initialEntity = client().path("/catalog/types/" + symbolicName + "/" + TEST_VERSION)
+                .get(TypeSummary.class);
+        assertEquals(initialEntity.getDisplayName(), initialName);
+        assertEquals(initialEntity.getDescription(), initialDescription);
+
+        Response invalidResponse = client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .post(Streams.readFully(new FileInputStream(updatedZip)));
+
+        assertEquals(invalidResponse.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+
+        Response validResponse = client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
+                .query("force", true)
+                .post(Streams.readFully(new FileInputStream(updatedZip)));
+
+        assertEquals(validResponse.getStatus(), Response.Status.CREATED.getStatusCode());
+
+        TypeSummary entity = client().path("/catalog/types/" + symbolicName + "/" + TEST_VERSION)
+                .get(TypeSummary.class);
+        assertEquals(entity.getDisplayName(), updatedName);
+        assertEquals(entity.getDescription(), updatedDescription);
+    }
+
+    @Test
+    public void testForceUpdateForJar() throws Exception {
+        final String symbolicName = "force.update.jar.catalog.application.id";
+        final String initialName = "My Catalog App";
+        final String initialDescription = "My Description";
+        final String updatedName = initialName + " 2";
+        final String updatedDescription = initialDescription  +" 2";
+
+        File initialJar = createJar(ImmutableMap.<String, String>of("catalog.bom", Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  bundle: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  id: " + symbolicName,
+                "  itemType: entity",
+                "  name: " + initialName,
+                "  description: " + initialDescription,
+                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity")));
+        File updatedJar = createJar(ImmutableMap.<String, String>of("catalog.bom", Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  bundle: " + symbolicName,
+                "  version: " + TEST_VERSION,
+                "  id: " + symbolicName,
+                "  itemType: entity",
+                "  name: " + updatedName,
+                "  description: " + updatedDescription,
+                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
+                "  item:",
+                "    type: org.apache.brooklyn.core.test.entity.TestEntity")));
+
+        client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
+                .post(Streams.readFully(new FileInputStream(initialJar)));
+
+        TypeSummary initialEntity = client().path("/catalog/types/" + symbolicName + "/" + TEST_VERSION)
+                .get(TypeSummary.class);
+        assertEquals(initialEntity.getDisplayName(), initialName);
+        assertEquals(initialEntity.getDescription(), initialDescription);
+
+        Response invalidResponse = client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
+                .post(Streams.readFully(new FileInputStream(updatedJar)));
+
+        assertEquals(invalidResponse.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
+
+        Response validResponse = client().path("/catalog/bundles")
+                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
+                .query("force", true)
+                .post(Streams.readFully(new FileInputStream(updatedJar)));
+
+        assertEquals(validResponse.getStatus(), Response.Status.CREATED.getStatusCode());
+
+        TypeSummary entity = client().path("/catalog/types/" + symbolicName + "/" + TEST_VERSION)
+                .get(TypeSummary.class);
+        assertEquals(entity.getDisplayName(), updatedName);
+        assertEquals(entity.getDescription(), updatedDescription);
+    }
+    
+    // TODO traits no longer always set - we have supertypes so not needed, we should investigate when they are and when they aren't
+    // and switch those to setting and using the supertypes
+    private boolean checkTraits(boolean currentExpectedToBeWorking) {
+        return currentExpectedToBeWorking;
+    }
+
+}


[08/11] brooklyn-server git commit: remaining PR comments

Posted by he...@apache.org.
remaining PR comments

move /bundles and /types under /catalog, remove subtype, remove autodetect create in POST /catalog/bundles


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

Branch: refs/heads/master
Commit: 05ad45e7ff1f78f655d7d3aa03aae557e70b8fb5
Parents: 206f744
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri Sep 22 13:07:19 2017 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Fri Sep 22 13:28:43 2017 +0100

----------------------------------------------------------------------
 .../org/apache/brooklyn/rest/api/BundleApi.java |   23 +-
 .../apache/brooklyn/rest/api/CatalogApi.java    |   56 +-
 .../apache/brooklyn/rest/api/SubtypeApi.java    |   96 --
 .../org/apache/brooklyn/rest/api/TypeApi.java   |    9 +-
 .../apache/brooklyn/rest/BrooklynRestApi.java   |    2 -
 .../brooklyn/rest/resources/BundleResource.java |   20 -
 .../rest/resources/SubtypeResource.java         |   82 --
 .../brooklyn/rest/resources/TypeResource.java   |   21 +-
 .../BundleAndTypeAndSubtypeResourcesTest.java   | 1093 -----------------
 .../resources/BundleAndTypeResourcesTest.java   | 1098 ++++++++++++++++++
 10 files changed, 1179 insertions(+), 1321 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/05ad45e7/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java
index 6bf46ea..2eb5ed8 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/BundleApi.java
@@ -42,8 +42,8 @@ import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 
-@Path("/bundles")
-@Api("Bundles")
+@Path("/catalog/bundles")
+@Api("Catalog Bundles")
 @Consumes(MediaType.APPLICATION_JSON)
 @Produces(MediaType.APPLICATION_JSON)
 public interface BundleApi {
@@ -144,23 +144,4 @@ public interface BundleApi {
             @QueryParam("force") @DefaultValue("false")
             Boolean force);
 
-    @POST
-    @Consumes // anything (if doesn't match other methods with specific content types
-    @ApiOperation(
-            value = "Adds types to the registry from the given item, autodetecting type as ZIP/JAR or BOM YAML",
-            response = BundleInstallationRestResult.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 createAutodetecting(
-            @ApiParam(
-                    name = "item",
-                    value = "Item to install, as JAR/ZIP or Catalog YAML (autodetected)",
-                    required = true)
-                    byte[] item,
-            @ApiParam(name = "force", value = "Whether to forcibly remove it, even if in use and/or errors", required = false, defaultValue = "false")
-            @QueryParam("force") @DefaultValue("false")
-            Boolean force);
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/05ad45e7/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 3cbae5b..cb4298f 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
@@ -47,9 +47,6 @@ import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 
-/** @deprecated since 0.12.0 use /bundle, /type, and /subtype */
-// but we will probably keep this around for a while as many places use it
-@Deprecated
 @Path("/catalog")
 @Api("Catalog")
 @Consumes(MediaType.APPLICATION_JSON)
@@ -156,6 +153,9 @@ public interface CatalogApi {
             @QueryParam("forceUpdate") @DefaultValue("false")
                     boolean forceUpdate);
     
+    /** @deprecated since 0.12.0 delete the bundle via DELETE /catalog/bundles/xxx */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @DELETE
     @Path("/applications/{symbolicName}/{version}")
     @ApiOperation(
@@ -173,6 +173,9 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "The version identifier of the application or template to delete", required = true)
         @PathParam("version") String version) throws Exception;
 
+    /** @deprecated since 0.12.0 delete the bundle via DELETE /catalog/bundles/xxx */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @DELETE
     @Path("/entities/{symbolicName}/{version}")
     @ApiOperation(
@@ -190,6 +193,9 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "The version identifier of the entity or template to delete", required = true)
         @PathParam("version") String version) throws Exception;
 
+    /** @deprecated since 0.12.0 delete the bundle via DELETE /catalog/bundles/xxx */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @DELETE
     @Path("/policies/{policyId}/{version}")
     @ApiOperation(
@@ -206,6 +212,9 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "The version identifier of the policy to delete", required = true)
         @PathParam("version") String version) throws Exception;
 
+    /** @deprecated since 0.12.0 delete the bundle via DELETE /catalog/bundles/xxx */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @DELETE
     @Path("/locations/{locationId}/{version}")
     @ApiOperation(
@@ -223,6 +232,9 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "The version identifier of the location to delete", required = true)
         @PathParam("version") String version) throws Exception;
 
+    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @GET
     @Path("/entities")
     @ApiOperation(value = "List available entity types optionally matching a query", 
@@ -237,6 +249,9 @@ public interface CatalogApi {
         @QueryParam("allVersions") @DefaultValue("false") boolean includeAllVersions);
 
     // bad name - it is just templates
+    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @GET
     @Path("/applications")
     @ApiOperation(value = "Fetch a list of templates (for applications) optionally matching a query", 
@@ -250,6 +265,9 @@ public interface CatalogApi {
             @ApiParam(name = "allVersions", value = "Include all versions (defaults false, only returning the best version)")
             @QueryParam("allVersions") @DefaultValue("false") boolean includeAllVersions);
 
+    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @GET
     @Path("/entities/{symbolicName}/{version}")
     @ApiOperation(
@@ -268,6 +286,9 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "The version identifier of the entity or template to retrieve", required = true)
         @PathParam("version") String version) throws Exception;
 
+    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @GET
     @Path("/applications/{symbolicName}/{version}")
     @ApiOperation(
@@ -286,6 +307,9 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "The version identifier of the application to retrieve", required = true)
         @PathParam("version") String version) throws Exception;
 
+    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @GET
     @Path("/policies")
     @ApiOperation(value = "List available policies optionally matching a query", 
@@ -299,6 +323,9 @@ public interface CatalogApi {
             @ApiParam(name = "allVersions", value = "Include all versions (defaults false, only returning the best version)")
             @QueryParam("allVersions") @DefaultValue("false") boolean includeAllVersions);
 
+    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @GET
     @Path("/policies/{policyId}/{version}")
     @ApiOperation(
@@ -316,6 +343,9 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "The version identifier of the application to retrieve", required = true)
         @PathParam("version") String version) throws Exception;
 
+    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @GET
     @Path("/locations")
     @ApiOperation(value = "List available locations optionally matching a query", 
@@ -329,6 +359,9 @@ public interface CatalogApi {
             @ApiParam(name = "allVersions", value = "Include all versions (defaults false, only returning the best version)")
             @QueryParam("allVersions") @DefaultValue("false") boolean includeAllVersions);
 
+    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @GET
     @Path("/locations/{locationId}/{version}")
     @ApiOperation(
@@ -364,6 +397,10 @@ public interface CatalogApi {
         @ApiParam(name = "version", value = "version identifier of catalog item (application, entity, policy, location)", required=true)
         @PathParam("version") String version);
     
+    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=...;
+     * deprecation/disabling needs to be done in the bundle, and we might support deprecating/disabling bundles */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @POST
     @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM, MediaType.TEXT_PLAIN})
     @ApiResponses(value = {
@@ -376,6 +413,10 @@ public interface CatalogApi {
         @ApiParam(name = "deprecated", value = "Whether or not the catalog item is deprecated", required = true)
         boolean deprecated);
     
+    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=...;
+     * deprecation/disabling needs to be done in the bundle, and we might support deprecating/disabling bundles */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @POST
     @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM, MediaType.TEXT_PLAIN})
     @ApiResponses(value = {
@@ -388,6 +429,9 @@ public interface CatalogApi {
         @ApiParam(name = "disabled", value = "Whether or not the catalog item is disabled", required = true)
         boolean disabled);
 
+    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @GET
     @Path("/enrichers")
     @ApiOperation(value = "List available enrichers types optionally matching a query",
@@ -401,6 +445,9 @@ public interface CatalogApi {
             @ApiParam(name = "allVersions", value = "Include all versions (defaults false, only returning the best version)")
             @QueryParam("allVersions") @DefaultValue("false") boolean includeAllVersions);
 
+    /** @deprecated since 0.12.0 use /catalog/bundles and /catalog/types?supertype=... */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @GET
     @Path("/enrichers/{enricherId}/{version}")
     @ApiOperation(value = "Fetch an enricher's definition from the catalog",
@@ -415,6 +462,9 @@ public interface CatalogApi {
             @ApiParam(name = "version", value = "The version identifier of the enricher to retrieve", required = true)
             @PathParam("version") String version) throws Exception;
 
+    /** @deprecated since 0.12.0 delete the bundle via DELETE /catalog/bundles/xxx */
+    // but we will probably keep this around for a while as many places use it
+    @Deprecated
     @DELETE
     @Path("/enrichers/{enricherId}/{version}")
     @ApiOperation(value = "Deletes a specific version of an enricher's definition from the catalog")

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/05ad45e7/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/SubtypeApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/SubtypeApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/SubtypeApi.java
deleted file mode 100644
index 01890c5..0000000
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/SubtypeApi.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.rest.api;
-
-import java.util.List;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DefaultValue;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-
-import org.apache.brooklyn.rest.domain.TypeSummary;
-
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-
-@Path("/subtypes")
-@Api("Subtypes")
-@Consumes(MediaType.APPLICATION_JSON)
-@Produces(MediaType.APPLICATION_JSON)
-public interface SubtypeApi {
-
-    @Path("/{supertype}")
-    @GET
-    @ApiOperation(value = "Get all known types which declare the given argument as a supertype", 
-            response = TypeSummary.class, responseContainer = "List")
-    public List<TypeSummary> list(
-        @ApiParam(name = "supertype", value = "Supertype to query", required = true)
-        @PathParam("supertype")
-        String supertype,
-        @ApiParam(name = "versions", value = "Whether to list 'latest' of each symbolic-name or 'all' versions", required = false, defaultValue = "latest")
-        @QueryParam("versions")
-        String versions,
-        @ApiParam(name = "regex", value = "Regular expression to search for")
-        @QueryParam("regex") @DefaultValue("") String regex,
-        @ApiParam(name = "fragment", value = "Substring case-insensitive to search for")
-        @QueryParam("fragment") @DefaultValue("") String fragment);
-
-    // conveniences for common items where internally it uses java class name
-    // caller can of course use /subtypes/org.apache.brooklyn.api.Entity
-    
-    @GET @Path("/application")
-    @ApiOperation(value = "Get all applications", response = TypeSummary.class, responseContainer = "List")
-    public List<TypeSummary> listApplications(@ApiParam(name = "versions", value = "Whether to list 'latest' of each symbolic-name or 'all' versions", required = false, defaultValue = "latest") @QueryParam("versions") String versions,
-        @ApiParam(name = "regex", value = "Regular expression to search for") @QueryParam("regex") @DefaultValue("") String regex,
-        @ApiParam(name = "fragment", value = "Substring case-insensitive to search for") @QueryParam("fragment") @DefaultValue("") String fragment);
-    
-    @GET @Path("/entity")
-    @ApiOperation(value = "Get all entities", response = TypeSummary.class, responseContainer = "List")
-    public List<TypeSummary> listEntities(@ApiParam(name = "versions", value = "Whether to list 'latest' of each symbolic-name or 'all' versions", required = false, defaultValue = "latest") @QueryParam("versions") String versions,
-        @ApiParam(name = "regex", value = "Regular expression to search for") @QueryParam("regex") @DefaultValue("") String regex,
-        @ApiParam(name = "fragment", value = "Substring case-insensitive to search for") @QueryParam("fragment") @DefaultValue("") String fragment);
-    
-    @GET @Path("/policy")
-    @ApiOperation(value = "Get all policies", response = TypeSummary.class, responseContainer = "List")
-    public List<TypeSummary> listPolicies(@ApiParam(name = "versions", value = "Whether to list 'latest' of each symbolic-name or 'all' versions", required = false, defaultValue = "latest") @QueryParam("versions") String versions,
-        @ApiParam(name = "regex", value = "Regular expression to search for") @QueryParam("regex") @DefaultValue("") String regex,
-        @ApiParam(name = "fragment", value = "Substring case-insensitive to search for") @QueryParam("fragment") @DefaultValue("") String fragment);
-    
-    @GET @Path("/enricher")
-    @ApiOperation(value = "Get all enrichers", response = TypeSummary.class, responseContainer = "List")
-    public List<TypeSummary> listEnrichers(@ApiParam(name = "versions", value = "Whether to list 'latest' of each symbolic-name or 'all' versions", required = false, defaultValue = "latest") @QueryParam("versions") String versions,
-        @ApiParam(name = "regex", value = "Regular expression to search for") @QueryParam("regex") @DefaultValue("") String regex,
-        @ApiParam(name = "fragment", value = "Substring case-insensitive to search for") @QueryParam("fragment") @DefaultValue("") String fragment);
-
-    @GET @Path("/location")
-    // note some (deprecated) locations are only available on location manager 
-    @ApiOperation(value = "Get all locations stored in the registry", response = TypeSummary.class, responseContainer = "List")
-    public List<TypeSummary> listLocations(@ApiParam(name = "versions", value = "Whether to list 'latest' of each symbolic-name or 'all' versions", required = false, defaultValue = "latest") @QueryParam("versions") String versions,
-        @ApiParam(name = "regex", value = "Regular expression to search for") @QueryParam("regex") @DefaultValue("") String regex,
-        @ApiParam(name = "fragment", value = "Substring case-insensitive to search for") @QueryParam("fragment") @DefaultValue("") String fragment);
-
-    // in future could have others, eg: tasks, feeds, etc
-    
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/05ad45e7/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/TypeApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/TypeApi.java b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/TypeApi.java
index f8167b5..914dce2 100644
--- a/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/TypeApi.java
+++ b/rest/rest-api/src/main/java/org/apache/brooklyn/rest/api/TypeApi.java
@@ -39,8 +39,8 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
 
-@Path("/types")
-@Api("Types")
+@Path("/catalog/types")
+@Api("Catalog Types")
 @Consumes(MediaType.APPLICATION_JSON)
 @Produces(MediaType.APPLICATION_JSON)
 @Beta
@@ -51,8 +51,11 @@ public interface TypeApi {
             response = TypeSummary.class,
             responseContainer = "List")
     public List<TypeSummary> list(
+        @ApiParam(name = "supertype", value = "Supertype to require (beta, currently intended only for 'entity', 'policy', 'enricher', and 'location')", required = false)
+        @QueryParam("supertype")
+        String supertype,
         @ApiParam(name = "versions", value = "Whether to list 'latest' of each symbolic-name or 'all' versions", 
-            required = false, defaultValue = "latest")
+        required = false, defaultValue = "latest")
         @QueryParam("versions")
         String versions,
         @ApiParam(name = "regex", value = "Regular expression to search for (in name and description)")

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/05ad45e7/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
index b3eb4e4..e172127 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynRestApi.java
@@ -38,7 +38,6 @@ import org.apache.brooklyn.rest.resources.PolicyResource;
 import org.apache.brooklyn.rest.resources.ScriptResource;
 import org.apache.brooklyn.rest.resources.SensorResource;
 import org.apache.brooklyn.rest.resources.ServerResource;
-import org.apache.brooklyn.rest.resources.SubtypeResource;
 import org.apache.brooklyn.rest.resources.TypeResource;
 import org.apache.brooklyn.rest.resources.UsageResource;
 import org.apache.brooklyn.rest.util.DefaultExceptionMapper;
@@ -56,7 +55,6 @@ public class BrooklynRestApi {
         resources.add(new LocationResource());
         resources.add(new CatalogResource());
         resources.add(new TypeResource());
-        resources.add(new SubtypeResource());
         resources.add(new BundleResource());
         resources.add(new ApplicationResource());
         resources.add(new EntityResource());

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/05ad45e7/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java
index b4664ea..24fe3ca 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/BundleResource.java
@@ -19,7 +19,6 @@
 package org.apache.brooklyn.rest.resources;
 
 import java.io.ByteArrayInputStream;
-import java.io.InputStreamReader;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
@@ -44,7 +43,6 @@ import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.exceptions.ReferenceWithError;
 import org.apache.brooklyn.util.osgi.VersionedName;
 import org.apache.brooklyn.util.osgi.VersionedName.VersionedNameComparator;
-import org.apache.brooklyn.util.yaml.Yamls;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -168,22 +166,4 @@ public class BundleResource extends AbstractBrooklynRestResource implements Bund
         return Response.status(Status.CREATED).entity( resultR ).build();
     }
 
-    @Override
-    public Response createAutodetecting(byte[] item, Boolean force) {
-        Throwable yamlException = null;
-        try {
-            MutableList.copyOf( Yamls.parseAll(new InputStreamReader(new ByteArrayInputStream(item))) );
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            yamlException = e;
-        }
-        
-        if (yamlException==null) {
-            // treat as yaml if it parsed
-            return createFromYaml(new String(item), force);
-        }
-        
-        return createFromArchive(item, force);
-    }
 }
-

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/05ad45e7/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/SubtypeResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/SubtypeResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/SubtypeResource.java
deleted file mode 100644
index 12716b3..0000000
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/SubtypeResource.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.rest.resources;
-
-import java.util.List;
-
-import org.apache.brooklyn.api.entity.Application;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.policy.Policy;
-import org.apache.brooklyn.api.sensor.Enricher;
-import org.apache.brooklyn.api.typereg.RegisteredType;
-import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
-import org.apache.brooklyn.core.typereg.RegisteredTypes;
-import org.apache.brooklyn.rest.api.SubtypeApi;
-import org.apache.brooklyn.rest.domain.TypeSummary;
-import org.apache.brooklyn.rest.filter.HaHotStateRequired;
-import org.apache.brooklyn.util.collections.MutableList;
-import org.apache.brooklyn.util.text.StringPredicates;
-import org.apache.brooklyn.util.text.Strings;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-
-@HaHotStateRequired
-public class SubtypeResource extends AbstractBrooklynRestResource implements SubtypeApi {
-
-    @SuppressWarnings("unused")
-    private static final Logger log = LoggerFactory.getLogger(SubtypeResource.class);
-
-    @Override
-    public List<TypeSummary> list(String supertype, String versions, String regex, String fragment) {
-        List<Predicate<RegisteredType>> filters = MutableList.<Predicate<RegisteredType>>of()
-            .append(RegisteredTypePredicates.entitledToSee(mgmt()))
-            .append(RegisteredTypePredicates.subtypeOf(supertype));
-        if (TypeResource.isLatestOnly(versions, true)) {
-            // TODO inefficient - does n^2 comparisons where n is sufficient
-            // create RegisteredTypes.filterBestVersions to do a list after the initial parse
-            // (and javadoc in predicate method below)
-            filters.add(RegisteredTypePredicates.isBestVersion(mgmt()));
-        }
-        if (Strings.isNonEmpty(regex)) {
-            filters.add(RegisteredTypePredicates.nameOrAlias(StringPredicates.containsRegex(regex)));
-        }
-        if (Strings.isNonEmpty(fragment)) {
-            filters.add(RegisteredTypePredicates.nameOrAlias(StringPredicates.containsLiteralIgnoreCase(fragment)));
-        }
-        Predicate<RegisteredType> filter = Predicates.and(filters);
-        ImmutableList<RegisteredType> sortedItems =
-            FluentIterable.from(brooklyn().getTypeRegistry().getMatching(filter))
-                .toSortedList(RegisteredTypes.RegisteredTypeNameThenBestFirstComparator.INSTANCE);
-        return TypeResource.toTypeSummary(brooklyn(), sortedItems, ui.getBaseUriBuilder());
-    }
-    
-    @Override public List<TypeSummary> listApplications(String versions, String regex, String fragment) { return list(Application.class.getName(), versions, regex, fragment); }
-    @Override public List<TypeSummary> listEntities(String versions, String regex, String fragment) { return list(Entity.class.getName(), versions, regex, fragment); }
-    @Override public List<TypeSummary> listPolicies(String versions, String regex, String fragment) { return list(Policy.class.getName(), versions, regex, fragment); }
-    @Override public List<TypeSummary> listEnrichers(String versions, String regex, String fragment) { return list(Enricher.class.getName(), versions, regex, fragment); }
-    @Override public List<TypeSummary> listLocations(String versions, String regex, String fragment) { return list(Location.class.getName(), versions, regex, fragment); }
-    
-}
-

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/05ad45e7/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/TypeResource.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/TypeResource.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/TypeResource.java
index 52627e3..cf9f40f 100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/TypeResource.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/TypeResource.java
@@ -27,6 +27,11 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriBuilder;
 
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.api.sensor.Enricher;
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
@@ -75,9 +80,23 @@ public class TypeResource extends AbstractBrooklynRestResource implements TypeAp
     }
     
     @Override
-    public List<TypeSummary> list(String versions, String regex, String fragment) {
+    public List<TypeSummary> list(String supertype, String versions, String regex, String fragment) {
         List<Predicate<RegisteredType>> filters = MutableList.<Predicate<RegisteredType>>of()
             .append(RegisteredTypePredicates.entitledToSee(mgmt()));
+        if (Strings.isNonBlank(supertype)) {
+            // rewrite certain well known ones
+            // (in future this should happen automatically as Entity.class should be known as user-friendly name 'entity') 
+            if ("entity".equals(supertype)) supertype = Entity.class.getName();
+            else if ("enricher".equals(supertype)) supertype = Enricher.class.getName();
+            else if ("policy".equals(supertype)) supertype = Policy.class.getName();
+            else if ("location".equals(supertype)) supertype = Location.class.getName();
+            // TODO application probably isn't at all interesting; keep it for backward compatibility,
+            // and meanwhile sort out things like "template" vs "quick launch"
+            // (probably adding tags on the API)
+            else if ("application".equals(supertype)) supertype = Application.class.getName();
+            
+            filters.add(RegisteredTypePredicates.subtypeOf(supertype));
+        }
         if (TypeResource.isLatestOnly(versions, true)) {
             // TODO inefficient - does n^2 comparisons where n is sufficient
             // create RegisteredTypes.filterBestVersions to do a list after the initial parse

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/05ad45e7/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeAndSubtypeResourcesTest.java
----------------------------------------------------------------------
diff --git a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeAndSubtypeResourcesTest.java b/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeAndSubtypeResourcesTest.java
deleted file mode 100644
index 03578f1..0000000
--- a/rest/rest-resources/src/test/java/org/apache/brooklyn/rest/resources/BundleAndTypeAndSubtypeResourcesTest.java
+++ /dev/null
@@ -1,1093 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.rest.resources;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-
-import java.awt.Image;
-import java.awt.Toolkit;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-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.policy.Policy;
-import org.apache.brooklyn.api.typereg.ManagedBundle;
-import org.apache.brooklyn.api.typereg.OsgiBundleWithUrl;
-import org.apache.brooklyn.api.typereg.RegisteredType;
-import org.apache.brooklyn.core.entity.EntityPredicates;
-import org.apache.brooklyn.core.mgmt.ha.OsgiManager;
-import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
-import org.apache.brooklyn.core.mgmt.osgi.OsgiStandaloneTest;
-import org.apache.brooklyn.core.test.entity.TestEntity;
-import org.apache.brooklyn.enricher.stock.Aggregator;
-import org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy;
-import org.apache.brooklyn.rest.domain.BundleInstallationRestResult;
-import org.apache.brooklyn.rest.domain.BundleSummary;
-import org.apache.brooklyn.rest.domain.TypeDetail;
-import org.apache.brooklyn.rest.domain.TypeSummary;
-import org.apache.brooklyn.rest.testing.BrooklynRestResourceTest;
-import org.apache.brooklyn.test.Asserts;
-import org.apache.brooklyn.test.support.TestResourceUnavailableException;
-import org.apache.brooklyn.util.collections.MutableList;
-import org.apache.brooklyn.util.collections.MutableMap;
-import org.apache.brooklyn.util.collections.MutableSet;
-import org.apache.brooklyn.util.core.ResourceUtils;
-import org.apache.brooklyn.util.core.osgi.BundleMaker;
-import org.apache.brooklyn.util.javalang.JavaClassNames;
-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.HttpHeaders;
-import org.eclipse.jetty.http.HttpStatus;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-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;
-
-public class BundleAndTypeAndSubtypeResourcesTest extends BrooklynRestResourceTest {
-
-    private static final Logger log = LoggerFactory.getLogger(BundleAndTypeAndSubtypeResourcesTest.class);
-    
-    private static String TEST_VERSION = "0.1.2";
-    private static String TEST_LASTEST_VERSION = "0.1.3";
-
-    private Collection<ManagedBundle> initialBundles;
-
-    @Override
-    protected boolean useLocalScannedCatalog() {
-        return true;
-    }
-
-    @Override
-    protected void initClass() throws Exception {
-        super.initClass();
-        // cache initially installed bundles
-        OsgiManager osgi = ((ManagementContextInternal)getManagementContext()).getOsgiManager().get();
-        initialBundles = osgi.getManagedBundles().values();
-    }
-    
-    protected void initMethod() throws Exception {
-        super.initMethod();
-        
-        // and reset OSGi container
-        OsgiManager osgi = ((ManagementContextInternal)getManagementContext()).getOsgiManager().get();
-        for (ManagedBundle b: osgi.getManagedBundles().values()) {
-            if (!initialBundles.contains(b)) {
-                osgi.uninstallUploadedBundle(b);
-            }
-        }
-    }
-    
-    @Test
-    /** based on CampYamlLiteTest */
-    public void testRegisterCustomEntityTopLevelSyntaxWithBundleWhereEntityIsFromCoreAndIconFromBundle() {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
-
-        String symbolicName = "my.catalog.entity.id";
-        String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
-        String yaml = Joiner.on("\n").join(
-                "brooklyn.catalog:",
-                "  id: " + symbolicName,
-                "  version: " + TEST_VERSION,
-                "  itemType: entity",
-                "  name: My Catalog App",
-                "  description: My description",
-                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
-                "  libraries:",
-                "  - url: " + bundleUrl,
-                "  item:",
-                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
-
-        Response response = client().path("/bundles")
-                .post(yaml);
-
-        assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
-        BundleInstallationRestResult installed = response.readEntity(BundleInstallationRestResult.class);
-        Asserts.assertSize(installed.getTypes().values(), 1);
-        TypeSummary installedItem = installed.getTypes().get(symbolicName+":"+TEST_VERSION);
-        Assert.assertNotNull(installedItem, ""+installed.getTypes());
-
-        TypeDetail entityItem = client().path("/types/"+symbolicName + "/" + TEST_VERSION)
-                .get(TypeDetail.class);
-
-        Assert.assertEquals(new TypeSummary(entityItem), installedItem);
-        Assert.assertNotNull(entityItem.getPlan());
-        Assert.assertTrue(((String)entityItem.getPlan().getData()).contains("org.apache.brooklyn.core.test.entity.TestEntity"));
-
-        assertEquals(entityItem.getSymbolicName(), symbolicName);
-        assertEquals(entityItem.getVersion(), TEST_VERSION);
-
-        // also check it's included in various lists
-        List<TypeSummary> list1 = client().path("/types/"+symbolicName).get(new GenericType<List<TypeSummary>>() {});
-        assertEquals(list1, MutableList.of(installedItem));
-        List<TypeSummary> list2 = client().path("/types").get(new GenericType<List<TypeSummary>>() {});
-        Assert.assertTrue(list2.contains(installedItem), ""+list2);
-        List<TypeSummary> list3 = client().path("/subtypes/entity").get(new GenericType<List<TypeSummary>>() {});
-        Assert.assertTrue(list3.contains(installedItem), ""+list3);
-        
-        // and internally let's check we have libraries
-        RegisteredType item = getManagementContext().getTypeRegistry().get(symbolicName, TEST_VERSION);
-        Assert.assertNotNull(item);
-        Collection<OsgiBundleWithUrl> libs = item.getLibraries();
-        assertEquals(libs.size(), 1);
-        assertEquals(Iterables.getOnlyElement(libs).getUrl(), bundleUrl);
-
-        // now let's check other things on the item
-        URI expectedIconUrl = URI.create(getEndpointAddress() + "/types/" + symbolicName + "/" + entityItem.getVersion()+"/icon").normalize();
-        assertEquals(entityItem.getDisplayName(), "My Catalog App");
-        assertEquals(entityItem.getDescription(), "My description");
-        assertEquals(entityItem.getIconUrl(), expectedIconUrl.getPath());
-        assertEquals(item.getIconUrl(), "classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif");
-
-        // an InterfacesTag should be created for every catalog item
-        if (checkTraits(false)) {
-            @SuppressWarnings("unchecked")
-            Map<String, List<String>> traitsMapTag = Iterables.getOnlyElement(Iterables.filter(entityItem.getTags(), Map.class));
-            List<String> actualInterfaces = traitsMapTag.get("traits");
-            List<Class<?>> expectedInterfaces = Reflections.getAllInterfaces(TestEntity.class);
-            assertEquals(actualInterfaces.size(), expectedInterfaces.size());
-            for (Class<?> expectedInterface : expectedInterfaces) {
-                assertTrue(actualInterfaces.contains(expectedInterface.getName()));
-            }
-        }
-
-        byte[] iconData = client().path("/types/" + symbolicName + "/" + TEST_VERSION+"/icon").get(byte[].class);
-        assertEquals(iconData.length, 43);
-    }
-
-    @Test
-    public void testRegisterOsgiPolicyTopLevelSyntax() {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
-
-        String symbolicName = "my.catalog.entity.id."+JavaClassNames.niceClassAndMethod();
-        String policyType = "org.apache.brooklyn.test.osgi.entities.SimplePolicy";
-        String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
-
-        String yaml = Joiner.on("\n").join(
-                "brooklyn.catalog:",
-                "  id: " + symbolicName,
-                "  version: " + TEST_VERSION,
-                "  itemType: policy",
-                "  name: My Catalog App",
-                "  description: My description",
-                "  libraries:",
-                "  - url: " + bundleUrl,
-                "  item:",
-                "    type: " + policyType);
-
-        TypeSummary installedItem = Iterables.getOnlyElement( client().path("/bundles")
-                .post(yaml, BundleInstallationRestResult.class).getTypes().values() );
-
-        assertEquals(installedItem.getSymbolicName(), symbolicName);
-        assertEquals(installedItem.getVersion(), TEST_VERSION);
-        Assert.assertTrue(installedItem.getSupertypes().contains(Policy.class.getName()), ""+installedItem.getSupertypes());
-    }
-
-    @Test
-    public void testFilterListOfEntitiesByName() {
-        List<TypeSummary> entities = client().path("/types")
-                .query("fragment", "vaNIllasOFTWAREpROCESS").get(new GenericType<List<TypeSummary>>() {});
-        log.info("Matching entities: " + entities);
-        assertEquals(entities.size(), 1);
-
-        entities = client().path("/subtypes/entity")
-                .query("fragment", "vaNIllasOFTWAREpROCESS").get(new GenericType<List<TypeSummary>>() {});
-        log.info("Matching entities: " + entities);
-        assertEquals(entities.size(), 1);
-
-        List<TypeSummary> entities2 = client().path("/types")
-                .query("regex", "[Vv]an.[alS]+oftware\\w+").get(new GenericType<List<TypeSummary>>() {});
-        assertEquals(entities2.size(), 1);
-
-        assertEquals(entities, entities2);
-    
-        entities = client().path("/subtypes/entity")
-                .query("fragment", "bweqQzZ").get(new GenericType<List<TypeSummary>>() {});
-        Asserts.assertSize(entities, 0);
-
-        entities = client().path("/subtypes/entity")
-                .query("regex", "bweq+z+").get(new GenericType<List<TypeSummary>>() {});
-        Asserts.assertSize(entities, 0);
-    }
-
-    @Test
-    public void testGetCatalogEntityIconDetails() throws IOException {
-        String catalogItemId = "testGetCatalogEntityIconDetails";
-        addTestCatalogItemAsEntity(catalogItemId);
-        Response response = client().path(URI.create("/types/" + catalogItemId + "/" + TEST_VERSION + "/icon"))
-                .get();
-        response.bufferEntity();
-        Assert.assertEquals(response.getStatus(), 200);
-        Assert.assertEquals(response.getMediaType(), MediaType.valueOf("image/png"));
-        Image image = Toolkit.getDefaultToolkit().createImage(Files.readFile(response.readEntity(InputStream.class)));
-        Assert.assertNotNull(image);
-    }
-
-    private void addTestCatalogItemAsEntity(String catalogItemId) {
-        addTestCatalogItem(catalogItemId, "entity", TEST_VERSION, "org.apache.brooklyn.rest.resources.DummyIconEntity");
-    }
-
-    private void addTestCatalogItem(String catalogItemId, String itemType, String version, String service) {
-        String yaml = Joiner.on("\n").join(
-                "brooklyn.catalog:",
-                "  id: " + catalogItemId,
-                "  version: " + TEST_VERSION,
-                "  itemType: " + checkNotNull(itemType),
-                "  name: My Catalog App",
-                "  description: My description",
-                "  icon_url: classpath:///bridge-small.png",
-                "  version: " + version,
-                "  item:",
-                "    type: " + service);
-
-        client().path("/bundles").post(yaml);
-    }
-
-    @Test
-    public void testListPolicies() {
-        Set<TypeSummary> policies = client().path("/subtypes/policy")
-                .get(new GenericType<Set<TypeSummary>>() {});
-
-        assertTrue(policies.size() > 0);
-        TypeSummary asp = null;
-        for (TypeSummary p : policies) {
-            if (AutoScalerPolicy.class.getName().equals(p.getSymbolicName()))
-                asp = p;
-        }
-        Assert.assertNotNull(asp, "didn't find AutoScalerPolicy");
-    }
-
-    @Test
-    public void testLocationAddGetAndRemove() {
-        String symbolicName = "my.catalog.location.id";
-        String locationType = "localhost";
-        String yaml = Joiner.on("\n").join(
-                "brooklyn.catalog:",
-                "  id: " + symbolicName,
-                "  version: " + TEST_VERSION,
-                "  itemType: location",
-                "  name: My Catalog Location",
-                "  description: My description",
-                "  item:",
-                "    type: " + locationType);
-
-        // Create location item
-        Map<String, TypeSummary> items = client().path("/bundles")
-                .post(yaml, BundleInstallationRestResult.class).getTypes();
-        TypeSummary locationItem = Iterables.getOnlyElement(items.values());
-
-        assertEquals(locationItem.getSymbolicName(), symbolicName);
-        assertEquals(locationItem.getVersion(), TEST_VERSION);
-
-        // Retrieve location item
-        TypeDetail location = client().path("/types/"+symbolicName+"/"+TEST_VERSION).get(TypeDetail.class);
-        assertEquals(location.getSymbolicName(), symbolicName);
-
-        // Retrieve all locations
-        Set<TypeSummary> locations = client().path("/subtypes/location")
-                .get(new GenericType<Set<TypeSummary>>() {});
-        boolean found = false;
-        for (TypeSummary contender : locations) {
-            if (contender.getSymbolicName().equals(symbolicName)) {
-                found = true;
-                break;
-            }
-        }
-        Assert.assertTrue(found, "contenders="+locations);
-        
-        // Delete
-        Response deleteResponse = client().path("/bundles/"+locationItem.getContainingBundle().replaceAll(":", "/"))
-                .delete();
-        assertEquals(deleteResponse.getStatus(), Response.Status.OK.getStatusCode());
-        BundleInstallationRestResult deletionResponse = deleteResponse.readEntity(BundleInstallationRestResult.class);
-        Assert.assertEquals(deletionResponse.getBundle(), symbolicName+":"+TEST_VERSION);
-        Assert.assertEquals(deletionResponse.getTypes().keySet(), MutableSet.of(symbolicName+":"+TEST_VERSION));
-
-        Response getPostDeleteResponse = client().path("/types/"+symbolicName+"/"+TEST_VERSION)
-                .get();
-        assertEquals(getPostDeleteResponse.getStatus(), Response.Status.NOT_FOUND.getStatusCode());
-    }
-
-    @Test
-    public void testListEnrichers() {
-        Set<TypeSummary> enrichers = client().path("/subtypes/enricher")
-                .get(new GenericType<Set<TypeSummary>>() {});
-
-        assertTrue(enrichers.size() > 0);
-        TypeSummary asp = null;
-        for (TypeSummary p : enrichers) {
-            if (Aggregator.class.getName().equals(p.getSymbolicName()))
-                asp = p;
-        }
-        Assert.assertNotNull(asp, "didn't find Aggregator");
-    }
-
-    @Test
-    public void testEnricherAddGet() {
-        String symbolicName = "my.catalog.enricher.id";
-        String enricherType = "org.apache.brooklyn.enricher.stock.Aggregator";
-        String yaml = Joiner.on("\n").join(
-                "brooklyn.catalog:",
-                "  id: " + symbolicName,
-                "  version: " + TEST_VERSION,
-                "  itemType: enricher",
-                "  name: My Catalog Enricher",
-                "  description: My description",
-                "  item:",
-                "    type: " + enricherType);
-
-        // Create location item
-        Map<String, TypeSummary> items = client().path("/bundles")
-                .post(yaml, BundleInstallationRestResult.class).getTypes();
-        TypeSummary enricherItem = Iterables.getOnlyElement(items.values());
-
-        assertEquals(enricherItem.getSymbolicName(), symbolicName);
-        assertEquals(enricherItem.getVersion(), TEST_VERSION);
-
-        // Retrieve location item
-        TypeSummary enricher = client().path("/types/"+symbolicName+"/"+TEST_VERSION)
-                .get(TypeSummary.class);
-        assertEquals(enricher.getSymbolicName(), symbolicName);
-
-        // Retrieve all locations
-        Set<TypeSummary> enrichers = client().path("/subtypes/enricher")
-                .get(new GenericType<Set<TypeSummary>>() {});
-        boolean found = false;
-        for (TypeSummary contender : enrichers) {
-            if (contender.getSymbolicName().equals(symbolicName)) {
-                found = true;
-                break;
-            }
-        }
-        Assert.assertTrue(found, "contenders="+enrichers);
-    }
-
-    @Test
-    // osgi may fail in IDE, typically works on mvn CLI though
-    public void testRegisterOsgiEnricherTopLevelSyntax() {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
-
-        String symbolicName = "my.catalog.enricher.id";
-        String enricherType = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENRICHER;
-        String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
-
-        String yaml = Joiner.on("\n").join(
-                "brooklyn.catalog:",
-                "  id: " + symbolicName,
-                "  version: " + TEST_VERSION,
-                "  itemType: enricher",
-                "  name: My Catalog Enricher",
-                "  description: My description",
-                "  libraries:",
-                "  - url: " + bundleUrl,
-                "  item:",
-                "    type: " + enricherType);
-
-        TypeSummary installedItem = Iterables.getOnlyElement( client().path("/bundles")
-                .post(yaml, BundleInstallationRestResult.class).getTypes().values() );
-
-        assertEquals(installedItem.getSymbolicName(), symbolicName);
-        assertEquals(installedItem.getVersion(), TEST_VERSION);
-    }
-
-    @Test
-    public void testDeleteCustomEntityFromCatalog() {
-        String symbolicName = "my.catalog.app.id.to.subsequently.delete";
-        String yaml = Joiner.on("\n").join(
-                "brooklyn.catalog:",
-                "  id: " + symbolicName,
-                "  version: " + TEST_VERSION,
-                "  itemType: entity",
-                "  name: My Catalog App To Be Deleted",
-                "  description: My description",
-                "  item:",
-                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
-
-        client().path("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/yaml")
-                .post(yaml);
-
-        BundleSummary getInstalledBundle = client().path("/bundles/"+symbolicName+"/"+TEST_VERSION)
-            .get(BundleSummary.class);
-        assertEquals(getInstalledBundle.getSymbolicName(), symbolicName);
-        assertEquals(getInstalledBundle.getVersion(), TEST_VERSION);
-        Asserts.assertNotNull(getInstalledBundle.getTypes(), "expected 'types' in: "+getInstalledBundle.getExtraFields());
-        Asserts.assertStringContains(""+getInstalledBundle.getTypes(), "My Catalog App");
-
-        Response deleteResponse = client().path("/bundles/"+symbolicName+"/"+TEST_VERSION)
-                .delete();
-
-        assertEquals(deleteResponse.getStatus(), Response.Status.OK.getStatusCode());
-        // contents of delete tested in delete location method
-
-        Response getPostDeleteResponse = client().path("/bundles/"+symbolicName+"/"+TEST_VERSION)
-                .get();
-        assertEquals(getPostDeleteResponse.getStatus(), Response.Status.NOT_FOUND.getStatusCode());
-    }
-
-    private void addCatalogItemWithInvalidBundleUrl(String bundleUrl) {
-        String symbolicName = "my.catalog.entity.id";
-        String yaml = Joiner.on("\n").join(
-                "brooklyn.catalog:",
-                "  id: " + symbolicName,
-                "  version: " + TEST_VERSION,
-                "  itemType: entity",
-                "  name: My Catalog App",
-                "  description: My description",
-                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
-                "  libraries:",
-                "  - url: " + bundleUrl,
-                "  item:",
-                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
-
-        Response response = client().path("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-yaml")
-                .post(yaml);
-
-        assertEquals(response.getStatus(), HttpStatus.BAD_REQUEST_400);
-    }
-    
-    @Test
-    public void testAddUnreachableItem() {
-        addCatalogItemWithInvalidBundleUrl("http://0.0.0.0/can-not-connect");
-    }
-
-    @Test
-    public void testAddInvalidItem() {
-        //equivalent to HTTP response 200 text/html
-        addCatalogItemWithInvalidBundleUrl("classpath://not-a-jar-file.txt");
-    }
-
-    @Test
-    public void testAddMissingItem() {
-        //equivalent to HTTP response 404 text/html
-        addCatalogItemWithInvalidBundleUrl("classpath://missing-jar-file.txt");
-    }
-
-    @Test
-    public void testInvalidArchive() throws Exception {
-        File f = Os.newTempFile("osgi", "zip");
-
-        Response response = client().path("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
-                .post(Streams.readFully(new FileInputStream(f)));
-
-        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
-        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), "zip file is empty");
-    }
-
-    @Test
-    public void testArchiveWithoutBom() throws Exception {
-        File f = createZip(ImmutableMap.<String, String>of());
-
-        Response response = client().path("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
-                .post(Streams.readFully(new FileInputStream(f)));
-
-        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
-        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), "Missing bundle symbolic name in BOM or MANIFEST");
-    }
-
-    @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("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
-                .post(Streams.readFully(new FileInputStream(f)));
-
-        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
-        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), "Missing bundle symbolic name in BOM or MANIFEST");
-    }
-
-    @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("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
-                .post(Streams.readFully(new FileInputStream(f)));
-
-        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
-        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), 
-            "Missing bundle symbolic name in BOM or MANIFEST");
-    }
-
-    @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("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
-                .post(Streams.readFully(new FileInputStream(f)));
-
-        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
-        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), "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";
-        String wrongBundleName = "org.apache.brooklyn.test2";
-        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: "+wrongBundleName,
-                        "Bundle-Version: " + version,
-                        "Bundle-ManifestVersion: " + version)));
-
-        Response response = client().path("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
-                .post(Streams.readFully(new FileInputStream(f)));
-
-        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
-        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), 
-            "symbolic name mismatch",
-            wrongBundleName, bundle);
-    }
-
-    @Test
-    public void testJarWithoutMatchingVersion() throws Exception {
-        String name = "My Catalog App";
-        String bundle = "org.apache.brooklyn.test";
-        String version = "0.1.0";
-        String wrongVersion = "0.3.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: " + wrongVersion,
-                        "Bundle-ManifestVersion: " + wrongVersion)));
-
-        Response response = client().path("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
-                .post(Streams.readFully(new FileInputStream(f)));
-
-        assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
-        Asserts.assertStringContainsIgnoreCase(response.readEntity(String.class), 
-            "version mismatch",
-            wrongVersion, version);
-    }
-
-    @Test
-    public void testOsgiBundleWithBom() throws Exception {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
-        final String symbolicName = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_SYMBOLIC_NAME_FULL;
-        final String version = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_VERSION;
-        final String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
-        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:/org/apache/brooklyn/test/osgi/entities/icon.gif",
-                "  item:",
-                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
-        
-        f = bm.copyAdding(f, MutableMap.of(new ZipEntry("catalog.bom"), (InputStream) new ByteArrayInputStream(bom.getBytes())));
-
-        Response response = client().path("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
-                .post(Streams.readFully(new FileInputStream(f)));
-        
-        assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
-
-        TypeSummary entityItem = client().path("/types/"+symbolicName + "/" + version)
-                .get(TypeSummary.class);
-
-        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(), "org.apache.brooklyn.test.resources.osgi.brooklyn-test-osgi-entities");
-        assertEquals(lib.getSuppliedVersionString(), version);
-
-        // now let's check other things on the item
-        URI expectedIconUrl = URI.create(getEndpointAddress() + "/types/" + symbolicName + "/" + entityItem.getVersion()+"/icon").normalize();
-        assertEquals(entityItem.getDisplayName(), "My Catalog App");
-        assertEquals(entityItem.getDescription(), "My description");
-        assertEquals(entityItem.getIconUrl(), expectedIconUrl.getPath());
-        assertEquals(item.getIconUrl(), "classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif");
-
-        if (checkTraits(false)) {
-            // an InterfacesTag should be created for every catalog item
-            @SuppressWarnings("unchecked")
-            Map<String, List<String>> traitsMapTag = Iterables.getOnlyElement(Iterables.filter(entityItem.getTags(), Map.class));
-            List<String> actualInterfaces = traitsMapTag.get("traits");
-            List<Class<?>> expectedInterfaces = Reflections.getAllInterfaces(TestEntity.class);
-            assertEquals(actualInterfaces.size(), expectedInterfaces.size());
-            for (Class<?> expectedInterface : expectedInterfaces) {
-                assertTrue(actualInterfaces.contains(expectedInterface.getName()));
-            }
-        }
-
-        byte[] iconData = client().path("/types/" + symbolicName + "/" + version + "/icon").get(byte[].class);
-        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("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
-                .post(Streams.readFully(new FileInputStream(f)));
-
-
-        assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
-
-        TypeDetail entityItem = client().path("/types/"+symbolicName + "/" + version)
-                .get(TypeDetail.class);
-
-        Assert.assertNotNull(entityItem.getPlan().getData());
-        Assert.assertTrue(entityItem.getPlan().getData().toString().contains(entityType));
-
-        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.getSuppliedVersionString(), version);
-
-        // now let's check other things on the item
-        assertEquals(entityItem.getDescription(), "My description");
-        URI expectedIconUrl = URI.create(getEndpointAddress() + "/types/" + symbolicName + "/" + entityItem.getVersion() + "/icon").normalize();
-        assertEquals(entityItem.getIconUrl(), expectedIconUrl.getPath());
-        assertEquals(item.getIconUrl(), "classpath:" + iconPath);
-
-        if (checkTraits(false)) {
-            // an InterfacesTag should be created for every catalog item
-            @SuppressWarnings("unchecked")
-            Map<String, List<String>> traitsMapTag = Iterables.getOnlyElement(Iterables.filter(entityItem.getTags(), Map.class));
-            List<String> actualInterfaces = traitsMapTag.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("/types/" + symbolicName + "/" + version + "/icon").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 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;
-    }
-
-    @Test
-    public void testGetOnlyLatestApplication() {
-        String symbolicName = "latest.catalog.application.id";
-        String itemType = "template";
-        String serviceType = "org.apache.brooklyn.core.test.entity.TestEntity";
-
-        addTestCatalogItem(symbolicName, itemType, TEST_VERSION, serviceType);
-        addTestCatalogItem(symbolicName, itemType, TEST_LASTEST_VERSION, serviceType);
-
-        TypeSummary application = client().path("/types/" + symbolicName + "/latest")
-                .get(TypeSummary.class);
-        assertEquals(application.getVersion(), TEST_LASTEST_VERSION);
-    }
-
-    @Test
-    public void testGetOnlyLatestDifferentCases() {
-        // depends on installation of this
-        testGetOnlyLatestApplication();
-        
-        String symbolicName = "latest.catalog.application.id";
-
-        TypeSummary application = client().path("/types/" + symbolicName + "/LaTeSt")
-                .get(TypeSummary.class);
-        assertEquals(application.getVersion(), TEST_LASTEST_VERSION);
-
-        application = client().path("/types/" + symbolicName + "/LATEST")
-                .get(TypeSummary.class);
-        assertEquals(application.getVersion(), TEST_LASTEST_VERSION);
-    }
-
-    @Test
-    public void testGetOnlyLatestEntity() {
-        String symbolicName = "latest.catalog.entity.id";
-        String itemType = "entity";
-        String serviceType = "org.apache.brooklyn.core.test.entity.TestEntity";
-
-        addTestCatalogItem(symbolicName, itemType, TEST_VERSION, serviceType);
-        addTestCatalogItem(symbolicName, itemType, TEST_LASTEST_VERSION, serviceType);
-
-        TypeSummary application = client().path("/types/" + symbolicName + "/latest")
-                .get(TypeSummary.class);
-        assertEquals(application.getVersion(), TEST_LASTEST_VERSION);
-    }
-
-    @Test
-    public void testGetOnlyLatestLocation() {
-        String symbolicName = "latest.catalog.location.id";
-        String itemType = "location";
-        String serviceType = "localhost";
-
-        addTestCatalogItem(symbolicName, itemType, TEST_VERSION, serviceType);
-        addTestCatalogItem(symbolicName, itemType, TEST_LASTEST_VERSION, serviceType);
-
-        TypeSummary application = client().path("/types/" + symbolicName + "/latest")
-                .get(TypeSummary.class);
-        assertEquals(application.getVersion(), TEST_LASTEST_VERSION);
-    }
-
-
-    @Test
-    public void testForceUpdateForYAML() {
-        String symbolicName = "force.update.catalog.application.id";
-        String itemType = "template";
-        String initialName = "My Catalog App";
-        String initialDescription = "My description";
-        String updatedName = initialName + " 2";
-        String updatedDescription = initialDescription + " 2";
-
-        String initialYaml = Joiner.on("\n").join(
-                "brooklyn.catalog:",
-                "  id: " + symbolicName,
-                "  version: " + TEST_VERSION,
-                "  itemType: " + itemType,
-                "  name: " + initialName,
-                "  description: " + initialDescription,
-                "  icon_url: classpath:///bridge-small.png",
-                "  version: " + TEST_VERSION,
-                "  item:",
-                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
-        String updatedYaml = Joiner.on("\n").join(
-                "brooklyn.catalog:",
-                "  id: " + symbolicName,
-                "  version: " + TEST_VERSION,
-                "  itemType: " + itemType,
-                "  name: " + updatedName,
-                "  description: " + updatedDescription,
-                "  icon_url: classpath:///bridge-small.png",
-                "  version: " + TEST_VERSION,
-                "  item:",
-                "    type: org.apache.brooklyn.core.test.entity.TestEntity");
-
-        client().path("/bundles").post(initialYaml);
-
-        TypeDetail initialApplication = client().path("/types/" + symbolicName + "/" + TEST_VERSION)
-                .get(TypeDetail.class);
-        assertEquals(initialApplication.getDisplayName(), initialName);
-        assertEquals(initialApplication.getDescription(), initialDescription);
-
-        Response invalidResponse = client().path("/bundles").post(updatedYaml);
-
-        assertEquals(invalidResponse.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
-
-        Response validResponse = client().path("/bundles").query("force", true).post(updatedYaml);
-
-        assertEquals(validResponse.getStatus(), Response.Status.CREATED.getStatusCode());
-
-        TypeSummary application = client().path("/types/" + symbolicName + "/" + TEST_VERSION)
-                .get(TypeSummary.class);
-        assertEquals(application.getDisplayName(), updatedName);
-        assertEquals(application.getDescription(), updatedDescription);
-    }
-
-    @Test
-    public void testForceUpdateForZip() throws Exception {
-        final String symbolicName = "force.update.zip.catalog.application.id";
-        final String initialName = "My Catalog App";
-        final String initialDescription = "My Description";
-        final String updatedName = initialName + " 2";
-        final String updatedDescription = initialDescription  +" 2";
-
-        File initialZip = createZip(ImmutableMap.<String, String>of("catalog.bom", Joiner.on("\n").join(
-                "brooklyn.catalog:",
-                "  bundle: " + symbolicName,
-                "  version: " + TEST_VERSION,
-                "  id: " + symbolicName,
-                "  itemType: entity",
-                "  name: " + initialName,
-                "  description: " + initialDescription,
-                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
-                "  item:",
-                "    type: org.apache.brooklyn.core.test.entity.TestEntity")));
-        File updatedZip = createZip(ImmutableMap.<String, String>of("catalog.bom", Joiner.on("\n").join(
-                "brooklyn.catalog:",
-                "  bundle: " + symbolicName,
-                "  version: " + TEST_VERSION,
-                "  id: " + symbolicName,
-                "  itemType: entity",
-                "  name: " + updatedName,
-                "  description: " + updatedDescription,
-                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
-                "  item:",
-                "    type: org.apache.brooklyn.core.test.entity.TestEntity")));
-
-        client().path("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
-                .post(Streams.readFully(new FileInputStream(initialZip)));
-
-        TypeSummary initialEntity = client().path("/types/" + symbolicName + "/" + TEST_VERSION)
-                .get(TypeSummary.class);
-        assertEquals(initialEntity.getDisplayName(), initialName);
-        assertEquals(initialEntity.getDescription(), initialDescription);
-
-        Response invalidResponse = client().path("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
-                .post(Streams.readFully(new FileInputStream(updatedZip)));
-
-        assertEquals(invalidResponse.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
-
-        Response validResponse = client().path("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-zip")
-                .query("force", true)
-                .post(Streams.readFully(new FileInputStream(updatedZip)));
-
-        assertEquals(validResponse.getStatus(), Response.Status.CREATED.getStatusCode());
-
-        TypeSummary entity = client().path("/types/" + symbolicName + "/" + TEST_VERSION)
-                .get(TypeSummary.class);
-        assertEquals(entity.getDisplayName(), updatedName);
-        assertEquals(entity.getDescription(), updatedDescription);
-    }
-
-    @Test
-    public void testForceUpdateForJar() throws Exception {
-        final String symbolicName = "force.update.jar.catalog.application.id";
-        final String initialName = "My Catalog App";
-        final String initialDescription = "My Description";
-        final String updatedName = initialName + " 2";
-        final String updatedDescription = initialDescription  +" 2";
-
-        File initialJar = createJar(ImmutableMap.<String, String>of("catalog.bom", Joiner.on("\n").join(
-                "brooklyn.catalog:",
-                "  bundle: " + symbolicName,
-                "  version: " + TEST_VERSION,
-                "  id: " + symbolicName,
-                "  itemType: entity",
-                "  name: " + initialName,
-                "  description: " + initialDescription,
-                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
-                "  item:",
-                "    type: org.apache.brooklyn.core.test.entity.TestEntity")));
-        File updatedJar = createJar(ImmutableMap.<String, String>of("catalog.bom", Joiner.on("\n").join(
-                "brooklyn.catalog:",
-                "  bundle: " + symbolicName,
-                "  version: " + TEST_VERSION,
-                "  id: " + symbolicName,
-                "  itemType: entity",
-                "  name: " + updatedName,
-                "  description: " + updatedDescription,
-                "  icon_url: classpath:/org/apache/brooklyn/test/osgi/entities/icon.gif",
-                "  item:",
-                "    type: org.apache.brooklyn.core.test.entity.TestEntity")));
-
-        client().path("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
-                .post(Streams.readFully(new FileInputStream(initialJar)));
-
-        TypeSummary initialEntity = client().path("/types/" + symbolicName + "/" + TEST_VERSION)
-                .get(TypeSummary.class);
-        assertEquals(initialEntity.getDisplayName(), initialName);
-        assertEquals(initialEntity.getDescription(), initialDescription);
-
-        Response invalidResponse = client().path("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
-                .post(Streams.readFully(new FileInputStream(updatedJar)));
-
-        assertEquals(invalidResponse.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
-
-        Response validResponse = client().path("/bundles")
-                .header(HttpHeaders.CONTENT_TYPE, "application/x-jar")
-                .query("force", true)
-                .post(Streams.readFully(new FileInputStream(updatedJar)));
-
-        assertEquals(validResponse.getStatus(), Response.Status.CREATED.getStatusCode());
-
-        TypeSummary entity = client().path("/types/" + symbolicName + "/" + TEST_VERSION)
-                .get(TypeSummary.class);
-        assertEquals(entity.getDisplayName(), updatedName);
-        assertEquals(entity.getDescription(), updatedDescription);
-    }
-    
-    // TODO traits no longer always set - we have supertypes so not needed, we should investigate when they are and when they aren't
-    // and switch those to setting and using the supertypes
-    private boolean checkTraits(boolean currentExpectedToBeWorking) {
-        return currentExpectedToBeWorking;
-    }
-
-}