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();
+        }
+    }
 }

Reply via email to