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 2014/11/13 23:23:34 UTC

[01/18] incubator-brooklyn git commit: Catalog versioning - REST API

Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master 50e3cbce1 -> 6791d51d4


Catalog versioning - REST API


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

Branch: refs/heads/master
Commit: 91f16116789a0246d785ad604bc51086f9dae4dc
Parents: 7f59924
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Thu Jul 17 18:37:42 2014 +0300
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 11:49:47 2014 +0200

----------------------------------------------------------------------
 .../main/java/brooklyn/rest/api/CatalogApi.java |  80 ++++++++++++++-
 .../rest/resources/CatalogResource.java         | 100 +++++++++++++++----
 .../rest/resources/ApiDocResourceTest.java      |   2 +-
 .../rest/resources/CatalogResourceTest.java     |  40 ++++++--
 4 files changed, 190 insertions(+), 32 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/91f16116/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java b/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java
index 78dca9f..f527d60 100644
--- a/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java
+++ b/usage/rest-api/src/main/java/brooklyn/rest/api/CatalogApi.java
@@ -75,9 +75,11 @@ public interface CatalogApi {
             @Valid String xml
     ) ;
 
+    /** @deprecated since 0.7.0 use {@link #getEntity(String, String)} */
+    @Deprecated
     @DELETE
     @Path("/entities/{entityId}")
-    @ApiOperation(value = "Deletes an entity's definition from the catalog")
+    @ApiOperation(value = "Deletes a specific version of an entity's definition from the catalog")
     @ApiErrors(value = {
         @ApiError(code = 404, reason = "Entity not found")
     })
@@ -85,6 +87,19 @@ public interface CatalogApi {
         @ApiParam(name = "entityId", value = "The ID of the entity or template to delete", required = true)
         @PathParam("entityId") String entityId) throws Exception ;
 
+    @DELETE
+    @Path("/entities/{entityId}/{version}")
+    @ApiOperation(value = "Deletes a specific version of an entity's definition from the catalog")
+    @ApiErrors(value = {
+        @ApiError(code = 404, reason = "Entity not found")
+    })
+    public void deleteEntity(
+        @ApiParam(name = "entityId", value = "The ID of the entity or template to delete", required = true)
+        @PathParam("entityId") String entityId,
+
+        @ApiParam(name = "version", value = "The version identifier of the entity or template to delete", required = true)
+        @PathParam("version") String version) throws Exception ;
+
     @GET
     @Path("/entities")
     @ApiOperation(value = "List available entity types optionally matching a query", responseClass = "CatalogItemSummary", multiValueResponse = true)
@@ -105,6 +120,8 @@ public interface CatalogApi {
             @QueryParam("fragment") @DefaultValue("") String fragment
     ) ;
 
+    /** @deprecated since 0.7.0 use {@link #getEntity(String, String)} */
+    @Deprecated
     @GET
     @Path("/entities/{entityId}")
     @ApiOperation(value = "Fetch an entity's definition from the catalog", responseClass = "CatalogEntitySummary", multiValueResponse = true)
@@ -116,8 +133,23 @@ public interface CatalogApi {
         @PathParam("entityId") String entityId) throws Exception ;
 
     @GET
+    @Path("/entities/{entityId}/{version}")
+    @ApiOperation(value = "Fetch a specific version of an entity's definition from the catalog", responseClass = "CatalogEntitySummary", multiValueResponse = true)
+    @ApiErrors(value = {
+        @ApiError(code = 404, reason = "Entity not found")
+    })
+    public CatalogEntitySummary getEntity(
+        @ApiParam(name = "entityId", value = "The ID of the entity or template to retrieve", required = true)
+        @PathParam("entityId") String entityId,
+        
+        @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.7.0 use {@link #getEntity(String, String)} */
+    @Deprecated
+    @GET
     @Path("/applications/{applicationId}")
-    @ApiOperation(value = "Fetch an application's definition from the catalog", responseClass = "CatalogEntitySummary", multiValueResponse = true)
+    @ApiOperation(value = "Fetch a specific version of an application's definition from the catalog", responseClass = "CatalogEntitySummary", multiValueResponse = true)
     @ApiErrors(value = {
         @ApiError(code = 404, reason = "Entity not found")
     })
@@ -126,6 +158,19 @@ public interface CatalogApi {
         @PathParam("applicationId") String entityId) throws Exception ;
 
     @GET
+    @Path("/applications/{applicationId}/{version}")
+    @ApiOperation(value = "Fetch a specific version of an application's definition from the catalog", responseClass = "CatalogEntitySummary", multiValueResponse = true)
+    @ApiErrors(value = {
+        @ApiError(code = 404, reason = "Entity not found")
+    })
+    public CatalogEntitySummary getApplication(
+        @ApiParam(name = "applicationId", value = "The ID of the application to retrieve", required = true)
+        @PathParam("applicationId") String entityId,
+        
+        @ApiParam(name = "version", value = "The version identifier of the application to retrieve", required = true)
+        @PathParam("version") String version) throws Exception ;
+
+    @GET
     @Path("/policies")
     @ApiOperation(value = "List available policies optionally matching a query", responseClass = "CatalogItemSummary", multiValueResponse = true)
     public List<CatalogItemSummary> listPolicies(
@@ -135,6 +180,8 @@ public interface CatalogApi {
             @QueryParam("fragment") @DefaultValue("") String fragment
     ) ;
     
+    /** @deprecated since 0.7.0 use {@link #getEntity(String, String)} */
+    @Deprecated
     @GET
     @Path("/policies/{policyId}")
     @ApiOperation(value = "Fetch a policy's definition from the catalog", responseClass = "CatalogItemSummary", multiValueResponse = true)
@@ -146,6 +193,20 @@ public interface CatalogApi {
         @PathParam("policyId") String policyId) throws Exception ;
     
     @GET
+    @Path("/policies/{policyId}/{version}")
+    @ApiOperation(value = "Fetch a policy's definition from the catalog", responseClass = "CatalogItemSummary", multiValueResponse = true)
+    @ApiErrors(value = {
+        @ApiError(code = 404, reason = "Entity not found")
+    })
+    public CatalogItemSummary getPolicy(
+        @ApiParam(name = "policyId", value = "The ID of the policy to retrieve", required = true)
+        @PathParam("policyId") String policyId,
+        @ApiParam(name = "version", value = "The version identifier of the application to retrieve", required = true)
+        @PathParam("version") String version) throws Exception ;
+    
+    /** @deprecated since 0.7.0 use {@link #getIcon(String, String)} */
+    @Deprecated
+    @GET
     @Path("/icon/{itemId}")
     @ApiOperation(value = "Return the icon for a given catalog entry (application/image or HTTP redirect)")
     @ApiErrors(value = {
@@ -157,5 +218,20 @@ public interface CatalogApi {
         @PathParam("itemId") @DefaultValue("") String itemId
     ) ;
 
+    @GET
+    @Path("/icon/{itemId}/{version}")
+    @ApiOperation(value = "Return the icon for a given catalog entry (application/image or HTTP redirect)")
+    @ApiErrors(value = {
+            @ApiError(code = 404, reason = "Item not found")
+        })
+    @Produces("application/image")
+    public Response getIcon(
+        @ApiParam(name = "itemId", value = "ID of catalog item (application, entity, policy)", required=true)
+        @PathParam("itemId") String itemId,
+
+        @ApiParam(name = "version", value = "version identifier of catalog item (application, entity, policy)", required=true)
+        @PathParam("version") String version
+    ) ;
+
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/91f16116/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
index 0ba8b8c..9621e9c 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
@@ -101,11 +101,11 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
         // FIXME configurations/ not supported
         switch (item.getCatalogItemType()) {
         case TEMPLATE:
-            return Response.created(URI.create("applications/" + itemId))
+            return Response.created(URI.create("applications/" + itemId + "/" + item.getVersion()))
                     .entity(CatalogTransformer.catalogEntitySummary(brooklyn(), (CatalogItem<? extends Entity, EntitySpec<?>>) item))
                     .build();
         case ENTITY:
-            return Response.created(URI.create("entities/" + itemId))
+            return Response.created(URI.create("entities/" + itemId + "/" + item.getVersion()))
                     .entity(CatalogTransformer.catalogEntitySummary(brooklyn(), (CatalogItem<? extends Entity, EntitySpec<?>>) item))
                     .build();
         case POLICY:
@@ -128,12 +128,22 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
     }
     
     @Override
+    @Deprecated
     public void deleteEntity(String entityId) throws Exception {
-      CatalogItem<?,?> result = brooklyn().getCatalog().getCatalogItem(entityId);
+        CatalogItem<?,?> result = brooklyn().getCatalog().getCatalogItem(entityId);
+        if (result==null) {
+            throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId);
+        }
+        brooklyn().getCatalog().deleteCatalogItem(entityId);
+    }
+
+    @Override
+    public void deleteEntity(String entityId, String version) throws Exception {
+      CatalogItem<?,?> result = brooklyn().getCatalog().getCatalogItem(entityId, version);
       if (result==null) {
         throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId);
       }
-      brooklyn().getCatalog().deleteCatalogItem(entityId);
+      brooklyn().getCatalog().deleteCatalogItem(entityId, version);
     }
 
     @Override
@@ -146,20 +156,45 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
         return getCatalogItemSummariesMatchingRegexFragment(CatalogPredicates.IS_TEMPLATE, regex, fragment);
     }
 
+    
     @Override
-    @SuppressWarnings("unchecked")
+    @Deprecated
     public CatalogEntitySummary getEntity(String entityId) {
-      CatalogItem<?,?> result = brooklyn().getCatalog().getCatalogItem(entityId);
-      if (result==null) {
-        throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId);
-      }
+        //TODO These casts are not pretty, we could just provide separate get methods for the different types?
+        //Or we could provide asEntity/asPolicy cast methods on the CataloItem doing a safety check internally
+        @SuppressWarnings("unchecked")
+        CatalogItem<? extends Entity,EntitySpec<?>> result =
+                (CatalogItem<? extends Entity,EntitySpec<?>>) brooklyn().getCatalog().getCatalogItem(entityId);
 
-      return CatalogTransformer.catalogEntitySummary(brooklyn(), (CatalogItem<? extends Entity, EntitySpec<?>>) result);
+        if (result==null) {
+            throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId);
+        }
+
+        return CatalogTransformer.catalogEntitySummary(brooklyn(), result);
+    }
+    
+    @Override
+    public CatalogEntitySummary getEntity(String entityId, String version) {
+        @SuppressWarnings("unchecked")
+        CatalogItem<? extends Entity, EntitySpec<?>> result =
+              (CatalogItem<? extends Entity, EntitySpec<?>>) brooklyn().getCatalog().getCatalogItem(entityId, version);
+
+        if (result==null) {
+            throw WebResourceUtils.notFound("Entity with id '%s:%s' not found", entityId, version);
+        }
+
+        return CatalogTransformer.catalogEntitySummary(brooklyn(), result);
     }
 
     @Override
-    public CatalogEntitySummary getApplication(String applicationId) {
-        return getEntity(applicationId);
+    @Deprecated
+    public CatalogEntitySummary getApplication(String entityId) throws Exception {
+        return getEntity(entityId);
+    }
+
+    @Override
+    public CatalogEntitySummary getApplication(String applicationId, String version) {
+        return getEntity(applicationId, version);
     }
 
     @Override
@@ -167,15 +202,31 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
         return getCatalogItemSummariesMatchingRegexFragment(CatalogPredicates.IS_POLICY, regex, fragment);
     }
     
-    @SuppressWarnings("unchecked")
     @Override
+    @Deprecated
     public CatalogItemSummary getPolicy(String policyId) {
-        CatalogItem<?,?> result = brooklyn().getCatalog().getCatalogItem(policyId);
+        @SuppressWarnings("unchecked")
+        CatalogItem<? extends Policy, PolicySpec<?>> result =
+                (CatalogItem<? extends Policy, PolicySpec<?>>) brooklyn().getCatalog().getCatalogItem(policyId);
+
+        if (result==null) {
+            throw WebResourceUtils.notFound("Policy with id '%s' not found", policyId);
+        }
+
+        return CatalogTransformer.catalogPolicySummary(brooklyn(), result);
+    }
+
+    @Override
+    public CatalogItemSummary getPolicy(String policyId, String version) throws Exception {
+        @SuppressWarnings("unchecked")
+        CatalogItem<? extends Policy, PolicySpec<?>> result =
+                (CatalogItem<? extends Policy, PolicySpec<?>>)brooklyn().getCatalog().getCatalogItem(policyId, version);
+
         if (result==null) {
-          throw WebResourceUtils.notFound("Policy with id '%s' not found", policyId);
+          throw WebResourceUtils.notFound("Policy with id '%s:%s' not found", policyId, version);
         }
 
-        return CatalogTransformer.catalogPolicySummary(brooklyn(), (CatalogItem<? extends Policy, PolicySpec<?>>) result);
+        return CatalogTransformer.catalogPolicySummary(brooklyn(), result);
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
@@ -194,8 +245,19 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
     }
 
     @Override
+    @Deprecated
     public Response getIcon(String itemId) {
         CatalogItem<?,?> result = brooklyn().getCatalog().getCatalogItem(itemId);
+        return getCatalogItemIcon(result);
+    }
+
+    @Override
+    public Response getIcon(String itemId, String version) {
+        CatalogItem<?,?> result = brooklyn().getCatalog().getCatalogItem(itemId, version);
+        return getCatalogItemIcon(result);
+    }
+
+    private Response getCatalogItemIcon(CatalogItem<?, ?> result) {
         String url = result.getIconUrl();
         if (url==null) {
             log.debug("No icon available for "+result+"; returning "+Status.NO_CONTENT);
@@ -218,13 +280,13 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
                     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 "+itemId+", expected at: "+url+" (subsequent messages will log debug only)");
+                        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 "+itemId+", expected at: "+url+" (already logged WARN and error details)");
+                        log.debug("Missing icon data for "+result.getId()+", expected at: "+url+" (already logged WARN and error details)");
                     }
                 }
-                throw WebResourceUtils.notFound("Icon unavailable for %s", itemId);
+                throw WebResourceUtils.notFound("Icon unavailable for %s", result.getId());
             }
         }
         

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/91f16116/usage/rest-server/src/test/java/brooklyn/rest/resources/ApiDocResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/ApiDocResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/ApiDocResourceTest.java
index 66d707d..e58b237 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/resources/ApiDocResourceTest.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/ApiDocResourceTest.java
@@ -98,7 +98,7 @@ public class ApiDocResourceTest extends BrooklynRestResourceTest {
     @Test
     public void testCatalogDetails() throws Exception {
         ApidocRoot response = client().resource("/v1/apidoc/brooklyn.rest.resources.CatalogResource").get(ApidocRoot.class);
-        assertEquals(countOperations(response), 11, "ops="+getOperations(response));
+        assertEquals(countOperations(response), 16, "ops="+getOperations(response));
     }
 
     @SuppressWarnings("rawtypes")

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/91f16116/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
index 8047ef5..993a7bd 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
@@ -53,6 +53,8 @@ import com.sun.jersey.api.client.GenericType;
 public class CatalogResourceTest extends BrooklynRestResourceTest {
 
     private static final Logger log = LoggerFactory.getLogger(CatalogResourceTest.class);
+    
+    private static String TEST_VERSION = "0.1.2";
 
     @BeforeClass(alwaysRun=true)
     @Override
@@ -77,7 +79,7 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
         "  name: My Catalog App\n"+
         "  description: My description\n"+
         "  icon_url: classpath:/brooklyn/osgi/tests/icon.gif\n"+
-        "  version: 0.1.2\n"+
+        "  version: " + TEST_VERSION + "\n"+
         "  libraries:\n"+
         "  - url: " + bundleUrl + "\n"+
         "\n"+
@@ -89,20 +91,18 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
 
     assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
 
-    CatalogEntitySummary entityItem = client().resource("/v1/catalog/entities/"+registeredTypeName)
+    CatalogEntitySummary entityItem = client().resource("/v1/catalog/entities/"+registeredTypeName + "/" + TEST_VERSION)
             .get(CatalogEntitySummary.class);
 
     assertEquals(entityItem.getRegisteredType(), registeredTypeName);
     
-    // stored as yaml, not java
-//    assertEquals(entityItem.getJavaType(), "brooklyn.test.entity.TestEntity");
     Assert.assertNotNull(entityItem.getPlanYaml());
     Assert.assertTrue(entityItem.getPlanYaml().contains("brooklyn.test.entity.TestEntity"));
     
     assertEquals(entityItem.getId(), registeredTypeName);
     
     // and internally let's check we have libraries
-    CatalogItem<?, ?> item = getManagementContext().getCatalog().getCatalogItem(registeredTypeName);
+    CatalogItem<?, ?> item = getManagementContext().getCatalog().getCatalogItem(registeredTypeName, TEST_VERSION);
     Assert.assertNotNull(item);
     List<String> libs = item.getLibraries().getBundles();
     assertEquals(libs, MutableList.of(bundleUrl));
@@ -113,7 +113,7 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
     assertEquals(entityItem.getIconUrl(), "/v1/catalog/icon/" + registeredTypeName);
     assertEquals(item.getIconUrl(), "classpath:/brooklyn/osgi/tests/icon.gif");
     
-    byte[] iconData = client().resource("/v1/catalog/icon/"+registeredTypeName).get(byte[].class);
+    byte[] iconData = client().resource("/v1/catalog/icon/"+registeredTypeName + "/" + TEST_VERSION).get(byte[].class);
     assertEquals(iconData.length, 43);
   }
 
@@ -176,6 +176,9 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
   }
 
   @Test
+  @Deprecated
+  //If we move to using a yaml catalog item, the details will be of the wrapping app,
+  //not of the entity itself, so the test won't make sense any more.
   public void testGetCatalogEntityDetails() {
       CatalogEntitySummary details = client().resource(
               URI.create("/v1/catalog/entities/brooklyn.entity.nosql.redis.RedisStore"))
@@ -187,7 +190,9 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
 
   @Test
   public void testGetCatalogEntityIconDetails() throws IOException {
-      ClientResponse response = client().resource(URI.create("/v1/catalog/icon/brooklyn.entity.nosql.redis.RedisStore"))
+      String catalogItemId = "testGetCatalogEntityIconDetails";
+      addTestCatalogItem(catalogItemId);
+      ClientResponse response = client().resource(URI.create("/v1/catalog/icon/" + catalogItemId + "/" + TEST_VERSION))
               .get(ClientResponse.class);
       response.bufferEntity();
       Assert.assertEquals(response.getStatus(), 200);
@@ -196,6 +201,21 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
       Assert.assertNotNull(image);
   }
 
+  private void addTestCatalogItem(String catalogItemId) {
+      String yaml =
+              "brooklyn.catalog:\n"+
+              "  id: " + catalogItemId + "\n"+
+              "  name: My Catalog App\n"+
+              "  description: My description\n"+
+              "  icon_url: classpath:///redis-logo.png\n"+
+              "  version: " + TEST_VERSION + "\n"+
+              "\n"+
+              "services:\n"+
+              "- type: brooklyn.entity.nosql.redis.RedisStore\n";
+
+      client().resource("/v1/catalog").post(yaml);
+  }
+
   @Test
   public void testListPolicies() {
     Set<CatalogItemSummary> policies = client().resource("/v1/catalog/policies")
@@ -221,7 +241,7 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
         "  id: " + registeredTypeName + "\n"+
         "  name: My Catalog App To Be Deleted\n"+
         "  description: My description\n"+
-        "  version: 0.1.2\n"+
+        "  version: " + TEST_VERSION + "\n"+
         "\n"+
         "services:\n"+
         "- type: brooklyn.test.entity.TestEntity\n";
@@ -229,12 +249,12 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
     client().resource("/v1/catalog")
             .post(ClientResponse.class, yaml);
     
-    ClientResponse deleteResponse = client().resource("/v1/catalog/entities/"+registeredTypeName)
+    ClientResponse deleteResponse = client().resource("/v1/catalog/entities/"+registeredTypeName+"/"+TEST_VERSION)
             .delete(ClientResponse.class);
 
     assertEquals(deleteResponse.getStatus(), Response.Status.NO_CONTENT.getStatusCode());
 
-    ClientResponse getPostDeleteResponse = client().resource("/v1/catalog/entities/"+registeredTypeName)
+    ClientResponse getPostDeleteResponse = client().resource("/v1/catalog/entities/"+registeredTypeName+"/"+TEST_VERSION)
             .get(ClientResponse.class);
     assertEquals(getPostDeleteResponse.getStatus(), Response.Status.NOT_FOUND.getStatusCode());
   }


[16/18] incubator-brooklyn git commit: use osgi to deserialize mementos

Posted by he...@apache.org.
use osgi to deserialize mementos

introduces a ClassLoader based on the BrooklynClassLoadingContext for use when deserializing mementos, so that osgi-bundle-specific effectors etc can be instantiated


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

Branch: refs/heads/master
Commit: 8bd76ad39447d3e8dde52930aa27987be0b64744
Parents: e81a260
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Nov 13 15:13:24 2014 +0000
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 19:04:04 2014 +0200

----------------------------------------------------------------------
 .../mementos/BrooklynMementoPersister.java      |   4 +
 .../rebind/RebindContextLookupContext.java      |  31 ++
 .../entity/rebind/RebindManagerImpl.java        | 357 ++++++++++---------
 .../BrooklynMementoPersisterInMemory.java       |  20 ++
 .../BrooklynMementoPersisterToObjectStore.java  |  61 +++-
 .../rebind/persister/XmlMementoSerializer.java  |   2 +-
 ...ssLoaderFromBrooklynClassLoadingContext.java |  64 ++++
 .../persister/XmlMementoSerializerTest.java     |  34 +-
 8 files changed, 396 insertions(+), 177 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8bd76ad3/api/src/main/java/brooklyn/mementos/BrooklynMementoPersister.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/mementos/BrooklynMementoPersister.java b/api/src/main/java/brooklyn/mementos/BrooklynMementoPersister.java
index 229c7c3..63a04d1 100644
--- a/api/src/main/java/brooklyn/mementos/BrooklynMementoPersister.java
+++ b/api/src/main/java/brooklyn/mementos/BrooklynMementoPersister.java
@@ -55,6 +55,10 @@ public interface BrooklynMementoPersister {
         Enricher lookupEnricher(String id);
         Feed lookupFeed(String id);
         CatalogItem<?, ?> lookupCatalogItem(String id);
+        
+        BrooklynObject lookup(BrooklynObjectType type, String objectId);
+        /** like {@link #lookup(BrooklynObjectType, String)} but doesn't record an exception if not found */
+        BrooklynObject peek(BrooklynObjectType type, String objectId);
     }
     
     /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8bd76ad3/core/src/main/java/brooklyn/entity/rebind/RebindContextLookupContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindContextLookupContext.java b/core/src/main/java/brooklyn/entity/rebind/RebindContextLookupContext.java
index e23aa73..bf80ef9 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindContextLookupContext.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindContextLookupContext.java
@@ -23,6 +23,7 @@ import javax.annotation.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import brooklyn.basic.BrooklynObject;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.entity.Entity;
 import brooklyn.entity.Feed;
@@ -104,4 +105,34 @@ public class RebindContextLookupContext implements LookupContext {
         }
         return result;
     }
+    
+    @Override
+    public BrooklynObject lookup(BrooklynObjectType type, String id) {
+        switch (type) {
+        case CATALOG_ITEM: return lookupCatalogItem(id);
+        case ENRICHER: return lookupEnricher(id);
+        case ENTITY: return lookupEntity(id);
+        case FEED: return lookupFeed(id);
+        case LOCATION: return lookupLocation(id);
+        case POLICY: return lookupPolicy(id);
+        case UNKNOWN: return null;
+        }
+        throw new IllegalStateException("Unexpected type "+type+" / id "+id);
+    }
+    
+    @Override
+    public BrooklynObject peek(BrooklynObjectType type, String id) {
+        switch (type) {
+        case CATALOG_ITEM: return rebindContext.getCatalogItem(id);
+        case ENRICHER: return rebindContext.getEnricher(id);
+        case ENTITY: return rebindContext.getEntity(id);
+        case FEED: return rebindContext.getFeed(id);
+        case LOCATION: return rebindContext.getLocation(id);
+        case POLICY: return rebindContext.getPolicy(id);
+        case UNKNOWN: return null;
+        }
+        throw new IllegalStateException("Unexpected type "+type+" / id "+id);
+    }
+
+
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8bd76ad3/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
index 177dca6..07c60a5 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
@@ -21,7 +21,6 @@ package brooklyn.entity.rebind;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.io.IOException;
-import java.lang.reflect.Field;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -36,6 +35,7 @@ import org.slf4j.LoggerFactory;
 
 import brooklyn.basic.AbstractBrooklynObject;
 import brooklyn.basic.BrooklynObject;
+import brooklyn.basic.BrooklynObjectInternal;
 import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.CatalogLoadMode;
@@ -84,6 +84,7 @@ import brooklyn.mementos.EnricherMemento;
 import brooklyn.mementos.EntityMemento;
 import brooklyn.mementos.FeedMemento;
 import brooklyn.mementos.LocationMemento;
+import brooklyn.mementos.Memento;
 import brooklyn.mementos.PolicyMemento;
 import brooklyn.mementos.TreeNode;
 import brooklyn.policy.Enricher;
@@ -560,16 +561,18 @@ public class RebindManagerImpl implements RebindManager {
             }
 
             //
-            // PHASE TWO
+            // PHASE TWO - build catalog so we can load other things
             //
             
+            BrooklynObjectInstantiator instantiator = new BrooklynObjectInstantiator(classLoader, rebindContext, reflections);
+            
             // Instantiate catalog items
             if (persistCatalogItemsEnabled) {
                 logRebindingDebug("RebindManager instantiating catalog items: {}", mementoManifest.getCatalogItemIds());
                 for (CatalogItemMemento catalogItemMemento : mementoManifest.getCatalogItemMementos().values()) {
                     if (LOG.isDebugEnabled()) LOG.debug("RebindManager instantiating catalog item {}", catalogItemMemento);
                     try {
-                        CatalogItem<?, ?> catalogItem = newCatalogItem(catalogItemMemento, reflections);
+                        CatalogItem<?, ?> catalogItem = instantiator.newCatalogItem(catalogItemMemento);
                         rebindContext.registerCatalogItem(catalogItemMemento.getId(), catalogItem);
                     } catch (Exception e) {
                         exceptionHandler.onCreateFailed(BrooklynObjectType.CATALOG_ITEM, catalogItemMemento.getId(), catalogItemMemento.getType(), e);
@@ -645,7 +648,7 @@ public class RebindManagerImpl implements RebindManager {
                 if (LOG.isTraceEnabled()) LOG.trace("RebindManager instantiating location {}", locId);
                 
                 try {
-                    Location location = newLocation(locId, locType, reflections);
+                    Location location = instantiator.newLocation(locId, locType);
                     rebindContext.registerLocation(locId, location);
                 } catch (Exception e) {
                     exceptionHandler.onCreateFailed(BrooklynObjectType.LOCATION, locId, locType, e);
@@ -662,13 +665,10 @@ public class RebindManagerImpl implements RebindManager {
                 if (LOG.isTraceEnabled()) LOG.trace("RebindManager instantiating entity {}", entityId);
                 
                 try {
-                    Entity entity = newEntity(entityId, entityManifest.getType(), getLoadingContextFromCatalogItemId(catalogItemId, classLoader, rebindContext));
+                    Entity entity = (Entity) instantiator.newEntity(entityId, entityManifest.getType(), catalogItemId);
                     ((EntityInternal)entity).getManagementSupport().setReadOnly( rebindContext.isReadOnly(entity) );
                     rebindContext.registerEntity(entityId, entity);
 
-                    if (forceCatalogItem(entityManifest)) {
-                        ((EntityInternal)entity).setCatalogItemId(catalogItemId);
-                    }
                 } catch (Exception e) {
                     exceptionHandler.onCreateFailed(BrooklynObjectType.ENTITY, entityId, entityManifest.getType(), e);
                 }
@@ -693,7 +693,7 @@ public class RebindManagerImpl implements RebindManager {
                     logRebindingDebug("RebindManager instantiating policy {}", policyMemento);
                     
                     try {
-                        Policy policy = newPolicy(policyMemento, getLoadingContextFromCatalogItemId(policyMemento.getCatalogItemId(), classLoader, rebindContext));
+                        Policy policy = instantiator.newPolicy(policyMemento);
                         rebindContext.registerPolicy(policyMemento.getId(), policy);
                     } catch (Exception e) {
                         exceptionHandler.onCreateFailed(BrooklynObjectType.POLICY, policyMemento.getId(), policyMemento.getType(), e);
@@ -710,7 +710,7 @@ public class RebindManagerImpl implements RebindManager {
                     logRebindingDebug("RebindManager instantiating enricher {}", enricherMemento);
 
                     try {
-                        Enricher enricher = newEnricher(enricherMemento, reflections);
+                        Enricher enricher = instantiator.newEnricher(enricherMemento);
                         rebindContext.registerEnricher(enricherMemento.getId(), enricher);
                     } catch (Exception e) {
                         exceptionHandler.onCreateFailed(BrooklynObjectType.ENRICHER, enricherMemento.getId(), enricherMemento.getType(), e);
@@ -727,7 +727,7 @@ public class RebindManagerImpl implements RebindManager {
                     if (LOG.isDebugEnabled()) LOG.debug("RebindManager instantiating feed {}", feedMemento);
 
                     try {
-                        Feed feed = newFeed(feedMemento, reflections);
+                        Feed feed = instantiator.newFeed(feedMemento);
                         rebindContext.registerFeed(feedMemento.getId(), feed);
                     } catch (Exception e) {
                         exceptionHandler.onCreateFailed(BrooklynObjectType.FEED, feedMemento.getId(), feedMemento.getType(), e);
@@ -955,11 +955,6 @@ public class RebindManagerImpl implements RebindManager {
         }
     }
 
-    private boolean forceCatalogItem(EntityMementoManifest entityManifest) {
-        return entityManifest.getCatalogItemId() == null && 
-                BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_INFER_CATALOG_ITEM_ON_REBIND);
-    }
-
     private String findCatalogItemId(Map<String, EntityMementoManifest> entityIdToManifest, EntityMementoManifest entityManifest) {
         if (entityManifest.getCatalogItemId() != null) {
             return entityManifest.getCatalogItemId();
@@ -1070,184 +1065,226 @@ public class RebindManagerImpl implements RebindManager {
         return result;
     }
 
-    private Entity newEntity(String entityId, String entityType, BrooklynClassLoadingContext loader) {
-        Class<? extends Entity> entityClazz = loader.loadClass(entityType, Entity.class);
-        
-        if (InternalFactory.isNewStyle(entityClazz)) {
-            // Not using entityManager.createEntity(EntitySpec) because don't want init() to be called.
-            // Creates an uninitialized entity, but that has correct id + proxy.
-            InternalEntityFactory entityFactory = managementContext.getEntityFactory();
-            Entity entity = entityFactory.constructEntity(entityClazz, Reflections.getAllInterfaces(entityClazz), entityId);
-            
-            return entity;
+    private class BrooklynObjectInstantiator {
 
-        } else {
-            LOG.warn("Deprecated rebind of entity without no-arg constructor; this may not be supported in future versions: id="+entityId+"; type="+entityType);
-            
-            // There are several possibilities for the constructor; find one that works.
-            // Prefer passing in the flags because required for Application to set the management context
-            // TODO Feels very hacky!
+        private final ClassLoader classLoader;
+        private final RebindContextImpl rebindContext;
+        private final Reflections reflections;
+        
+        private BrooklynObjectInstantiator(ClassLoader classLoader, RebindContextImpl rebindContext, Reflections reflections) {
+            this.classLoader = classLoader;
+            this.rebindContext = rebindContext;
+            this.reflections = reflections;
+        }
 
-            Map<Object,Object> flags = Maps.newLinkedHashMap();
-            flags.put("id", entityId);
-            if (AbstractApplication.class.isAssignableFrom(entityClazz)) flags.put("mgmt", managementContext);
+        private Entity newEntity(String entityId, String entityType, String catalogItemId) {
+            Class<? extends Entity> entityClazz = load(Entity.class, entityType, catalogItemId, entityId);
 
-            // TODO document the multiple sources of flags, and the reason for setting the mgmt context *and* supplying it as the flag
-            // (NB: merge reported conflict as the two things were added separately)
-            Entity entity = (Entity) invokeConstructor(null, entityClazz, new Object[] {flags}, new Object[] {flags, null}, new Object[] {null}, new Object[0]);
+            Entity entity;
             
-            // In case the constructor didn't take the Map arg, then also set it here.
-            // e.g. for top-level app instances such as WebClusterDatabaseExampleApp will (often?) not have
-            // interface + constructor.
-            // TODO On serializing the memento, we should capture which interfaces so can recreate
-            // the proxy+spec (including for apps where there's not an obvious interface).
-            FlagUtils.setFieldsFromFlags(ImmutableMap.of("id", entityId), entity);
-            if (entity instanceof AbstractApplication) {
-                FlagUtils.setFieldsFromFlags(ImmutableMap.of("mgmt", managementContext), entity);
+            if (InternalFactory.isNewStyle(entityClazz)) {
+                // Not using entityManager.createEntity(EntitySpec) because don't want init() to be called.
+                // Creates an uninitialized entity, but that has correct id + proxy.
+                InternalEntityFactory entityFactory = managementContext.getEntityFactory();
+                entity = entityFactory.constructEntity(entityClazz, Reflections.getAllInterfaces(entityClazz), entityId);
+
+            } else {
+                LOG.warn("Deprecated rebind of entity without no-arg constructor; this may not be supported in future versions: id="+entityId+"; type="+entityType);
+
+                // There are several possibilities for the constructor; find one that works.
+                // Prefer passing in the flags because required for Application to set the management context
+                // TODO Feels very hacky!
+
+                Map<Object,Object> flags = Maps.newLinkedHashMap();
+                flags.put("id", entityId);
+                if (AbstractApplication.class.isAssignableFrom(entityClazz)) flags.put("mgmt", managementContext);
+
+                // TODO document the multiple sources of flags, and the reason for setting the mgmt context *and* supplying it as the flag
+                // (NB: merge reported conflict as the two things were added separately)
+                entity = (Entity) invokeConstructor(null, entityClazz, new Object[] {flags}, new Object[] {flags, null}, new Object[] {null}, new Object[0]);
+
+                // In case the constructor didn't take the Map arg, then also set it here.
+                // e.g. for top-level app instances such as WebClusterDatabaseExampleApp will (often?) not have
+                // interface + constructor.
+                // TODO On serializing the memento, we should capture which interfaces so can recreate
+                // the proxy+spec (including for apps where there's not an obvious interface).
+                FlagUtils.setFieldsFromFlags(ImmutableMap.of("id", entityId), entity);
+                if (entity instanceof AbstractApplication) {
+                    FlagUtils.setFieldsFromFlags(ImmutableMap.of("mgmt", managementContext), entity);
+                }
+                ((AbstractEntity)entity).setManagementContext(managementContext);
+                managementContext.prePreManage(entity);
             }
-            ((AbstractEntity)entity).setManagementContext(managementContext);
-            managementContext.prePreManage(entity);
             
+            setCatalogItemId(entity, catalogItemId);
             return entity;
         }
-    }
-    
-    /**
-     * Constructs a new location, passing to its constructor the location id and all of memento.getFlags().
-     */
-    private Location newLocation(String locationId, String locationType, Reflections reflections) {
-        Class<? extends Location> locationClazz = reflections.loadClass(locationType, Location.class);
-
-        if (InternalFactory.isNewStyle(locationClazz)) {
-            // Not using loationManager.createLocation(LocationSpec) because don't want init() to be called
-            // TODO Need to rationalise this to move code into methods of InternalLocationFactory.
-            //      But note that we'll change all locations to be entities at some point!
-            // See same code approach used in #newEntity(EntityMemento, Reflections)
-            InternalLocationFactory locationFactory = managementContext.getLocationFactory();
-            Location location = locationFactory.constructLocation(locationClazz);
-            FlagUtils.setFieldsFromFlags(ImmutableMap.of("id", locationId), location);
-            managementContext.prePreManage(location);
-            ((AbstractLocation)location).setManagementContext(managementContext);
-            
-            return location;
-        } else {
-            LOG.warn("Deprecated rebind of location without no-arg constructor; this may not be supported in future versions: id="+locationId+"; type="+locationType);
-            
-            // There are several possibilities for the constructor; find one that works.
-            // Prefer passing in the flags because required for Application to set the management context
-            // TODO Feels very hacky!
-            Map<String,?> flags = MutableMap.of("id", locationId, "deferConstructionChecks", true);
 
-            return (Location) invokeConstructor(reflections, locationClazz, new Object[] {flags});
+        private void setCatalogItemId(BrooklynObject item, String catalogItemId) {
+            if (catalogItemId!=null) {
+                ((BrooklynObjectInternal)item).setCatalogItemId(catalogItemId);
+            }
         }
-        // note 'used' config keys get marked in BasicLocationRebindSupport
-    }
 
-    /**
-     * Constructs a new policy, passing to its constructor the policy id and all of memento.getConfig().
-     */
-    private Policy newPolicy(PolicyMemento memento, BrooklynClassLoadingContext loader) {
-        String id = memento.getId();
-        String policyType = checkNotNull(memento.getType(), "policy type of %s must not be null in memento", id);
-        Class<? extends Policy> policyClazz = loader.loadClass(policyType, Policy.class);
-
-        if (InternalFactory.isNewStyle(policyClazz)) {
-            InternalPolicyFactory policyFactory = managementContext.getPolicyFactory();
-            Policy policy = policyFactory.constructPolicy(policyClazz);
-            FlagUtils.setFieldsFromFlags(ImmutableMap.of("id", id), policy);
-            ((AbstractPolicy)policy).setManagementContext(managementContext);
-            
-            return policy;
+        private <T extends BrooklynObject> Class<? extends T> load(Class<T> bType, Memento memento) {
+            return load(bType, memento.getType(), memento.getCatalogItemId(), memento.getId());
+        }
+        @SuppressWarnings("unchecked")
+        private <T extends BrooklynObject> Class<? extends T> load(Class<T> bType, String jType, String catalogItemId, String contextSuchAsId) {
+            checkNotNull(jType, "Type of %s (%s) must not be null", contextSuchAsId, bType.getSimpleName());
+            if (catalogItemId==null) {
+                // we have previously used reflections; not sure if that's needed?
+                try {
+                    return (Class<T>)reflections.loadClass(jType);
+                } catch (Exception e) {
+                    LOG.warn("Unable to load "+jType+" using reflections; will try standard context");
+                }
+            }
+            BrooklynClassLoadingContext loader = getLoadingContextFromCatalogItemId(catalogItemId, classLoader, rebindContext);
+            return loader.loadClass(jType, bType);
+        }
 
-        } else {
-            LOG.warn("Deprecated rebind of policy without no-arg constructor; this may not be supported in future versions: id="+id+"; type="+policyType);
+        /**
+         * Constructs a new location, passing to its constructor the location id and all of memento.getFlags().
+         */
+        private Location newLocation(String locationId, String locationType) {
+            Class<? extends Location> locationClazz = reflections.loadClass(locationType, Location.class);
+
+            if (InternalFactory.isNewStyle(locationClazz)) {
+                // Not using loationManager.createLocation(LocationSpec) because don't want init() to be called
+                // TODO Need to rationalise this to move code into methods of InternalLocationFactory.
+                //      But note that we'll change all locations to be entities at some point!
+                // See same code approach used in #newEntity(EntityMemento, Reflections)
+                InternalLocationFactory locationFactory = managementContext.getLocationFactory();
+                Location location = locationFactory.constructLocation(locationClazz);
+                FlagUtils.setFieldsFromFlags(ImmutableMap.of("id", locationId), location);
+                managementContext.prePreManage(location);
+                ((AbstractLocation)location).setManagementContext(managementContext);
+
+                return location;
+            } else {
+                LOG.warn("Deprecated rebind of location without no-arg constructor; this may not be supported in future versions: id="+locationId+"; type="+locationType);
+
+                // There are several possibilities for the constructor; find one that works.
+                // Prefer passing in the flags because required for Application to set the management context
+                // TODO Feels very hacky!
+                Map<String,?> flags = MutableMap.of("id", locationId, "deferConstructionChecks", true);
+
+                return (Location) invokeConstructor(reflections, locationClazz, new Object[] {flags});
+            }
+            // note 'used' config keys get marked in BasicLocationRebindSupport
+        }
+
+        /**
+         * Constructs a new policy, passing to its constructor the policy id and all of memento.getConfig().
+         */
+        private Policy newPolicy(PolicyMemento memento) {
+            String id = memento.getId();
+            Class<? extends Policy> policyClazz = load(Policy.class, memento.getType(), memento.getCatalogItemId(), id);
             
-            // There are several possibilities for the constructor; find one that works.
-            // Prefer passing in the flags because required for Application to set the management context
-            // TODO Feels very hacky!
-            Map<String, Object> flags = MutableMap.<String, Object>of(
+            Policy policy;
+            if (InternalFactory.isNewStyle(policyClazz)) {
+                InternalPolicyFactory policyFactory = managementContext.getPolicyFactory();
+                policy = policyFactory.constructPolicy(policyClazz);
+                FlagUtils.setFieldsFromFlags(ImmutableMap.of("id", id), policy);
+                ((AbstractPolicy)policy).setManagementContext(managementContext);
+
+            } else {
+                LOG.warn("Deprecated rebind of policy without no-arg constructor; this may not be supported in future versions: id="+id+"; type="+policyClazz);
+
+                // There are several possibilities for the constructor; find one that works.
+                // Prefer passing in the flags because required for Application to set the management context
+                // TODO Feels very hacky!
+                Map<String, Object> flags = MutableMap.<String, Object>of(
                     "id", id, 
                     "deferConstructionChecks", true,
                     "noConstructionInit", true);
-            flags.putAll(memento.getConfig());
+                flags.putAll(memento.getConfig());
 
-            return (Policy) invokeConstructor(null, policyClazz, new Object[] {flags});
+                policy = invokeConstructor(null, policyClazz, new Object[] {flags});
+            }
+            
+            setCatalogItemId(policy, memento.getCatalogItemId());
+            return policy;
         }
-    }
 
-    /**
-     * Constructs a new enricher, passing to its constructor the enricher id and all of memento.getConfig().
-     */
-    private Enricher newEnricher(EnricherMemento memento, Reflections reflections) {
-        String id = memento.getId();
-        String enricherType = checkNotNull(memento.getType(), "enricher type of %s must not be null in memento", id);
-        Class<? extends Enricher> enricherClazz = reflections.loadClass(enricherType, Enricher.class);
-
-        if (InternalFactory.isNewStyle(enricherClazz)) {
-            InternalPolicyFactory policyFactory = managementContext.getPolicyFactory();
-            Enricher enricher = policyFactory.constructEnricher(enricherClazz);
-            FlagUtils.setFieldsFromFlags(ImmutableMap.of("id", id), enricher);
-            ((AbstractEnricher)enricher).setManagementContext(managementContext);
-            
-            return enricher;
+        /**
+         * Constructs a new enricher, passing to its constructor the enricher id and all of memento.getConfig().
+         */
+        private Enricher newEnricher(EnricherMemento memento) {
+            Class<? extends Enricher> enricherClazz = load(Enricher.class, memento);
+            String id = memento.getId();
 
-        } else {
-            LOG.warn("Deprecated rebind of enricher without no-arg constructor; this may not be supported in future versions: id="+id+"; type="+enricherType);
-            
-            // There are several possibilities for the constructor; find one that works.
-            // Prefer passing in the flags because required for Application to set the management context
-            // TODO Feels very hacky!
-            Map<String, Object> flags = MutableMap.<String, Object>of(
+            Enricher enricher;
+            if (InternalFactory.isNewStyle(enricherClazz)) {
+                InternalPolicyFactory policyFactory = managementContext.getPolicyFactory();
+                enricher = policyFactory.constructEnricher(enricherClazz);
+                FlagUtils.setFieldsFromFlags(ImmutableMap.of("id", id), enricher);
+                ((AbstractEnricher)enricher).setManagementContext(managementContext);
+
+            } else {
+                LOG.warn("Deprecated rebind of enricher without no-arg constructor; this may not be supported in future versions: id="+id+"; type="+enricherClazz);
+
+                // There are several possibilities for the constructor; find one that works.
+                // Prefer passing in the flags because required for Application to set the management context
+                // TODO Feels very hacky!
+                Map<String, Object> flags = MutableMap.<String, Object>of(
                     "id", id, 
                     "deferConstructionChecks", true,
                     "noConstructionInit", true);
-            flags.putAll(memento.getConfig());
+                flags.putAll(memento.getConfig());
 
-            return (Enricher) invokeConstructor(reflections, enricherClazz, new Object[] {flags});
+                enricher = invokeConstructor(reflections, enricherClazz, new Object[] {flags});
+            }
+            
+            setCatalogItemId(enricher, memento.getCatalogItemId());
+            return enricher;
         }
-    }
 
-    /**
-     * Constructs a new enricher, passing to its constructor the enricher id and all of memento.getConfig().
-     */
-    private Feed newFeed(FeedMemento memento, Reflections reflections) {
-        String id = memento.getId();
-        String feedType = checkNotNull(memento.getType(), "feed type of %s must not be null in memento", id);
-        Class<? extends Feed> feedClazz = reflections.loadClass(feedType, Feed.class);
-
-        if (InternalFactory.isNewStyle(feedClazz)) {
-            InternalPolicyFactory policyFactory = managementContext.getPolicyFactory();
-            Feed feed = policyFactory.constructFeed(feedClazz);
-            FlagUtils.setFieldsFromFlags(ImmutableMap.of("id", id), feed);
-            ((AbstractFeed)feed).setManagementContext(managementContext);
+        /**
+         * Constructs a new enricher, passing to its constructor the enricher id and all of memento.getConfig().
+         */
+        private Feed newFeed(FeedMemento memento) {
+            Class<? extends Feed> feedClazz = load(Feed.class, memento);
+            String id = memento.getId();
+
+            Feed feed;
+            if (InternalFactory.isNewStyle(feedClazz)) {
+                InternalPolicyFactory policyFactory = managementContext.getPolicyFactory();
+                feed = policyFactory.constructFeed(feedClazz);
+                FlagUtils.setFieldsFromFlags(ImmutableMap.of("id", id), feed);
+                ((AbstractFeed)feed).setManagementContext(managementContext);
+
+            } else {
+                throw new IllegalStateException("rebind of feed without no-arg constructor unsupported: id="+id+"; type="+feedClazz);
+            }
             
+            setCatalogItemId(feed, memento.getCatalogItemId());
             return feed;
+        }
 
-        } else {
-            throw new IllegalStateException("rebind of feed without no-arg constructor unsupported: id="+id+"; type="+feedType);
+        @SuppressWarnings({ "rawtypes" })
+        private CatalogItem<?, ?> newCatalogItem(CatalogItemMemento memento) {
+            String id = memento.getId();
+            // catalog item subtypes are internal to brooklyn, not in osgi
+            String itemType = checkNotNull(memento.getType(), "catalog item type of %s must not be null in memento", id);
+            Class<? extends CatalogItem> clazz = reflections.loadClass(itemType, CatalogItem.class);
+            return invokeConstructor(reflections, clazz, new Object[]{});
         }
-    }
 
-    @SuppressWarnings({ "rawtypes" })
-    private CatalogItem<?, ?> newCatalogItem(CatalogItemMemento memento, Reflections reflections) {
-        String id = memento.getId();
-        String itemType = checkNotNull(memento.getType(), "catalog item type of %s must not be null in memento", id);
-        Class<? extends CatalogItem> clazz = reflections.loadClass(itemType, CatalogItem.class);
-        return invokeConstructor(reflections, clazz, new Object[]{});
-    }
-    
-    private <T> T invokeConstructor(Reflections reflections, Class<T> clazz, Object[]... possibleArgs) {
-        for (Object[] args : possibleArgs) {
-            try {
-                Optional<T> v = Reflections.invokeConstructorWithArgs(clazz, args, true);
-                if (v.isPresent()) {
-                    return v.get();
+        private <T> T invokeConstructor(Reflections reflections, Class<T> clazz, Object[]... possibleArgs) {
+            for (Object[] args : possibleArgs) {
+                try {
+                    Optional<T> v = Reflections.invokeConstructorWithArgs(clazz, args, true);
+                    if (v.isPresent()) {
+                        return v.get();
+                    }
+                } catch (Exception e) {
+                    throw Exceptions.propagate(e);
                 }
-            } catch (Exception e) {
-                throw Exceptions.propagate(e);
             }
+            throw new IllegalStateException("Cannot instantiate instance of type "+clazz+"; expected constructor signature not found");
         }
-        throw new IllegalStateException("Cannot instantiate instance of type "+clazz+"; expected constructor signature not found");
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8bd76ad3/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemory.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemory.java
index f4e4fc0..902b57e 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemory.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemory.java
@@ -28,11 +28,13 @@ import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+import brooklyn.basic.BrooklynObject;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.entity.Entity;
 import brooklyn.entity.Feed;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.proxying.EntityProxy;
+import brooklyn.entity.rebind.BrooklynObjectType;
 import brooklyn.entity.rebind.PersistenceExceptionHandler;
 import brooklyn.entity.rebind.PersistenceExceptionHandlerImpl;
 import brooklyn.entity.rebind.RebindExceptionHandler;
@@ -156,6 +158,24 @@ public class BrooklynMementoPersisterInMemory extends AbstractBrooklynMementoPer
                         Class<?> clazz = loadClass(manifest.getCatalogItemMemento(id).getType());
                         return (CatalogItem<?,?>) invokeConstructor(clazz, new Object[0]);
                     }
+                    
+                    @Override
+                    public BrooklynObject lookup(BrooklynObjectType type, String id) {
+                        switch (type) {
+                        case CATALOG_ITEM: return lookupCatalogItem(id);
+                        case ENRICHER: return lookupEnricher(id);
+                        case ENTITY: return lookupEntity(id);
+                        case FEED: return lookupFeed(id);
+                        case LOCATION: return lookupLocation(id);
+                        case POLICY: return lookupPolicy(id);
+                        case UNKNOWN: return null;
+                        }
+                        throw new IllegalStateException("Unexpected type "+type+" / id "+id);
+                    }
+                    @Override
+                    public BrooklynObject peek(BrooklynObjectType type, String id) {
+                        return lookup(type, id);
+                    }
 
                     private Class<?> loadClass(String name) {
                         try {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8bd76ad3/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
index de691e0..2c1e323 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
@@ -32,11 +32,17 @@ import java.util.concurrent.TimeoutException;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import javax.annotation.Nullable;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import brooklyn.basic.BrooklynObject;
+import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.ConfigKey;
+import brooklyn.config.StringConfigMap;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.rebind.BrooklynObjectType;
 import brooklyn.entity.rebind.PeriodicDeltaChangeListener;
@@ -47,6 +53,7 @@ import brooklyn.entity.rebind.dto.BrooklynMementoImpl;
 import brooklyn.entity.rebind.dto.BrooklynMementoManifestImpl;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessor;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessorWithLock;
+import brooklyn.management.classloading.ClassLoaderFromBrooklynClassLoadingContext;
 import brooklyn.mementos.BrooklynMemento;
 import brooklyn.mementos.BrooklynMementoManifest;
 import brooklyn.mementos.BrooklynMementoPersister;
@@ -99,7 +106,7 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
         BrooklynObjectType.ENRICHER, BrooklynObjectType.FEED, BrooklynObjectType.CATALOG_ITEM);
 
     private final PersistenceObjectStore objectStore;
-    private final MementoSerializer<Object> serializer;
+    private final MementoSerializer<Object> serializerWithStandardClassLoader;
 
     private final Map<String, StoreObjectAccessorWithLock> writers = new LinkedHashMap<String, PersistenceObjectStore.StoreObjectAccessorWithLock>();
 
@@ -107,7 +114,8 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
 
     private volatile boolean writesAllowed = false;
     private volatile boolean writesShuttingDown = false;
-
+    private StringConfigMap brooklynProperties;
+    
     /**
      * Lock used on writes (checkpoint + delta) so that {@link #waitForWritesCompleted(Duration)} can block
      * for any concurrent call to complete.
@@ -116,12 +124,13 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
 
     public BrooklynMementoPersisterToObjectStore(PersistenceObjectStore objectStore, BrooklynProperties brooklynProperties, ClassLoader classLoader) {
         this.objectStore = checkNotNull(objectStore, "objectStore");
+        this.brooklynProperties = brooklynProperties;
         
         int maxSerializationAttempts = brooklynProperties.getConfig(PERSISTER_MAX_SERIALIZATION_ATTEMPTS);
-        int maxThreadPoolSize = brooklynProperties.getConfig(PERSISTER_MAX_THREAD_POOL_SIZE);
-                
         MementoSerializer<Object> rawSerializer = new XmlMementoSerializer<Object>(classLoader);
-        this.serializer = new RetryingMementoSerializer<Object>(rawSerializer, maxSerializationAttempts);
+        this.serializerWithStandardClassLoader = new RetryingMementoSerializer<Object>(rawSerializer, maxSerializationAttempts);
+
+        int maxThreadPoolSize = brooklynProperties.getConfig(PERSISTER_MAX_THREAD_POOL_SIZE);
 
         objectStore.createSubPath("entities");
         objectStore.createSubPath("locations");
@@ -139,6 +148,34 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
             }}));
     }
 
+    protected MementoSerializer<Object> getSerializerWithStandardClassLoader() {
+        return serializerWithStandardClassLoader;
+    }
+    
+    protected MementoSerializer<Object> getSerializerWithCustomClassLoader(LookupContext lookupContext, BrooklynObjectType type, String objectId) {
+        ClassLoader cl = getCustomClassLoaderForBrooklynObject(lookupContext, type, objectId);
+        if (cl==null) return serializerWithStandardClassLoader;
+        return getSerializerWithCustomClassLoader(lookupContext, cl);
+    }
+    
+    protected MementoSerializer<Object> getSerializerWithCustomClassLoader(LookupContext lookupContext, ClassLoader classLoader) {
+        int maxSerializationAttempts = brooklynProperties.getConfig(PERSISTER_MAX_SERIALIZATION_ATTEMPTS);
+        MementoSerializer<Object> rawSerializer = new XmlMementoSerializer<Object>(classLoader);
+        MementoSerializer<Object> result = new RetryingMementoSerializer<Object>(rawSerializer, maxSerializationAttempts);
+        result.setLookupContext(lookupContext);
+        return result;
+    }
+    
+    @Nullable protected ClassLoader getCustomClassLoaderForBrooklynObject(LookupContext lookupContext, BrooklynObjectType type, String objectId) {
+        BrooklynObject item = lookupContext.peek(type, objectId);
+        // TODO enrichers etc aren't yet known -- would need to backtrack to the entity to get them from bundles
+        if (item==null || item.getCatalogItemId()==null) {
+            return null;
+        }
+        CatalogItem<?, ?> catalogItem = lookupContext.lookupCatalogItem(item.getCatalogItemId());
+        return ClassLoaderFromBrooklynClassLoadingContext.of(CatalogUtils.newClassLoadingContext(lookupContext.lookupManagementContext(), catalogItem));
+    }
+    
     @Override public void enableWriteAccess() {
         writesAllowed = true;
     }
@@ -310,7 +347,7 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
                         break;
                     case CATALOG_ITEM:
                         try {
-                            CatalogItemMemento memento = (CatalogItemMemento) serializer.fromString(contents);
+                            CatalogItemMemento memento = (CatalogItemMemento) getSerializerWithStandardClassLoader().fromString(contents);
                             if (memento == null) {
                                 LOG.warn("No "+type.toCamelCase()+"-memento deserialized from " + objectId + "; ignoring and continuing");
                             } else {
@@ -350,7 +387,7 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
     }
     
     @Override
-    public BrooklynMemento loadMemento(BrooklynMementoRawData mementoData, LookupContext lookupContext, final RebindExceptionHandler exceptionHandler) throws IOException {
+    public BrooklynMemento loadMemento(BrooklynMementoRawData mementoData, final LookupContext lookupContext, final RebindExceptionHandler exceptionHandler) throws IOException {
         if (mementoData==null)
             mementoData = loadMementoRawData(exceptionHandler);
 
@@ -362,7 +399,7 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
             @Override
             public void visit(BrooklynObjectType type, String objectId, String contents) throws Exception {
                 try {
-                    Memento memento = (Memento) serializer.fromString(contents);
+                    Memento memento = (Memento) getSerializerWithCustomClassLoader(lookupContext, type, objectId).fromString(contents);
                     if (memento == null) {
                         LOG.warn("No "+type.toCamelCase()+"-memento deserialized from " + objectId + "; ignoring and continuing");
                     } else {
@@ -372,13 +409,15 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
                     exceptionHandler.onLoadMementoFailed(type, "memento "+objectId+" deserialization error", e);
                 }
             }
+
         };
 
-        serializer.setLookupContext(lookupContext);
+        // TODO not convinced this is single threaded on reads; maybe should get a new one each time?
+        getSerializerWithStandardClassLoader().setLookupContext(lookupContext);
         try {
             visitMemento("deserialization", mementoData, visitor, exceptionHandler);
         } finally {
-            serializer.unsetLookupContext();
+            getSerializerWithStandardClassLoader().unsetLookupContext();
         }
 
         BrooklynMemento result = builder.build();
@@ -607,7 +646,7 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
 
     private void persist(String subPath, Memento memento, PersistenceExceptionHandler exceptionHandler) {
         try {
-            getWriter(getPath(subPath, memento.getId())).put(serializer.toString(memento));
+            getWriter(getPath(subPath, memento.getId())).put(getSerializerWithStandardClassLoader().toString(memento));
         } catch (Exception e) {
             exceptionHandler.onPersistMementoFailed(memento, e);
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8bd76ad3/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java b/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
index 533cfd2..5f3fb5c 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
@@ -74,12 +74,12 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento
 
     private static final Logger LOG = LoggerFactory.getLogger(XmlMementoSerializer.class);
 
-    @SuppressWarnings("unused")
     private final ClassLoader classLoader;
     private LookupContext lookupContext;
 
     public XmlMementoSerializer(ClassLoader classLoader) {
         this.classLoader = checkNotNull(classLoader, "classLoader");
+        xstream.setClassLoader(this.classLoader);
         
         // old (deprecated in 070? or earlier) single-file persistence uses this keyword; TODO remove soon in 080 ?
         xstream.alias("brooklyn", MutableBrooklynMemento.class);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8bd76ad3/core/src/main/java/brooklyn/management/classloading/ClassLoaderFromBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/classloading/ClassLoaderFromBrooklynClassLoadingContext.java b/core/src/main/java/brooklyn/management/classloading/ClassLoaderFromBrooklynClassLoadingContext.java
new file mode 100644
index 0000000..0ac035a
--- /dev/null
+++ b/core/src/main/java/brooklyn/management/classloading/ClassLoaderFromBrooklynClassLoadingContext.java
@@ -0,0 +1,64 @@
+/*
+ * 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 brooklyn.management.classloading;
+
+import java.net.URL;
+
+public class ClassLoaderFromBrooklynClassLoadingContext extends ClassLoader {
+
+    /** Constructs a {@link ClassLoader} which delegates to the given {@link BrooklynClassLoadingContext} */
+    public static ClassLoader of(BrooklynClassLoadingContext clc) {
+        return new ClassLoaderFromBrooklynClassLoadingContext(clc);
+    }
+    
+    protected final BrooklynClassLoadingContext clc;
+
+    protected ClassLoaderFromBrooklynClassLoadingContext(BrooklynClassLoadingContext clc) {
+        this.clc = clc;
+    }
+    
+    @Override
+    public Class<?> findClass(String className) throws ClassNotFoundException {
+        Class<?> result = clc.loadClass(className);
+        if (result!=null) return result;
+        
+        // last resort. see comment in XStream CompositeClassLoader
+        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+        if (contextClassLoader != null) {
+            result = contextClassLoader.loadClass(className);
+            if (result!=null) return result;
+        }
+        return null;
+    }
+    
+    @Override
+    protected URL findResource(String name) {
+        URL result = clc.getResource(name);
+        if (result!=null) return result;
+        
+        // last resort. see comment in XStream CompositeClassLoader
+        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+        if (contextClassLoader != null) {
+            result = contextClassLoader.getResource(name);
+            if (result!=null) return result;
+        }
+        return null;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8bd76ad3/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java b/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
index 38a0456..e0b4f4e 100644
--- a/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
@@ -20,10 +20,6 @@ package brooklyn.entity.rebind.persister;
 
 import static org.testng.Assert.assertEquals;
 
-import java.io.FileInputStream;
-import java.io.InputStreamReader;
-import java.io.StringWriter;
-import java.nio.charset.Charset;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
@@ -37,6 +33,7 @@ import org.slf4j.LoggerFactory;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import brooklyn.basic.BrooklynObject;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.internal.CatalogItemBuilder;
 import brooklyn.catalog.internal.CatalogItemDtoAbstract;
@@ -44,6 +41,7 @@ import brooklyn.entity.Entity;
 import brooklyn.entity.Feed;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.rebind.BrooklynObjectType;
 import brooklyn.location.Location;
 import brooklyn.location.LocationSpec;
 import brooklyn.management.ManagementContext;
@@ -55,7 +53,6 @@ import brooklyn.test.entity.TestEntity;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
-import brooklyn.util.stream.Streams;
 
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
@@ -373,5 +370,32 @@ public class XmlMementoSerializerTest {
             }
             return null;
         }
+        
+        @Override
+        public BrooklynObject lookup(BrooklynObjectType type, String id) {
+            switch (type) {
+            case CATALOG_ITEM: return lookupCatalogItem(id);
+            case ENRICHER: return lookupEnricher(id);
+            case ENTITY: return lookupEntity(id);
+            case FEED: return lookupFeed(id);
+            case LOCATION: return lookupLocation(id);
+            case POLICY: return lookupPolicy(id);
+            case UNKNOWN: return null;
+            }
+            throw new IllegalStateException("Unexpected type "+type+" / id "+id);
+        }
+        @Override
+        public BrooklynObject peek(BrooklynObjectType type, String id) {
+            switch (type) {
+            case CATALOG_ITEM: return catalogItems.get(id);
+            case ENRICHER: return enrichers.get(id);
+            case ENTITY: return entities.get(id);
+            case FEED: return feeds.get(id);
+            case LOCATION: return locations.get(id);
+            case POLICY: return policies.get(id);
+            case UNKNOWN: return null;
+            }
+            throw new IllegalStateException("Unexpected type "+type+" / id "+id);
+        }
     };
 }


[02/18] incubator-brooklyn git commit: Catalog versioning - groundwork changes

Posted by he...@apache.org.
Catalog versioning - groundwork changes


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

Branch: refs/heads/master
Commit: 7f5992467762fc0fb43ca778850eaace7d4dc6fa
Parents: d1c0782
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Wed Nov 5 18:45:55 2014 +0200
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 11:49:47 2014 +0200

----------------------------------------------------------------------
 .../java/brooklyn/catalog/BrooklynCatalog.java  |  24 ++++-
 .../catalog/internal/BasicBrooklynCatalog.java  |  72 +++++++++----
 .../brooklyn/catalog/internal/CatalogDo.java    |  32 +++---
 .../catalog/internal/CatalogItemBuilder.java    |  26 +++--
 .../internal/CatalogItemDtoAbstract.java        |   2 +-
 .../catalog/internal/CatalogItemId.java         |  67 +++++++++++++
 .../brooklyn/catalog/internal/CatalogUtils.java |  23 ++++-
 .../brooklyn/camp/lite/CampYamlLiteTest.java    |   6 +-
 .../catalog/internal/CatalogDtoTest.java        |  24 +++--
 .../brooklyn/catalog/internal/CatalogItems.java |  87 ----------------
 .../entity/rebind/RebindCatalogItemTest.java    |  11 +-
 ...talogWhenCatalogPersistenceDisabledTest.java |   2 +-
 .../entity/rebind/RebindTestFixture.java        |  19 ++--
 .../persister/XmlMementoSerializerTest.java     |   2 +-
 .../osgi/OsgiVersionMoreEntityTest.java         | 100 ++++++++++++-------
 .../BrooklynComponentTemplateResolver.java      |   6 +-
 .../BrooklynEntityDecorationResolver.java       |  12 ++-
 .../lookup/AbstractTemplateBrooklynLookup.java  |  13 ++-
 .../camp/brooklyn/AbstractYamlTest.java         |   5 +-
 .../brooklyn/catalog/CatalogYamlEntityTest.java |  46 ++++++++-
 .../brooklyn/catalog/CatalogYamlPolicyTest.java |   4 +-
 21 files changed, 377 insertions(+), 206 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
index 2b58cb8..c5d0a63 100644
--- a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
+++ b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
@@ -26,17 +26,35 @@ import com.google.common.base.Predicate;
 public interface BrooklynCatalog {
 
     /** @return The item with the given ID or {@link brooklyn.catalog.CatalogItem#getRegisteredTypeName()
-     * registeredTypeName}, or null if not found. */
+     * registeredTypeName}, or null if not found.
+     * @deprecated since 0.7.0 use {@link #getCatalogItem(String, String)} */
+    @Deprecated
     CatalogItem<?,?> getCatalogItem(String idOrRegisteredTypeName);
 
+    /** @return The item with the given ID or {@link brooklyn.catalog.CatalogItem#getRegisteredTypeName()
+     * registeredTypeName}, or null if not found. */
+    CatalogItem<?,?> getCatalogItem(String idOrRegisteredTypeName, String version);
+
     /** @return Deletes the item with the given ID
-     * @throws NoSuchElementException if not found */
+     * @throws NoSuchElementException if not found
+     * @deprecated since 0.7.0 use {@link #deleteCatalogItem(String, String)} */
+    @Deprecated
     void deleteCatalogItem(String id);
 
+    /** @return Deletes the item with the given ID and version
+     * @throws NoSuchElementException if not found */
+    void deleteCatalogItem(String id, String version);
+
     /** variant of {@link #getCatalogItem(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.7.0 use {@link #getCatalogItem(Class<T>, String, String)} */
+    @Deprecated
     <T,SpecT> CatalogItem<T,SpecT> getCatalogItem(Class<T> type, String id);
 
+    /** variant of {@link #getCatalogItem(String, String)} which checks (and casts) type for convenience
+     * (returns null if type does not match) */
+    <T,SpecT> CatalogItem<T,SpecT> getCatalogItem(Class<T> type, String id, String version);
+
     /** @return All items in the catalog */
     <T,SpecT> Iterable<CatalogItem<T,SpecT>> getCatalogItems();
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
index 4d31964..eb2e793 100644
--- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
@@ -72,6 +72,7 @@ import com.google.common.collect.Iterables;
 
 public class BasicBrooklynCatalog implements BrooklynCatalog {
     private static final String POLICIES_KEY = "brooklyn.policies";
+    public static final String NO_VERSION = "0.0.0_SNAPSHOT";
 
     private static final Logger log = LoggerFactory.getLogger(BasicBrooklynCatalog.class);
 
@@ -166,30 +167,45 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         return catalog;
     }
 
-    protected CatalogItemDo<?,?> getCatalogItemDo(String idOrRegisteredTypeName) {
+    protected CatalogItemDo<?,?> getCatalogItemDo(String idOrRegisteredTypeName, String version) {
+        CatalogItemId versionedId = new CatalogItemId(idOrRegisteredTypeName, version);
         CatalogItemDo<?, ?> item = null;
         // TODO really need to remove redundancy of id v registered type;
         // should also remove "manual additions" bucket; just have one map a la osgi
-        if (manualAdditionsCatalog!=null) item = manualAdditionsCatalog.getIdCache().get(idOrRegisteredTypeName);
-        if (item == null) item = catalog.getIdCache().get(idOrRegisteredTypeName);
-        if (item == null && manualAdditionsCatalog!=null) item = manualAdditionsCatalog.getRegisteredTypeNameCache().get(idOrRegisteredTypeName);
-        if (item == null) item = catalog.getRegisteredTypeNameCache().get(idOrRegisteredTypeName);
+        if (manualAdditionsCatalog!=null) item = manualAdditionsCatalog.getIdCache().get(versionedId);
+        if (item == null) item = catalog.getIdCache().get(versionedId);
+        if (item == null && manualAdditionsCatalog!=null) item = manualAdditionsCatalog.getRegisteredTypeNameCache().get(versionedId);
+        if (item == null) item = catalog.getRegisteredTypeNameCache().get(versionedId);
         return item;
     }
     
     @Override
-    public CatalogItem<?,?> getCatalogItem(String idOrRegisteredTypeName) {
+    @Deprecated
+    public CatalogItem<?,?> getCatalogItem(String id) {
+        return getCatalogItem(id, NO_VERSION);
+    }
+    
+    @Override
+    public CatalogItem<?,?> getCatalogItem(String idOrRegisteredTypeName, String version) {
         if (idOrRegisteredTypeName == null) return null;
-        CatalogItemDo<?, ?> itemDo = getCatalogItemDo(idOrRegisteredTypeName);
+        checkNotNull(version, "version");
+        CatalogItemDo<?, ?> itemDo = getCatalogItemDo(idOrRegisteredTypeName, version);
         if (itemDo == null) return null;
         return itemDo.getDto();
     }
     
     @Override
+    @Deprecated
     public void deleteCatalogItem(String id) {
-        log.debug("Deleting manual catalog item from "+mgmt+": "+id);
+        deleteCatalogItem(id, NO_VERSION);
+    }
+
+    @Override
+    public void deleteCatalogItem(String id, String version) {
+        log.debug("Deleting manual catalog item from "+mgmt+": "+id + ":" + version);
         checkNotNull(id, "id");
-        CatalogItem<?, ?> item = getCatalogItem(id);
+        checkNotNull(id, "version");
+        CatalogItem<?, ?> item = getCatalogItem(id, version);
         CatalogItemDtoAbstract<?,?> itemDto = getAbstractCatalogItem(item);
         if (itemDto == null) {
             throw new NoSuchElementException("No catalog item found with id "+id);
@@ -208,11 +224,17 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
 
     }
 
-    @SuppressWarnings("unchecked")
     @Override
+    @Deprecated
     public <T,SpecT> CatalogItem<T,SpecT> getCatalogItem(Class<T> type, String id) {
-        if (id==null) return null;
-        CatalogItem<?,?> result = getCatalogItem(id);
+        return getCatalogItem(type, id, NO_VERSION);
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T,SpecT> CatalogItem<T,SpecT> getCatalogItem(Class<T> type, String id, String version) {
+        if (id==null || version==null) return null;
+        CatalogItem<?,?> result = getCatalogItem(id, version);
         if (result==null) return null;
         if (type==null || type.isAssignableFrom(result.getCatalogItemJavaType())) 
             return (CatalogItem<T,SpecT>)result;
@@ -238,8 +260,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     @SuppressWarnings("unchecked")
     @Override
     public <T, SpecT> SpecT createSpec(CatalogItem<T, SpecT> item) {
-        CatalogItemDo<T,SpecT> loadedItem = (CatalogItemDo<T, SpecT>) getCatalogItemDo(item.getId());
-
+        CatalogItemDo<T,SpecT> loadedItem = (CatalogItemDo<T, SpecT>) getCatalogItemDo(item.getId(), item.getVersion());
         Class<SpecT> specType = loadedItem.getSpecType();
         if (specType==null) return null;
 
@@ -343,7 +364,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         if (log.isDebugEnabled())
             log.debug("Loading class for catalog item " + item);
         checkNotNull(item);
-        CatalogItemDo<?,?> loadedItem = getCatalogItemDo(item.getId());
+        CatalogItemDo<?,?> loadedItem = getCatalogItemDo(item.getId(), item.getVersion());
         if (loadedItem==null) throw new NoSuchElementException("Unable to load '"+item.getId()+"' to instantiate it");
         return (Class<? extends T>) loadedItem.getJavaClass();
     }
@@ -407,11 +428,20 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
             }
         }
 
+        Maybe<Object> possibleVersion = catalog.getMaybe("version");
+        String version;
+        if (possibleVersion.isAbsent()) {
+            throw new IllegalArgumentException("'version' attribute missing in 'brooklyn.catalog' section.");
+        }
+
+        //could be coalesced to a number - can be one of Integer, Double, String
+        version = possibleVersion.get().toString();
+
         CatalogUtils.installLibraries(mgmt, libraries);
 
         AbstractBrooklynObjectSpec<?, ?> spec = createSpec(plan, CatalogUtils.newClassLoadingContext(mgmt, "<not created yet>", libraries, getRootClassLoader()));
 
-        CatalogItemBuilder<?> builder = createItemBuilder(spec, registeredTypeName)
+        CatalogItemBuilder<?> builder = createItemBuilder(spec, registeredTypeName, version)
             .libraries(libraries)
             .displayName(plan.getName())
             .description(plan.getDescription())
@@ -428,8 +458,6 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         if (iconUrl.isAbsent()) iconUrl = catalog.getMaybe("icon_url");
         if (iconUrl.isPresent()) builder.iconUrl((String)iconUrl.get());
 
-        // TODO #3 support version info
-
         CatalogItemDtoAbstract<?, ?> dto = builder.build();
         // Overwrite generated ID
         if (catalog.getMaybe("id").isPresent()) {
@@ -450,15 +478,15 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         }
     }
 
-    private CatalogItemBuilder<?> createItemBuilder(AbstractBrooklynObjectSpec<?, ?> spec, String itemId) {
+    private CatalogItemBuilder<?> createItemBuilder(AbstractBrooklynObjectSpec<?, ?> spec, String itemId, String version) {
         if (spec instanceof EntitySpec) {
             if (isApplicationSpec((EntitySpec<?>)spec)) {
-                return CatalogItemBuilder.newTemplate(itemId);
+                return CatalogItemBuilder.newTemplate(itemId, version);
             } else {
-                return CatalogItemBuilder.newEntity(itemId);
+                return CatalogItemBuilder.newEntity(itemId, version);
             }
         } else if (spec instanceof PolicySpec) {
-            return CatalogItemBuilder.newPolicy(itemId);
+            return CatalogItemBuilder.newPolicy(itemId, version);
         } else {
             throw new IllegalStateException("Unknown spec type " + spec.getClass().getName() + " (" + spec + ")");
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
index 33552f0..e2249e0 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
@@ -51,8 +51,8 @@ public class CatalogDo {
     
     List<CatalogDo> childrenCatalogs = new ArrayList<CatalogDo>();
     CatalogClasspathDo classpath;
-    private Map<String, CatalogItemDo<?,?>> cacheById;
-    private Map<String, CatalogItemDo<?,?>> cacheByRegisteredTypeName;
+    private Map<CatalogItemId, CatalogItemDo<?,?>> cacheById;
+    private Map<CatalogItemId, CatalogItemDo<?,?>> cacheByRegisteredTypeName;
 
     AggregateClassLoader childrenClassLoader = AggregateClassLoader.newInstanceWithNoLoaders();
     ClassLoader recursiveClassLoader;
@@ -159,26 +159,26 @@ public class CatalogDo {
         return childL;
     }
 
-    protected Map<String, CatalogItemDo<?,?>> getIdCache() {
-        Map<String, CatalogItemDo<?,?>> cache = this.cacheById;
+    protected Map<CatalogItemId, CatalogItemDo<?,?>> getIdCache() {
+        Map<CatalogItemId, CatalogItemDo<?,?>> cache = this.cacheById;
         if (cache==null) cache = buildCaches();
         return cache;
     }
 
-    protected Map<String, CatalogItemDo<?,?>> getRegisteredTypeNameCache() {
-        Map<String, CatalogItemDo<?,?>> cache = this.cacheByRegisteredTypeName;
+    protected Map<CatalogItemId, CatalogItemDo<?,?>> getRegisteredTypeNameCache() {
+        Map<CatalogItemId, CatalogItemDo<?,?>> cache = this.cacheByRegisteredTypeName;
         if (cache==null) cache = buildCaches();
         return cache;
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
-    protected synchronized Map<String, CatalogItemDo<?,?>> buildCaches() {
+    protected synchronized Map<CatalogItemId, CatalogItemDo<?,?>> buildCaches() {
         if (cacheById != null) return cacheById;
         CatalogUtils.logDebugOrTraceIfRebinding(log, "Building cache for {}", this);
         if (!isLoaded()) 
             log.debug("Catalog not fully loaded when loading cache of "+this);
         
-        Map<String, CatalogItemDo<?,?>> cache = new LinkedHashMap<String, CatalogItemDo<?,?>>();
+        Map<CatalogItemId, CatalogItemDo<?,?>> cache = new LinkedHashMap<CatalogItemId, CatalogItemDo<?,?>>();
         
         // build the cache; first from children catalogs, then from local entities
         // so that root and near-root takes precedence over deeper items;
@@ -196,11 +196,11 @@ public class CatalogDo {
             List<CatalogItemDtoAbstract<?,?>> entriesReversed = MutableList.copyOf(dto.getUniqueEntries());
             Collections.reverse(entriesReversed);
             for (CatalogItemDtoAbstract<?,?> entry: entriesReversed)
-                cache.put(entry.getId(), new CatalogItemDo(this, entry));
+                cache.put(new CatalogItemId(entry), new CatalogItemDo(this, entry));
         }
-        Map<String, CatalogItemDo<?, ?>> typeNameCache = Maps.newHashMap();
+        Map<CatalogItemId, CatalogItemDo<?, ?>> typeNameCache = Maps.newHashMap();
         for (CatalogItemDo<?, ?> entry : cache.values()) {
-            typeNameCache.put(entry.getRegisteredTypeName(), entry);
+            typeNameCache.put(new CatalogItemId(entry.getRegisteredTypeName(), entry.getVersion()), entry);
         }
         this.cacheById = cache;
         this.cacheByRegisteredTypeName = typeNameCache;
@@ -225,9 +225,11 @@ public class CatalogDo {
     public synchronized void addEntry(CatalogItemDtoAbstract<?,?> entry) {
         dto.addEntry(entry);
         if (cacheById != null) {
+            CatalogItemId cdoId = new CatalogItemId(entry);
             CatalogItemDo<?, ?> cdo = new CatalogItemDo(this, entry);
-            cacheById.put(entry.getId(), cdo);
-            cacheByRegisteredTypeName.put(entry.getRegisteredTypeName(), cdo);
+            cacheById.put(cdoId, cdo);
+            CatalogItemId cdoTypeId = new CatalogItemId(entry.getRegisteredTypeName(), cdo.getVersion());
+            cacheByRegisteredTypeName.put(cdoTypeId, cdo);
         }
         if (mgmt != null) {
             mgmt.getRebindManager().getChangeListener().onManaged(entry);
@@ -240,8 +242,8 @@ public class CatalogDo {
     public synchronized void deleteEntry(CatalogItemDtoAbstract<?, ?> entry) {
         dto.removeEntry(entry);
         if (cacheById != null) {
-            cacheById.remove(entry.getId());
-            cacheByRegisteredTypeName.remove(entry.getRegisteredTypeName());
+            cacheById.remove(new CatalogItemId(entry));
+            cacheByRegisteredTypeName.remove(new CatalogItemId(entry.getRegisteredTypeName(), entry.getVersion()));
         }
         if (mgmt != null) {
             // TODO: Can the entry be in more than one catalogue? The management context has no notion of

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java
index 3468f05..8d2513b 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java
@@ -23,19 +23,22 @@ import com.google.common.base.Preconditions;
 public class CatalogItemBuilder<CatalogItemType extends CatalogItemDtoAbstract<?, ?>> {
     private CatalogItemType dto;
 
-    public static CatalogItemBuilder<CatalogEntityItemDto> newEntity(String registeredTypeName) {
+    public static CatalogItemBuilder<CatalogEntityItemDto> newEntity(String registeredTypeName, String version) {
         return new CatalogItemBuilder<CatalogEntityItemDto>(new CatalogEntityItemDto())
-                .registeredTypeName(registeredTypeName);
+                .registeredTypeName(registeredTypeName)
+                .version(version);
     }
 
-    public static CatalogItemBuilder<CatalogTemplateItemDto> newTemplate(String registeredTypeName) {
+    public static CatalogItemBuilder<CatalogTemplateItemDto> newTemplate(String registeredTypeName, String version) {
         return new CatalogItemBuilder<CatalogTemplateItemDto>(new CatalogTemplateItemDto())
-                .registeredTypeName(registeredTypeName);
+                .registeredTypeName(registeredTypeName)
+                .version(version);
     }
 
-    public static CatalogItemBuilder<CatalogPolicyItemDto> newPolicy(String registeredTypeName) {
+    public static CatalogItemBuilder<CatalogPolicyItemDto> newPolicy(String registeredTypeName, String version) {
         return new CatalogItemBuilder<CatalogPolicyItemDto>(new CatalogPolicyItemDto())
-                .registeredTypeName(registeredTypeName);
+                .registeredTypeName(registeredTypeName)
+                .version(version);
     }
 
     public CatalogItemBuilder(CatalogItemType dto) {
@@ -48,6 +51,12 @@ public class CatalogItemBuilder<CatalogItemType extends CatalogItemDtoAbstract<?
         return this;
     }
 
+    @Deprecated
+    public CatalogItemBuilder<CatalogItemType> javaType(String javaType) {
+        dto.javaType = javaType;
+        return this;
+    }
+
     /** @deprecated since 0.7.0 use {@link #displayName}*/
     @Deprecated
     public CatalogItemBuilder<CatalogItemType> name(String name) {
@@ -69,6 +78,11 @@ public class CatalogItemBuilder<CatalogItemType extends CatalogItemDtoAbstract<?
         return this;
     }
 
+    public CatalogItemBuilder<CatalogItemType> version(String version) {
+        dto.version = version;
+        return this;
+    }
+
     public CatalogItemBuilder<CatalogItemType> libraries(CatalogLibrariesDto libraries) {
         dto.libraries = libraries;
         return this;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
index 704976b..e78721a 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
@@ -49,7 +49,7 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
     @SetFromFlag String javaType;
     @SetFromFlag String description;
     @SetFromFlag String iconUrl;
-    @SetFromFlag String version;
+    @SetFromFlag String version = BasicBrooklynCatalog.NO_VERSION;
     @SetFromFlag CatalogLibrariesDto libraries;
     @SetFromFlag String planYaml;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/core/src/main/java/brooklyn/catalog/internal/CatalogItemId.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemId.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemId.java
new file mode 100644
index 0000000..47f7b62
--- /dev/null
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemId.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 brooklyn.catalog.internal;
+
+import brooklyn.catalog.CatalogItem;
+
+class CatalogItemId {
+    private String id;
+    private String version;
+    
+    public CatalogItemId(String id, String version) {
+        this.id = id;
+        this.version = version;
+    }
+    
+    public CatalogItemId(CatalogItem<?, ?> item) {
+        this(item.getId(), item.getVersion());
+    }
+    
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((id == null) ? 0 : id.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;
+        CatalogItemId other = (CatalogItemId) obj;
+        if (id == null) {
+            if (other.id != null)
+                return false;
+        } else if (!id.equals(other.id))
+            return false;
+        if (version == null) {
+            if (other.version != null)
+                return false;
+        } else if (!version.equals(other.version))
+            return false;
+        return true;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
index 6fd1f94..6f33574 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
@@ -50,6 +50,8 @@ import brooklyn.util.time.Time;
 public class CatalogUtils {
     private static final Logger log = LoggerFactory.getLogger(CatalogUtils.class);
 
+    public static final char VERSION_DELIMITER = ':';
+
     public static BrooklynClassLoadingContext newClassLoadingContext(ManagementContext mgmt, CatalogItem<?, ?> item) {
         CatalogItemLibraries libraries = item.getLibraries();
         // TODO getLibraries() should never be null but sometimes it is still
@@ -143,6 +145,25 @@ public class CatalogUtils {
         else
             log.debug(message, args);
     }
-    
+
+    public static String getIdFromVersionedId(String versionedId) {
+        if (versionedId == null) return null;
+        int versionDelimiterPos = versionedId.lastIndexOf(CatalogUtils.VERSION_DELIMITER);
+        if (versionDelimiterPos != -1) {
+            return versionedId.substring(0, versionDelimiterPos);
+        } else {
+            return null;
+        }
+    }
+
+    public static String getVersionFromVersionedId(String versionedId) {
+        if (versionedId == null) return null;
+        int versionDelimiterPos = versionedId.lastIndexOf(CatalogUtils.VERSION_DELIMITER);
+        if (versionDelimiterPos != -1) {
+            return versionedId.substring(versionDelimiterPos+1);
+        } else {
+            return null;
+        }
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
index e952f00..9eb56fd 100644
--- a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
+++ b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
@@ -71,6 +71,7 @@ import com.google.common.collect.Sets;
 /** Tests of lightweight CAMP integration. Since the "real" integration is in brooklyn-camp project,
  * but some aspects of CAMP we want to be able to test here. */
 public class CampYamlLiteTest {
+    private static final String TEST_VERSION = "0.1.2";
 
     private static final Logger log = LoggerFactory.getLogger(CampYamlLiteTest.class);
     
@@ -209,7 +210,7 @@ public class CampYamlLiteTest {
                 "  name: My Catalog App\n" +
                 "  description: My description\n" +
                 "  icon_url: classpath:/brooklyn/osgi/tests/icon.gif\n" +
-                "  version: 0.1.2\n" +
+                "  version: " + TEST_VERSION + "\n" +
                 "  libraries:\n" +
                 "  - url: " + bundleUrl + "\n" +
                 "\n" +
@@ -218,13 +219,12 @@ public class CampYamlLiteTest {
     }
 
     private void assertMgmtHasSampleMyCatalogApp(String registeredTypeName, String bundleUrl) {
-        CatalogItem<?, ?> item = mgmt.getCatalog().getCatalogItem(registeredTypeName);
+        CatalogItem<?, ?> item = mgmt.getCatalog().getCatalogItem(registeredTypeName, TEST_VERSION);
         assertNotNull(item, "failed to load item with id=" + registeredTypeName + " from catalog. Entries were: " +
                 Joiner.on(",").join(mgmt.getCatalog().getCatalogItems()));
         assertEquals(item.getRegisteredTypeName(), registeredTypeName);
 
         // stored as yaml, not java
-//      assertEquals(entityItem.getJavaType(), "brooklyn.test.entity.TestEntity");
         assertNotNull(item.getPlanYaml());
         Assert.assertTrue(item.getPlanYaml().contains("io.camp.mock:AppServer"));
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
index 1bc4392..8e6d704 100644
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
+++ b/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
@@ -76,7 +76,7 @@ public class CatalogDtoTest {
         CatalogDo loader = new CatalogDo(managementContext, root).load();
         
         // test app comes from jar, by default
-        CatalogItemDo<?,?> worker = loader.getRegisteredTypeNameCache().get(TestApplication.class.getCanonicalName());
+        CatalogItemDo<?,?> worker = loader.getRegisteredTypeNameCache().get(new CatalogItemId(TestApplication.class.getCanonicalName(), BasicBrooklynCatalog.NO_VERSION));
         assertNotNull(worker);
         assertEquals(worker.getDisplayName(), "Test App from JAR");
         
@@ -96,10 +96,8 @@ public class CatalogDtoTest {
                 CatalogDto.newNamedInstance("Test Entities from Java", null, "test-java"));
         testEntitiesJavaCatalog.setClasspathScanForEntities(CatalogScanningModes.NONE);
         testEntitiesJavaCatalog.addToClasspath(MavenRetriever.localUrl(BrooklynMavenArtifacts.artifact("", "brooklyn-core", "jar", "tests")));
-        testEntitiesJavaCatalog.addEntry(CatalogItems.newTemplateFromJava(
-                TestApplication.class.getCanonicalName(), "Test App from JAR"));
-        testEntitiesJavaCatalog.addEntry(CatalogItems.newEntityFromJava(
-                TestEntity.class.getCanonicalName(), "Test Entity from JAR"));
+        testEntitiesJavaCatalog.addEntry(CatalogItemBuilder.newTemplate(TestApplication.class.getCanonicalName(), "Test App from JAR").build());
+        testEntitiesJavaCatalog.addEntry(CatalogItemBuilder.newEntity(TestEntity.class.getCanonicalName(), "Test Entity from JAR").build());
         root.addCatalog(testEntitiesJavaCatalog.dto);
 
         CatalogDo testEntitiesJavaCatalogScanning = new CatalogDo(
@@ -114,7 +112,7 @@ public class CatalogDtoTest {
                 CatalogDto.newNamedInstance("Test Entities from OSGi",
                         "A catalog whose entries define their libraries as a list of OSGi bundles", "test-osgi-defined"));
         osgiCatalog.setClasspathScanForEntities(CatalogScanningModes.NONE);
-        CatalogEntityItemDto osgiEntity = CatalogItems.newEntityFromJava(TestEntity.class.getCanonicalName(), "Test Entity from OSGi");
+        CatalogEntityItemDto osgiEntity = CatalogItemBuilder.newEntity(TestEntity.class.getCanonicalName(), "Test Entity from OSGi").build();
         // NB: this is not actually an OSGi bundle, but it's okay as we don't instantiate the bundles ahead of time (currently)
         osgiEntity.libraries.addBundle(MavenRetriever.localUrl(BrooklynMavenArtifacts.artifact("", "brooklyn-core", "jar", "tests")));
         testEntitiesJavaCatalog.addEntry(osgiEntity);
@@ -123,4 +121,18 @@ public class CatalogDtoTest {
         root.addCatalog(CatalogDto.newLinkedInstance("classpath://brooklyn-catalog-empty.xml"));
         return root.dto;
     }
+    
+    @Test
+    public void testVersionedIdSplitter() {
+        String id = "simple.id";
+        String version = "0.1.2";
+        String versionedId = id + CatalogUtils.VERSION_DELIMITER + version;
+        
+        Assert.assertNull(CatalogUtils.getIdFromVersionedId(null));
+        Assert.assertNull(CatalogUtils.getVersionFromVersionedId(null));
+        Assert.assertNull(CatalogUtils.getIdFromVersionedId(id));
+        Assert.assertNull(CatalogUtils.getVersionFromVersionedId(version));
+        Assert.assertEquals(CatalogUtils.getIdFromVersionedId(versionedId), id);
+        Assert.assertEquals(CatalogUtils.getVersionFromVersionedId(versionedId), version);
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/core/src/test/java/brooklyn/catalog/internal/CatalogItems.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogItems.java b/core/src/test/java/brooklyn/catalog/internal/CatalogItems.java
deleted file mode 100644
index 80b41d5..0000000
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogItems.java
+++ /dev/null
@@ -1,87 +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 brooklyn.catalog.internal;
-
-import com.google.common.annotations.Beta;
-
-import io.brooklyn.camp.spi.pdp.DeploymentPlan;
-
-/** Only for internal use / use in tests. */
-public class CatalogItems {
-
-    public static CatalogTemplateItemDto newTemplateFromJava(String javaType, String name) {
-        return newTemplateFromJava(javaType, name, null, null);
-    }
-
-    public static CatalogTemplateItemDto newTemplateFromJava(String javaType, String name, String description) {
-        return newTemplateFromJava(javaType, name, description, null);
-    }
-
-    public static CatalogTemplateItemDto newTemplateFromJava(String javaType, String name, String description, CatalogLibrariesDto libraries) {
-        return set(new CatalogTemplateItemDto(), javaType, javaType, name, description, libraries);
-    }
-
-    public static CatalogEntityItemDto newEntityFromPlan(String registeredTypeName, CatalogLibrariesDto libraries, DeploymentPlan plan, String underlyingPlanYaml) {
-        CatalogEntityItemDto target = set(new CatalogEntityItemDto(), registeredTypeName, null, plan.getName(), plan.getDescription(), libraries);
-        target.planYaml = underlyingPlanYaml;
-        return target;
-    }
-    
-    // TODO just added this method to expose registeredTypeName for tests; but it should all go away;
-    // so long as tests pass no need to keep deprecation imho
-    @Beta
-    public static CatalogEntityItemDto newEntityFromJavaWithRegisteredTypeName(String registeredTypeName, String javaType) {
-        return set(new CatalogEntityItemDto(), registeredTypeName, javaType, registeredTypeName, null, null);
-    }
-    public static CatalogEntityItemDto newEntityFromJava(String javaType, String name) {
-        return newEntityFromJava(javaType, name, null, null);
-    }
-
-    public static CatalogEntityItemDto newEntityFromJava(String javaType, String name, String description) {
-        return newEntityFromJava(javaType, name, description, null);
-    }
-
-    public static CatalogEntityItemDto newEntityFromJava(String javaType, String name, String description, CatalogLibrariesDto libraries) {
-        return set(new CatalogEntityItemDto(), javaType, javaType, name, description, libraries);
-    }
-
-    public static CatalogPolicyItemDto newPolicyFromJava(String javaType, String name) {
-        return newPolicyFromJava(javaType, name, null, null);
-    }
-
-    public static CatalogPolicyItemDto newPolicyFromJava(String javaType, String name, String description) {
-        return newPolicyFromJava(javaType, name, description, null);
-    }
-
-    public static CatalogPolicyItemDto newPolicyFromJava(String javaType, String name, String description, CatalogLibrariesDto libraries) {
-        return set(new CatalogPolicyItemDto(), javaType, javaType, name, description, libraries);
-    }
- 
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    private static <T extends CatalogItemDtoAbstract> T set(T target, String registeredType, String javaType, String name,
-            String description, CatalogLibrariesDto libraries) {
-        target.registeredType = registeredType;
-        target.javaType = javaType;
-        target.name = name;
-        target.description = description;
-        target.libraries = libraries != null ? libraries : new CatalogLibrariesDto();
-        return target;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
index f2f4fad..a601bb7 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
@@ -47,6 +47,8 @@ import com.google.common.collect.Iterables;
 
 public class RebindCatalogItemTest extends RebindTestFixtureWithApp {
 
+    private static final String TEST_VERSION = "0.0.1";
+
     private static final Logger LOG = LoggerFactory.getLogger(RebindCatalogItemTest.class);
     public static class MyPolicy extends AbstractPolicy {}
     private boolean catalogPersistenceWasEnabled;
@@ -101,7 +103,10 @@ public class RebindCatalogItemTest extends RebindTestFixtureWithApp {
 
     @Test
     public void testAddAndRebindEntity() throws Exception {
-        String yaml = "name: rebind-yaml-catalog-item-test\n" +
+        String yaml = 
+                "name: rebind-yaml-catalog-item-test\n" +
+                "brooklyn.catalog:\n" +
+                "  version: " + TEST_VERSION + "\n" +
                 "services:\n" +
                 "- type: io.camp.mock:AppServer";
         CatalogItem<?, ?> added = origManagementContext.getCatalog().addItem(yaml);
@@ -121,7 +126,7 @@ public class RebindCatalogItemTest extends RebindTestFixtureWithApp {
         String yaml = "name: Test Policy\n" +
                 "brooklyn.catalog:\n" +
                 "  id: sample_policy\n" +
-                "  version: 0.1.0\n" +
+                "  version: " + TEST_VERSION + "\n" +
                 "brooklyn.policies: \n" +
                 "- type: brooklyn.entity.rebind.RebindCatalogItemTest$MyPolicy\n" +
                 "  brooklyn.config:\n" +
@@ -145,7 +150,7 @@ public class RebindCatalogItemTest extends RebindTestFixtureWithApp {
         // persistence window. Because BrooklynMementoPersisterToObjectStore applies writes/deletes
         // asynchronously the winner is down to a race and the test might pass or fail.
         origManagementContext.getRebindManager().forcePersistNow();
-        origManagementContext.getCatalog().deleteCatalogItem(toRemove.getId());
+        origManagementContext.getCatalog().deleteCatalogItem(toRemove.getId(), toRemove.getVersion());
         assertEquals(Iterables.size(origManagementContext.getCatalog().getCatalogItems()), 0);
         rebindAndAssertCatalogsAreEqual();
         assertEquals(Iterables.size(newManagementContext.getCatalog().getCatalogItems()), 0);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
index 8852e15..728126e 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
@@ -82,7 +82,7 @@ public class RebindCatalogWhenCatalogPersistenceDisabledTest extends RebindTestF
     public void testModificationsToCatalogAreNotPersistedWhenCatalogPersistenceFeatureIsDisabled() throws Exception {
         assertEquals(Iterables.size(origManagementContext.getCatalog().getCatalogItems()), 1);
         CatalogItem<Object, Object> toRemove = Iterables.getOnlyElement(origManagementContext.getCatalog().getCatalogItems());
-        origManagementContext.getCatalog().deleteCatalogItem(toRemove.getId());
+        origManagementContext.getCatalog().deleteCatalogItem(toRemove.getId(), toRemove.getVersion());
         assertEquals(Iterables.size(origManagementContext.getCatalog().getCatalogItems()), 0);
 
         rebind();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java b/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
index fb4e286..2973276 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
@@ -32,9 +32,9 @@ import org.testng.annotations.BeforeMethod;
 
 import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.Entities;
-import brooklyn.entity.basic.EntityFunctions;
 import brooklyn.entity.basic.StartableApplication;
 import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
 import brooklyn.entity.rebind.persister.FileBasedObjectStore;
@@ -53,7 +53,6 @@ import brooklyn.util.time.Duration;
 
 import com.google.api.client.util.Sets;
 import com.google.common.annotations.Beta;
-import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
@@ -242,16 +241,20 @@ public abstract class RebindTestFixture<T extends StartableApplication> {
         Set<String> expectedIds = getCatalogItemIds(expected.getCatalogItems());
         assertEquals(actualIds.size(), Iterables.size(actual.getCatalogItems()), "id keyset size != size of catalog. Are there duplicates in the catalog?");
         assertEquals(actualIds, expectedIds);
-        for (String id : actualIds) {
-            assertCatalogItemsEqual(actual.getCatalogItem(id), expected.getCatalogItem(id));
+        for (String versionedId : actualIds) {
+            String id = CatalogUtils.getIdFromVersionedId(versionedId);
+            String version = CatalogUtils.getVersionFromVersionedId(versionedId);
+            assertCatalogItemsEqual(actual.getCatalogItem(id, version), expected.getCatalogItem(id, version));
         }
     }
 
     private Set<String> getCatalogItemIds(Iterable<CatalogItem<Object, Object>> catalogItems) {
-        return FluentIterable.from(catalogItems)
-                .transform(EntityFunctions.id())
-                .copyInto(Sets.<String>newHashSet());
-    }
+        Set<String> itemIds = Sets.<String>newHashSet();
+        for (CatalogItem<?, ?> item : catalogItems) {
+            itemIds.add(item.getId() + ":" + item.getVersion());
+        }
+        return itemIds;
+   }
 
     protected void assertCatalogItemsEqual(CatalogItem<?, ?> actual, CatalogItem<?, ?> expected) {
         assertEquals(actual.getClass(), expected.getClass());

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java b/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
index ebcd0e3..37c9815 100644
--- a/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
@@ -161,7 +161,7 @@ public class XmlMementoSerializerTest {
         final TestApplication app = TestApplication.Factory.newManagedInstanceForTests();
         ManagementContext managementContext = app.getManagementContext();
         try {
-            CatalogItem<?, ?> catalogItem = CatalogItemBuilder.newEntity("registeredtypename")
+            CatalogItem<?, ?> catalogItem = CatalogItemBuilder.newEntity("registeredtypename", "0.0.1")
                     .displayName("test catalog item")
                     .description("description")
                     .plan("yaml plan")

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java b/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java
index 4b653ab..92fdb47 100644
--- a/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java
+++ b/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java
@@ -21,6 +21,7 @@ package brooklyn.management.osgi;
 import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
 import java.util.List;
 
 import org.osgi.framework.Bundle;
@@ -33,7 +34,8 @@ import org.testng.annotations.Test;
 
 import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.internal.CatalogEntityItemDto;
-import brooklyn.catalog.internal.CatalogItems;
+import brooklyn.catalog.internal.CatalogItemBuilder;
+import brooklyn.catalog.internal.CatalogLibrariesDto;
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.Entities;
@@ -71,6 +73,8 @@ public class OsgiVersionMoreEntityTest {
     public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_PATH = OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_PATH;
     public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL = "classpath:"+BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_PATH;
     
+    public static final String TEST_VERSION = "0.1.0";
+    
     protected LocalManagementContext mgmt;
     protected TestApplication app;
 
@@ -119,25 +123,26 @@ public class OsgiVersionMoreEntityTest {
         }
     }
     
-    protected CatalogItem<?, ?> addCatalogItemWithTypeAsName(String type, String ...libraries) {
-        return addCatalogItemWithNameAndType(type, type, libraries);
+    protected CatalogItem<?, ?> addCatalogItemWithTypeAsName(String type, String version, String ...libraries) {
+        return addCatalogItemWithNameAndType(type, version, type, libraries);
     }
     @SuppressWarnings("deprecation")
-    protected CatalogItem<?, ?> addCatalogItemWithNameAndType(String symName, String type, String ...libraries) {
-        CatalogEntityItemDto c1 = newCatalogItemWithNameAndType(symName, type, libraries);
+    protected CatalogItem<?, ?> addCatalogItemWithNameAndType(String symName, String version, String type, String ...libraries) {
+        CatalogEntityItemDto c1 = newCatalogItemWithNameAndType(symName, version, type, libraries);
         mgmt.getCatalog().addItem(c1);
-        CatalogItem<?, ?> c2 = mgmt.getCatalog().getCatalogItem(type);
+        CatalogItem<?, ?> c2 = mgmt.getCatalog().getCatalogItem(type, version);
         return c2;
     }
 
-    static CatalogEntityItemDto newCatalogItemWithTypeAsName(String type, String ...libraries) {
-        return newCatalogItemWithNameAndType(type, type, libraries);
+    static CatalogEntityItemDto newCatalogItemWithTypeAsName(String type, String version, String ...libraries) {
+        return newCatalogItemWithNameAndType(type, version, type, libraries);
     }
-    static CatalogEntityItemDto newCatalogItemWithNameAndType(String symName, String type, String ...libraries) {
-        CatalogEntityItemDto c1 = CatalogItems.newEntityFromJavaWithRegisteredTypeName(symName, type);
-        c1.setCatalogItemId(symName);
-        for (String library: libraries)
-            c1.getLibrariesDto().addBundle(library);
+    static CatalogEntityItemDto newCatalogItemWithNameAndType(String symName, String version, String type, String ...libraries) {
+        CatalogEntityItemDto c1 = CatalogItemBuilder.newEntity(symName, version)
+                .javaType(type)
+                .libraries(CatalogLibrariesDto.from(Arrays.asList(libraries)))
+                .build();
+        c1.setCatalogItemId(type);
         return c1;
     }
 
@@ -183,7 +188,10 @@ public class OsgiVersionMoreEntityTest {
 
     @Test
     public void testMoreEntitiesV1() throws Exception {
-        CatalogItem<?, ?> c2 = addCatalogItemWithTypeAsName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
+        CatalogItem<?, ?> c2 = addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                TEST_VERSION,
+                BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
         
         // test load and instantiate
         Entity me = addItemFromCatalog(c2);
@@ -209,13 +217,18 @@ public class OsgiVersionMoreEntityTest {
 
     @Test
     public void testMoreEntitiesV1Policy() throws Exception {
-        CatalogItem<?, ?> c2 = addCatalogItemWithTypeAsName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
+        CatalogItem<?, ?> c2 = addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                TEST_VERSION,
+                BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
         
         // test load and instantiate
         Entity me = addItemFromCatalog(c2);
 
-        CatalogItem<?, ?> cp = addCatalogItemWithTypeAsName(OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_POLICY, 
-            BROOKLYN_TEST_OSGI_ENTITIES_URL);
+        CatalogItem<?, ?> cp = addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_POLICY,
+                TEST_VERSION,
+                BROOKLYN_TEST_OSGI_ENTITIES_URL);
         me.addPolicy(getPolicySpec(cp));
         
         Assert.assertEquals(me.getPolicies().size(), 1, "Wrong number of policies: "+me.getPolicies());
@@ -229,8 +242,10 @@ public class OsgiVersionMoreEntityTest {
 
     @Test
     public void testMoreEntitiesV2FailsWithoutBasicTestOsgiEntitiesBundle() throws Exception {
-        CatalogItem<?, ?> c2 = addCatalogItemWithTypeAsName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, 
-            BROOKLYN_TEST_MORE_ENTITIES_V2_URL);
+        CatalogItem<?, ?> c2 = addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                TEST_VERSION,
+                BROOKLYN_TEST_MORE_ENTITIES_V2_URL);
         
         // test load and instantiate
         try {
@@ -247,8 +262,11 @@ public class OsgiVersionMoreEntityTest {
     // and has policy, with policy item catalog ID is reasonable
     @Test
     public void testMoreEntitiesV2() throws Exception {
-        CatalogItem<?, ?> c2 = addCatalogItemWithTypeAsName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, 
-            BROOKLYN_TEST_MORE_ENTITIES_V2_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
+        CatalogItem<?, ?> c2 = addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                TEST_VERSION,
+                BROOKLYN_TEST_MORE_ENTITIES_V2_URL,
+                BROOKLYN_TEST_OSGI_ENTITIES_URL);
         
         // test load and instantiate
         Entity me = addItemFromCatalog(c2);
@@ -266,13 +284,17 @@ public class OsgiVersionMoreEntityTest {
 
     @Test
     public void testMoreEntitiesV1ThenV2GivesV2() throws Exception {
-        addCatalogItemWithTypeAsName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, 
-            BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
-        addCatalogItemWithTypeAsName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, 
-            BROOKLYN_TEST_MORE_ENTITIES_V2_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
+        addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                TEST_VERSION,
+                BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
+        addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                TEST_VERSION,
+                BROOKLYN_TEST_MORE_ENTITIES_V2_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
         
         // test load and instantiate
-        Entity me = addItemFromCatalog( mgmt.getCatalog().getCatalogItem(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY) );
+        Entity me = addItemFromCatalog( mgmt.getCatalog().getCatalogItem(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, TEST_VERSION) );
         
         assertV2MethodCall(me);
         assertV2EffectorCall(me);
@@ -281,13 +303,17 @@ public class OsgiVersionMoreEntityTest {
 
     @Test
     public void testMoreEntitiesV2ThenV1GivesV1() throws Exception {
-        addCatalogItemWithTypeAsName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, 
-            BROOKLYN_TEST_MORE_ENTITIES_V2_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
-        addCatalogItemWithTypeAsName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, 
-            BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
+        addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                TEST_VERSION,
+                BROOKLYN_TEST_MORE_ENTITIES_V2_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
+        addCatalogItemWithTypeAsName(
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                TEST_VERSION,
+                BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
         
         // test load and instantiate
-        Entity me = addItemFromCatalog( mgmt.getCatalog().getCatalogItem(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY) );
+        Entity me = addItemFromCatalog( mgmt.getCatalog().getCatalogItem(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, TEST_VERSION) );
         /*
          * WARNING - Weird maven-bundle-plugin and OSGi behaviour.  Some caveats:
          * <p>
@@ -346,30 +372,30 @@ public class OsgiVersionMoreEntityTest {
 
         Maybe<Bundle> versionIgnoresInvalidPreferredUrl = Osgis.bundleFinder(mgmt.getOsgiManager().get().getFramework())
             .symbolicName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL)
-            .version("0.1.0")
+            .version(TEST_VERSION)
             .preferringFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL).find();
         Assert.assertTrue(versionIgnoresInvalidPreferredUrl.isPresent());
-        Assert.assertEquals(versionIgnoresInvalidPreferredUrl.get().getVersion().toString(), "0.1.0");
+        Assert.assertEquals(versionIgnoresInvalidPreferredUrl.get().getVersion().toString(), TEST_VERSION);
 
         Maybe<Bundle> versionRespectsInvalidRequiredUrl = Osgis.bundleFinder(mgmt.getOsgiManager().get().getFramework())
             .symbolicName(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL)
-            .version("0.1.0")
+            .version(TEST_VERSION)
             .requiringFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL).find();
         Assert.assertFalse(versionRespectsInvalidRequiredUrl.isPresent());
 
         // test entity resolution
         
-        Entity v2 = addItemFromCatalog( mgmt.getCatalog().getCatalogItem("v2") );
+        Entity v2 = addItemFromCatalog( mgmt.getCatalog().getCatalogItem("v2", TEST_VERSION) );
         assertV2MethodCall(v2);
         assertV2EffectorCall(v2);
         Assert.assertEquals(v2.getPolicies().size(), 1, "Wrong number of policies: "+v2.getPolicies());
 
-        Entity v2_evil = addItemFromCatalog( mgmt.getCatalog().getCatalogItem("v2-evil") );
+        Entity v2_evil = addItemFromCatalog( mgmt.getCatalog().getCatalogItem("v2-evil", TEST_VERSION) );
         assertV2EvilTwinMethodCall(v2_evil);
         assertV2EvilTwinEffectorCall(v2_evil);
         Assert.assertEquals(v2_evil.getPolicies().size(), 1, "Wrong number of policies: "+v2_evil.getPolicies());
 
-        Entity v1 = addItemFromCatalog( mgmt.getCatalog().getCatalogItem("v1") );
+        Entity v1 = addItemFromCatalog( mgmt.getCatalog().getCatalogItem("v1", TEST_VERSION) );
         assertV1MethodCall(v1);
         assertV1EffectorCall(v1);
         Assert.assertEquals(v1.getPolicies().size(), 0, "Wrong number of policies: "+v1.getPolicies());

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
index 2adad06..b384d98 100644
--- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
+++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
@@ -36,6 +36,7 @@ import javax.annotation.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.ConfigKey;
@@ -190,7 +191,10 @@ public class BrooklynComponentTemplateResolver {
     public CatalogItem<Entity,EntitySpec<?>> getCatalogItem() {
         String type = getBrooklynType();
         if (type != null) {
-            return loader.getManagementContext().getCatalog().getCatalogItem(Entity.class, type);
+            BrooklynCatalog catalog = loader.getManagementContext().getCatalog();
+            return catalog.getCatalogItem(Entity.class,
+                    CatalogUtils.getIdFromVersionedId(type),
+                    CatalogUtils.getVersionFromVersionedId(type));
         } else {
             return null;
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
index 0025cf8..13c6454 100644
--- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
+++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
@@ -25,6 +25,7 @@ import java.util.Map;
 
 import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.proxying.EntityInitializer;
 import brooklyn.entity.proxying.EntitySpec;
 import brooklyn.management.ManagementContext;
@@ -108,7 +109,7 @@ public abstract class BrooklynEntityDecorationResolver<DT> {
             String policyType = decoLoader.getTypeName().get();
             ManagementContext mgmt = instantiator.loader.getManagementContext();
             BrooklynCatalog catalog = mgmt.getCatalog();
-            CatalogItem<?, ?> item = catalog.getCatalogItem(policyType);
+            CatalogItem<?, ?> item = getPolicyCatalogItem(catalog, policyType);
             PolicySpec<? extends Policy> spec;
             if (item != null) {
                 spec = (PolicySpec<? extends Policy>) catalog.createSpec(item);
@@ -120,6 +121,15 @@ public abstract class BrooklynEntityDecorationResolver<DT> {
             }
             decorations.add(spec);
         }
+        private CatalogItem<?, ?> getPolicyCatalogItem(BrooklynCatalog catalog, String policyType) {
+            String id = CatalogUtils.getIdFromVersionedId(policyType);
+            String version = CatalogUtils.getVersionFromVersionedId(policyType);
+            if (id != null) {
+                return catalog.getCatalogItem(id, version);
+            } else {
+                return catalog.getCatalogItem(policyType);
+            }
+        }
     }
 
     public static class EnricherSpecResolver extends BrooklynEntityDecorationResolver<EnricherSpec<?>> {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/lookup/AbstractTemplateBrooklynLookup.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/lookup/AbstractTemplateBrooklynLookup.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/lookup/AbstractTemplateBrooklynLookup.java
index 8d815ee..c329193 100644
--- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/lookup/AbstractTemplateBrooklynLookup.java
+++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/lookup/AbstractTemplateBrooklynLookup.java
@@ -26,6 +26,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.Entity;
 import brooklyn.entity.proxying.EntitySpec;
 import brooklyn.management.ManagementContext;
@@ -40,7 +41,7 @@ public abstract class AbstractTemplateBrooklynLookup<T extends AbstractResource>
 
     @Override
     public T get(String id) {
-        CatalogItem<?,?> item = bmc.getCatalog().getCatalogItem(id);
+        CatalogItem<?,?> item = getCatalogItem(id);
         if (item==null) {
             log.warn("Could not find item '"+id+"' in Brooklyn catalog; returning null");
             return null;
@@ -48,6 +49,16 @@ public abstract class AbstractTemplateBrooklynLookup<T extends AbstractResource>
         return adapt(item);
     }
 
+    private CatalogItem<?, ?> getCatalogItem(String versionedId) {
+        String id = CatalogUtils.getIdFromVersionedId(versionedId);
+        String version = CatalogUtils.getVersionFromVersionedId(versionedId);
+        if (id != null) {
+            return bmc.getCatalog().getCatalogItem(id, version);
+        } else {
+            return bmc.getCatalog().getCatalogItem(id);
+        }
+    }
+
     public abstract T adapt(CatalogItem<?,?> item);
 
     protected ResolvableLink<T> newLink(CatalogItem<? extends Entity,EntitySpec<?>> li) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
index d9186a3..61e3e4f 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
@@ -44,6 +44,7 @@ import com.google.common.base.Joiner;
 public abstract class AbstractYamlTest {
 
     private static final Logger LOG = LoggerFactory.getLogger(AbstractYamlTest.class);
+    protected static final String TEST_VERSION = "0.1.2";
 
     private ManagementContext brooklynMgmt;
     protected BrooklynCampPlatform platform;
@@ -137,9 +138,9 @@ public abstract class AbstractYamlTest {
     }
 
     protected void deleteCatalogEntity(String catalogItem) {
-        mgmt().getCatalog().deleteCatalogItem(catalogItem);
+        mgmt().getCatalog().deleteCatalogItem(catalogItem, TEST_VERSION);
     }
-    
+
     protected Logger getLogger() {
         return LOG;
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
index 83cd46d..dfbc96b 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
@@ -28,6 +28,7 @@ import java.util.Collection;
 import org.testng.annotations.Test;
 
 import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.BasicEntity;
 import brooklyn.management.osgi.OsgiStandaloneTest;
@@ -44,30 +45,62 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
     public void testAddCatalogItem() throws Exception {
         String registeredTypeName = "my.catalog.app.id.load";
         addCatalogOSGiEntity(registeredTypeName);
-
-        CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(registeredTypeName);
+        CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(registeredTypeName, TEST_VERSION);
         assertEquals(item.getRegisteredTypeName(), registeredTypeName);
 
         deleteCatalogEntity(registeredTypeName);
     }
 
     @Test
+    public void testAddCatalogItemWithoutVersionFail() throws Exception {
+        try {
+            addCatalogItem(
+                "brooklyn.catalog:",
+                "  name: My Catalog App",
+                "services:",
+                "- type: " + SIMPLE_ENTITY_TYPE);
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertEquals(e.getMessage(), "'version' attribute missing in 'brooklyn.catalog' section.");
+        }
+    }
+
+    @Test
     public void testLaunchApplicationReferencingCatalog() throws Exception {
         String registeredTypeName = "my.catalog.app.id.launch";
         registerAndLaunchAndAssertSimpleEntity(registeredTypeName, SIMPLE_ENTITY_TYPE);
     }
 
     @Test
+    public void testLaunchApplicationReferencingUnversionedCatalogFail() throws Exception {
+        String registeredTypeName = "my.catalog.app.id.fail";
+        addCatalogOSGiEntity(registeredTypeName, SIMPLE_ENTITY_TYPE);
+        try {
+            String yaml = "name: simple-app-yaml\n" +
+                          "location: localhost\n" +
+                          "services: \n" +
+                          "  - serviceType: " + registeredTypeName;
+            try {
+                createAndStartApplication(yaml);
+            } catch (UnsupportedOperationException e) {
+                assertTrue(e.getMessage().endsWith("cannot be matched"));
+            }
+        } finally {
+            deleteCatalogEntity(registeredTypeName);
+        }
+    }
+
+    @Test
     public void testLaunchApplicationWithCatalogReferencingOtherCatalog() throws Exception {
         String referencedRegisteredTypeName = "my.catalog.app.id.referenced";
         String referrerRegisteredTypeName = "my.catalog.app.id.referring";
         addCatalogOSGiEntity(referencedRegisteredTypeName, SIMPLE_ENTITY_TYPE);
-        addCatalogOSGiEntity(referrerRegisteredTypeName, referencedRegisteredTypeName);
+        addCatalogOSGiEntity(referrerRegisteredTypeName, ver(referencedRegisteredTypeName));
 
         String yaml = "name: simple-app-yaml\n" +
                       "location: localhost\n" +
                       "services: \n" +
-                      "  - serviceType: "+referrerRegisteredTypeName;
+                      "  - serviceType: " + ver(referrerRegisteredTypeName);
         Entity app = createAndStartApplication(yaml);
 
         Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
@@ -159,7 +192,7 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
             "  name: My Catalog App",
             "  description: My description",
             "  icon_url: classpath://path/to/myicon.jpg",
-            "  version: 0.1.2",
+            "  version: " + TEST_VERSION,
             "  libraries:",
             "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
             "",
@@ -184,4 +217,7 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
             "  - type: " + serviceType);
     }
 
+    private String ver(String id) {
+        return id + CatalogUtils.VERSION_DELIMITER + TEST_VERSION;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7f599246/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
index 0d5c44d..e704ea1 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
@@ -43,7 +43,7 @@ public class CatalogYamlPolicyTest extends AbstractYamlTest {
         String registeredTypeName = "my.catalog.policy.id.load";
         addCatalogOSGiPolicy(registeredTypeName, SIMPLE_POLICY_TYPE);
 
-        CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(registeredTypeName);
+        CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(registeredTypeName, TEST_VERSION);
         assertEquals(item.getRegisteredTypeName(), registeredTypeName);
         assertEquals(countCatalogPolicies(), 1);
 
@@ -117,7 +117,7 @@ public class CatalogYamlPolicyTest extends AbstractYamlTest {
             "  name: My Catalog Policy",
             "  description: My description",
             "  icon_url: classpath://path/to/myicon.jpg",
-            "  version: 0.1.2",
+            "  version: " + TEST_VERSION,
             "  libraries:",
             "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
             "",


[10/18] incubator-brooklyn git commit: Catalog versioning - when no explicit version use the highest version (preferring non-snapshot)

Posted by he...@apache.org.
Catalog versioning - when no explicit version use the highest version (preferring non-snapshot)


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

Branch: refs/heads/master
Commit: 1a9d0e30972c717d30d9bae94613bf8197ab82ba
Parents: 415619a
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Mon Nov 10 19:39:35 2014 +0200
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 11:49:52 2014 +0200

----------------------------------------------------------------------
 .../catalog/internal/BasicBrooklynCatalog.java  |  2 +-
 .../catalog/internal/CatalogItemComparator.java | 33 ++++++++++++++++++++
 .../internal/CatalogItemVersionComparator.java  | 16 ----------
 .../src/main/webapp/assets/js/view/catalog.js   |  6 ++--
 .../rest/resources/CatalogResource.java         | 13 +++++---
 5 files changed, 44 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1a9d0e30/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
index f74e24c..ed85e76 100644
--- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
@@ -193,7 +193,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
 
     private String getDefaultVersion(String symbolicName) {
         Iterable<CatalogItem<Object, Object>> versions = getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName)));
-        ImmutableSortedSet<CatalogItem<?, ?>> orderedVersions = ImmutableSortedSet.orderedBy(new CatalogItemVersionComparator()).addAll(versions).build();
+        ImmutableSortedSet<CatalogItem<?, ?>> orderedVersions = ImmutableSortedSet.orderedBy(new CatalogItemComparator()).addAll(versions).build();
         if (!orderedVersions.isEmpty()) {
             return orderedVersions.iterator().next().getVersion();
         } else {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1a9d0e30/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
new file mode 100644
index 0000000..f5523e2
--- /dev/null
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
@@ -0,0 +1,33 @@
+package brooklyn.catalog.internal;
+
+import java.util.Comparator;
+
+import brooklyn.catalog.CatalogItem;
+import brooklyn.util.text.NaturalOrderComparator;
+
+public class CatalogItemComparator implements Comparator<CatalogItem<?, ?>> {
+    private static final String SNAPSHOT = "SNAPSHOT";
+    private static final Comparator<String> COMPARATOR = new NaturalOrderComparator();
+
+    public static final CatalogItemComparator INSTANCE = new CatalogItemComparator();
+
+    @Override
+    public int compare(CatalogItem<?, ?> o1, CatalogItem<?, ?> o2) {
+        int symbolicNameComparison = o1.getSymbolicName().compareTo(o2.getSymbolicName());
+        if (symbolicNameComparison != 0) {
+            return symbolicNameComparison;
+        } else {
+            String v1 = o1.getVersion().toUpperCase();
+            String v2 = o2.getVersion().toUpperCase();
+            boolean isV1Snapshot = v1.contains(SNAPSHOT);
+            boolean isV2Snapshot = v2.contains(SNAPSHOT);
+            if (isV1Snapshot == isV2Snapshot) {
+                return -COMPARATOR.compare(v1, v2);
+            } else if (isV1Snapshot) {
+                return 1;
+            } else {
+                return -1;
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1a9d0e30/core/src/main/java/brooklyn/catalog/internal/CatalogItemVersionComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemVersionComparator.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemVersionComparator.java
deleted file mode 100644
index 3e279a4..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemVersionComparator.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package brooklyn.catalog.internal;
-
-import java.util.Comparator;
-
-import brooklyn.catalog.CatalogItem;
-
-public class CatalogItemVersionComparator implements Comparator<CatalogItem<?, ?>> {
-
-    protected CatalogItemVersionComparator() {}
-
-    @Override
-    public int compare(CatalogItem<?, ?> o1, CatalogItem<?, ?> o2) {
-        return -o1.getVersion().compareTo(o2.getVersion());
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1a9d0e30/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/catalog.js b/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
index cf14684..2d6aa8b 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
@@ -319,7 +319,6 @@ define([
                     isChild: isChild,
                     extraClasses: (activeDetailsView == this.name && model.cid == this.activeCid) ? "active" : ""
                 }, this.entryTemplateArgs(model));
-            console.log(args);
             return this.template(args);
         },
 
@@ -376,9 +375,8 @@ define([
             var orderedIds = _.uniq(this.collection.map(symbolicNameFn), true);
 
             function getLatestStableVersion(items) {
-                //TODO implement more sophisticated "default" version selection
-                //Could let the server choose it
-                return items[items.length-1];
+                //the server sorts items by descending version, snapshots at the back
+                return items[0];
             }
 
             var catalogTree = orderedIds.map(function(symbolicName) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1a9d0e30/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
index 188825f..ad1324d 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
@@ -39,6 +39,7 @@ import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.CatalogPredicates;
 import brooklyn.catalog.internal.BasicBrooklynCatalog;
 import brooklyn.catalog.internal.CatalogDto;
+import brooklyn.catalog.internal.CatalogItemComparator;
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.Entity;
 import brooklyn.entity.proxying.EntitySpec;
@@ -48,7 +49,6 @@ import brooklyn.rest.api.CatalogApi;
 import brooklyn.rest.domain.ApiError;
 import brooklyn.rest.domain.CatalogEntitySummary;
 import brooklyn.rest.domain.CatalogItemSummary;
-import brooklyn.rest.domain.SummaryComparators;
 import brooklyn.rest.transform.CatalogTransformer;
 import brooklyn.rest.util.WebResourceUtils;
 import brooklyn.util.ResourceUtils;
@@ -62,6 +62,8 @@ import com.google.common.base.Function;
 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.collect.Lists;
 import com.google.common.io.Files;
 import com.sun.jersey.core.header.FormDataContentDisposition;
 
@@ -236,10 +238,11 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
         if (Strings.isNonEmpty(fragment))
             filters.add(CatalogPredicates.xml(StringPredicates.containsLiteralIgnoreCase(fragment)));
 
-        return FluentIterable.from(brooklyn().getCatalog().getCatalogItems())
-                .filter(Predicates.and(filters))
-                .transform(TO_CATALOG_ITEM_SUMMARY)
-                .toSortedList(SummaryComparators.displayNameComparator());
+        ImmutableList<CatalogItem<Object, Object>> sortedItems =
+                FluentIterable.from(brooklyn().getCatalog().getCatalogItems())
+                    .filter(Predicates.and(filters))
+                    .toSortedList(CatalogItemComparator.INSTANCE);
+        return Lists.transform(sortedItems, TO_CATALOG_ITEM_SUMMARY);
     }
 
     @Override


[11/18] incubator-brooklyn git commit: Catalog versioning - fix failing tests

Posted by he...@apache.org.
Catalog versioning - fix failing tests


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

Branch: refs/heads/master
Commit: 9c6539f0adf4affa41de0d77efaba340b11a4ee2
Parents: 1a9d0e3
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Tue Nov 11 22:32:23 2014 +0200
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 11:49:53 2014 +0200

----------------------------------------------------------------------
 .../catalog/internal/CatalogItemComparator.java | 18 ++++++++++
 .../brooklyn/catalog/internal/CatalogUtils.java |  1 +
 .../brooklyn/management/ha/OsgiManager.java     | 24 ++++++++++---
 .../src/main/java/brooklyn/util/osgi/Osgis.java |  1 +
 .../osgi/OsgiVersionMoreEntityTest.java         | 18 ++++++----
 .../brooklyn/catalog/CatalogYamlEntityTest.java | 37 +++++++++-----------
 6 files changed, 69 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9c6539f0/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
index f5523e2..4ac03f9 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
@@ -1,3 +1,21 @@
+/*
+ * 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 brooklyn.catalog.internal;
 
 import java.util.Comparator;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9c6539f0/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
index 849075f..24bfe47 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
@@ -174,6 +174,7 @@ public class CatalogUtils {
     //surface minimal. Could instead have the interface methods accept VerionedId object and have the helpers
     //construct it as needed.
     public static CatalogItem<?, ?> getCatalogItemOptionalVersion(ManagementContext mgmt, String versionedId) {
+        if (versionedId == null) return null;
         if (looksLikeVersionedId(versionedId)) {
             String id = getIdFromVersionedId(versionedId);
             String version = getVersionFromVersionedId(versionedId);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9c6539f0/core/src/main/java/brooklyn/management/ha/OsgiManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ha/OsgiManager.java b/core/src/main/java/brooklyn/management/ha/OsgiManager.java
index 47b0225..c3a2faa 100644
--- a/core/src/main/java/brooklyn/management/ha/OsgiManager.java
+++ b/core/src/main/java/brooklyn/management/ha/OsgiManager.java
@@ -109,11 +109,19 @@ public class OsgiManager {
     }
 
     private void checkCorrectlyInstalled(CatalogBundle bundle, Bundle b) {
+        String nv = b.getSymbolicName()+":"+b.getVersion().toString();
+
+        if (bundle.isNamed() &&
+                (!bundle.getName().equals(b.getSymbolicName()) ||
+                !bundle.getVersion().equals(b.getVersion().toString()))) {
+            log.warn("Bundle at " + bundle.getUrl() + " installed as " + nv +
+                    " but user explicitly requested " + bundle.getName() + ":" + bundle.getVersion());
+        }
+
         List<Bundle> matches = Osgis.bundleFinder(framework)
                 .symbolicName(b.getSymbolicName())
                 .version(b.getVersion().toString())
                 .findAll();
-        String nv = b.getSymbolicName()+":"+b.getVersion().toString();
         if (matches.isEmpty()) {
             log.error("OSGi could not find bundle "+nv+" in search after installing it from "+bundle.getUrl());
         } else if (matches.size()==1) {
@@ -211,11 +219,19 @@ public class OsgiManager {
     }
 
     public Maybe<Bundle> findBundle(CatalogBundle catalogBundle) {
+        //Either fail at install time when the user supplied name:version is different
+        //from the one reported from the bundle
+        //or
+        //Ignore user supplied name:version when URL is supplied to be able to find the
+        //bundle even if it's with a different version.
+        //
+        //For now we just log a warning if there's a version discrepancy at install time,
+        //so prefer URL if supplied.
         BundleFinder bundleFinder = Osgis.bundleFinder(framework);
-        if (catalogBundle.isNamed()) {
-            bundleFinder.symbolicName(catalogBundle.getName()).version(catalogBundle.getVersion());
-        } else {
+        if (catalogBundle.getUrl() != null) {
             bundleFinder.requiringFromUrl(catalogBundle.getUrl());
+        } else {
+            bundleFinder.symbolicName(catalogBundle.getName()).version(catalogBundle.getVersion());
         }
         return bundleFinder.find();
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9c6539f0/core/src/main/java/brooklyn/util/osgi/Osgis.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/osgi/Osgis.java b/core/src/main/java/brooklyn/util/osgi/Osgis.java
index 98720a6..dfcea83 100644
--- a/core/src/main/java/brooklyn/util/osgi/Osgis.java
+++ b/core/src/main/java/brooklyn/util/osgi/Osgis.java
@@ -229,6 +229,7 @@ public class Osgis {
                     boolean matches = url.equals(b.getLocation());
                     if (urlMandatory) {
                         if (!matches) continue;
+                        else urlMatched = true;
                     } else {
                         if (matches) {
                             if (!urlMatched) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9c6539f0/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java b/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java
index 84f181b..fd6baef 100644
--- a/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java
+++ b/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java
@@ -345,12 +345,18 @@ public class OsgiVersionMoreEntityTest {
 
     @Test
     public void testUnfazedByMoreEntitiesV1AndV2AndV2EvilTwin() throws Exception {
-        addCatalogItemWithNameAndType("v1", OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-            BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
-        addCatalogItemWithNameAndType("v2", OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-            BROOKLYN_TEST_MORE_ENTITIES_V2_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
-        addCatalogItemWithNameAndType("v2-evil", OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
-            BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
+        addCatalogItemWithNameAndType("v1",
+                TEST_VERSION,
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                BROOKLYN_TEST_MORE_ENTITIES_V1_URL);
+        addCatalogItemWithNameAndType("v2",
+                TEST_VERSION,
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                BROOKLYN_TEST_MORE_ENTITIES_V2_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
+        addCatalogItemWithNameAndType("v2-evil", 
+                TEST_VERSION,
+                OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY,
+                BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL, BROOKLYN_TEST_OSGI_ENTITIES_URL);
 
         // test osgi finding
         

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9c6539f0/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
index ab6c7dd..c0083de 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
@@ -53,17 +53,18 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
     }
 
     @Test
-    public void testAddCatalogItemWithoutVersionFail() throws Exception {
-        try {
-            addCatalogItem(
-                "brooklyn.catalog:",
-                "  name: My Catalog App",
-                "services:",
-                "- type: " + SIMPLE_ENTITY_TYPE);
-            fail();
-        } catch (IllegalArgumentException e) {
-            assertEquals(e.getMessage(), "'version' attribute missing in 'brooklyn.catalog' section.");
-        }
+    public void testAddCatalogItemWithoutVersion() throws Exception {
+        String id = "unversioned.app";
+        addCatalogItem(
+            "brooklyn.catalog:",
+            "  name: " + id,
+            "  libraries:",
+            "  - " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
+            "services:",
+            "- type: " + SIMPLE_ENTITY_TYPE);
+        CatalogItem<?, ?> catalogItem = mgmt().getCatalog().getCatalogItem(id, BrooklynCatalog.DEFAULT_VERSION);
+        assertEquals(catalogItem.getVersion(), "0.0.0_SNAPSHOT");
+        mgmt().getCatalog().deleteCatalogItem(id, "0.0.0_SNAPSHOT");
     }
 
     @Test
@@ -73,19 +74,15 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
     }
 
     @Test
-    public void testLaunchApplicationReferencingUnversionedCatalogFail() throws Exception {
+    public void testLaunchApplicationUnverionedCatalogReference() throws Exception {
         String symbolicName = "my.catalog.app.id.fail";
         addCatalogOSGiEntity(symbolicName, SIMPLE_ENTITY_TYPE);
         try {
             String yaml = "name: simple-app-yaml\n" +
                           "location: localhost\n" +
                           "services: \n" +
-                          "  - serviceType: " + ver(symbolicName);
-            try {
-                createAndStartApplication(yaml);
-            } catch (UnsupportedOperationException e) {
-                assertTrue(e.getMessage().endsWith("cannot be matched"));
-            }
+                          "  - serviceType: " + symbolicName;
+            createAndStartApplication(yaml);
         } finally {
             deleteCatalogEntity(symbolicName);
         }
@@ -214,7 +211,7 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
                 "- type: " + SIMPLE_ENTITY_TYPE);
             fail();
         } catch (IllegalStateException e) {
-            Assert.assertEquals(e.getMessage(), "Bundle CatalogBundleDto{name=" + nonExistentId + ", version=" + nonExistentVersion + ", url=null} not already registered by name:version, but URL is empty.");
+            Assert.assertEquals(e.getMessage(), "Bundle CatalogBundleDto{name=" + nonExistentId + ", version=" + nonExistentVersion + ", url=null} not previously registered, but URL is empty.");
         }
     }
 
@@ -304,7 +301,7 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
             fail();
         } catch (IllegalStateException e) {
             assertEquals(e.getMessage(), "Bundle CatalogBundleDto{name=" + nonExistentId + ", version=" + nonExistentVersion + ", url=null} " +
-                    "not already registered by name:version, but URL is empty.");
+                    "not previously registered, but URL is empty.");
         }
     }
     


[05/18] incubator-brooklyn git commit: Catalog versioning - Reference installed OSGi bundles by name+version

Posted by he...@apache.org.
Catalog versioning - Reference installed OSGi bundles by name+version

Also fix failing tests from previous commits.


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

Branch: refs/heads/master
Commit: 268aaf6bc85bcdf04496ca17d3aaa3a8b199dc37
Parents: 1e88783
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Mon Jul 21 19:08:02 2014 +0300
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 11:49:49 2014 +0200

----------------------------------------------------------------------
 .../main/java/brooklyn/catalog/CatalogItem.java |  13 +-
 .../catalog/internal/CatalogBundleDto.java      |  66 ++++++++
 .../catalog/internal/CatalogLibrariesDo.java    |   5 +-
 .../catalog/internal/CatalogLibrariesDto.java   |  62 ++++++--
 .../brooklyn/catalog/internal/CatalogUtils.java |  17 +-
 .../catalog/internal/CatalogXmlSerializer.java  |   6 +-
 .../OsgiBrooklynClassLoadingContext.java        |   8 +-
 .../brooklyn/management/ha/OsgiManager.java     | 131 ++++++++++------
 .../src/main/java/brooklyn/util/osgi/Osgis.java |  65 ++++++--
 .../brooklyn/camp/lite/CampYamlLiteTest.java    |  19 ++-
 .../catalog/internal/CatalogDtoTest.java        |  15 +-
 .../catalog/internal/CatalogLoadTest.java       |  21 ++-
 .../management/osgi/OsgiStandaloneTest.java     |   2 +
 .../camp/lite/test-app-service-blueprint.yaml   |   2 +-
 .../brooklyn/catalog/internal/osgi-catalog.xml  |   4 +-
 .../camp/brooklyn/AbstractYamlTest.java         |   6 +-
 .../camp/brooklyn/ReferencedYamlTest.java       |   7 +-
 .../CatalogOsgiVersionMoreEntityTest.java       |  14 +-
 .../brooklyn/catalog/CatalogYamlEntityTest.java | 157 +++++++++++++++++--
 .../brooklyn/catalog/CatalogYamlPolicyTest.java |   8 +-
 ...more-entity-v1-with-policy-osgi-catalog.yaml |   2 +-
 .../catalog/more-entity-v2-osgi-catalog.yaml    |   2 +-
 usage/camp/src/test/resources/yaml-ref-app.yaml |   2 +-
 .../src/test/resources/yaml-ref-catalog.yaml    |   2 +-
 .../src/test/resources/yaml-ref-entity.yaml     |   2 +-
 .../rest/resources/CatalogResourceTest.java     |  18 ++-
 26 files changed, 510 insertions(+), 146 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/api/src/main/java/brooklyn/catalog/CatalogItem.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/catalog/CatalogItem.java b/api/src/main/java/brooklyn/catalog/CatalogItem.java
index 409bd7e..1f67ccd 100644
--- a/api/src/main/java/brooklyn/catalog/CatalogItem.java
+++ b/api/src/main/java/brooklyn/catalog/CatalogItem.java
@@ -18,7 +18,7 @@
  */
 package brooklyn.catalog;
 
-import java.util.List;
+import java.util.Collection;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -36,10 +36,19 @@ public interface CatalogItem<T,SpecT> extends BrooklynObject, Rebindable {
     public static enum CatalogItemType {
         TEMPLATE, ENTITY, POLICY, CONFIGURATION
     }
+    
+    public static interface CatalogBundle {
+        public String getName();
+        public String getVersion();
+        public String getUrl();
+
+        /** @return true if the bundle reference contains both name and version*/
+        public boolean isNamed();
+    }
 
     @Beta
     public static interface CatalogItemLibraries {
-        List<String> getBundles();
+        Collection<CatalogBundle> getBundles();
     }
 
     public CatalogItemType getCatalogItemType();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
new file mode 100644
index 0000000..6ef148e
--- /dev/null
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
@@ -0,0 +1,66 @@
+/*
+ * 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 brooklyn.catalog.internal;
+
+import com.google.common.base.Objects;
+
+import brooklyn.catalog.CatalogItem.CatalogBundle;
+
+public class CatalogBundleDto implements CatalogBundle {
+    private String name;
+    private String version;
+    private String url;
+
+    public CatalogBundleDto() {}
+
+    public CatalogBundleDto(String name, String version, String url) {
+        this.name = name;
+        this.version = version;
+        this.url = url;
+    }
+
+    @Override
+    public boolean isNamed() {
+        return name != null && version != null;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String getVersion() {
+        return version;
+    }
+
+    @Override
+    public String getUrl() {
+        return url;
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("name", name)
+                .add("version", version)
+                .add("url", url)
+                .toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java
index fb461f3..fa5f478 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java
@@ -18,9 +18,10 @@
  */
 package brooklyn.catalog.internal;
 
-import java.util.List;
+import java.util.Collection;
 
 import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.CatalogItem.CatalogBundle;
 
 import com.google.common.base.Preconditions;
 
@@ -34,7 +35,7 @@ public class CatalogLibrariesDo implements CatalogItem.CatalogItemLibraries {
     }
 
     @Override
-    public List<String> getBundles() {
+    public Collection<CatalogBundle> getBundles() {
         return librariesDto.getBundles();
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java
index bcbd25b..646d69c 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java
@@ -20,34 +20,41 @@ package brooklyn.catalog.internal;
 
 import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.CatalogItem.CatalogBundle;
+
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 
-import brooklyn.catalog.CatalogItem;
-
 public class CatalogLibrariesDto implements CatalogItem.CatalogItemLibraries {
 
     private static Logger LOG = LoggerFactory.getLogger(CatalogLibrariesDto.class);
 
-    // TODO: Incorporate name and version into entries
-    private List<String> bundles = new CopyOnWriteArrayList<String>();
+    private Collection<CatalogBundle> bundles = new CopyOnWriteArrayList<CatalogBundle>();
 
-    public void addBundle(String url) {
+    public void addBundle(String name, String version, String url) {
         Preconditions.checkNotNull(bundles, "Cannot add a bundle to a deserialized DTO");
-        bundles.add(Preconditions.checkNotNull(url, "url"));
+        if (name == null && version == null) {
+            Preconditions.checkNotNull(url, "url");
+        } else {
+            Preconditions.checkNotNull(name, "name");
+            Preconditions.checkNotNull(version, "version");
+        }
+        
+        bundles.add(new CatalogBundleDto(name, version, url));
     }
 
     /**
      * @return An immutable copy of the bundle URLs referenced by this object
      */
-    public List<String> getBundles() {
+    @Override
+    public Collection<CatalogBundle> getBundles() {
         if (bundles == null) {
             // can be null on deserialization
             return Collections.emptyList();
@@ -63,15 +70,37 @@ public class CatalogLibrariesDto implements CatalogItem.CatalogItemLibraries {
         CatalogLibrariesDto dto = new CatalogLibrariesDto();
         for (Object object : possibleLibraries) {
             if (object instanceof Map) {
-                @SuppressWarnings("rawtypes")
-                Map entry = (Map) object;
-                // these might be useful in the future
-//                String name = stringValOrNull(entry, "name");
-//                String version = stringValOrNull(entry, "version");
+                Map<?, ?> entry = (Map<?, ?>) object;
+                String name = stringValOrNull(entry, "name");
+                String version = stringValOrNull(entry, "version");
                 String url = stringValOrNull(entry, "url");
-                dto.addBundle(url);
+                dto.addBundle(name, version, url);
             } else if (object instanceof String) {
-                dto.addBundle((String) object);
+                String inlineRef = (String) object;
+
+                final String name;
+                final String version;
+                final String url;
+
+                //Infer reference type (heuristically)
+                if (inlineRef.contains("/") || inlineRef.contains("\\")) {
+                    //looks like an url/file path
+                    name = null;
+                    version = null;
+                    url = inlineRef;
+                } else if (inlineRef.indexOf(CatalogUtils.VERSION_DELIMITER) != -1) {
+                    //looks like a name+version ref
+                    name = CatalogUtils.getIdFromVersionedId(inlineRef);
+                    version = CatalogUtils.getVersionFromVersionedId(inlineRef);
+                    url = null;
+                } else {
+                    //assume it to be relative url
+                    name = null;
+                    version = null;
+                    url = inlineRef;
+                }
+
+                dto.addBundle(name, version, url);
             } else {
                 LOG.debug("Unexpected entry in libraries list neither string nor map: " + object);
             }
@@ -79,8 +108,7 @@ public class CatalogLibrariesDto implements CatalogItem.CatalogItemLibraries {
         return dto;
     }
 
-    @SuppressWarnings("rawtypes")
-    private static String stringValOrNull(Map map, String key) {
+    private static String stringValOrNull(Map<?, ?> map, String key) {
         Object val = map.get(key);
         return val != null ? String.valueOf(val) : null;
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
index 6f33574..312351f 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
@@ -18,20 +18,17 @@
  */
 package brooklyn.catalog.internal;
 
-import java.util.List;
+import java.util.Collection;
 
 import javax.annotation.Nullable;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.annotations.Beta;
-import com.google.common.base.Joiner;
-import com.google.common.base.Stopwatch;
-
 import brooklyn.basic.BrooklynObject;
 import brooklyn.basic.BrooklynObjectInternal;
 import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.CatalogItem.CatalogBundle;
 import brooklyn.catalog.CatalogItem.CatalogItemLibraries;
 import brooklyn.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
 import brooklyn.config.BrooklynLogging;
@@ -47,6 +44,10 @@ import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.time.Time;
 
+import com.google.common.annotations.Beta;
+import com.google.common.base.Joiner;
+import com.google.common.base.Stopwatch;
+
 public class CatalogUtils {
     private static final Logger log = LoggerFactory.getLogger(CatalogUtils.class);
 
@@ -66,7 +67,7 @@ public class CatalogUtils {
         BrooklynClassLoadingContextSequential result = new BrooklynClassLoadingContextSequential(mgmt);
 
         if (libraries!=null) {
-            List<String> bundles = libraries.getBundles();
+            Collection<CatalogBundle> bundles = libraries.getBundles();
             if (bundles!=null && !bundles.isEmpty()) {
                 result.add(new OsgiBrooklynClassLoadingContext(mgmt, catalogItemId, bundles));
             }
@@ -88,7 +89,7 @@ public class CatalogUtils {
         if (libraries == null) return;
 
         ManagementContextInternal mgmt = (ManagementContextInternal) managementContext;
-        List<String> bundles = libraries.getBundles();
+        Collection<CatalogBundle> bundles = libraries.getBundles();
         if (!bundles.isEmpty()) {
             Maybe<OsgiManager> osgi = mgmt.getOsgiManager();
             if (osgi.isAbsent()) {
@@ -99,7 +100,7 @@ public class CatalogUtils {
                     "Loading bundles in {}: {}", 
                     new Object[] {managementContext, Joiner.on(", ").join(bundles)});
             Stopwatch timer = Stopwatch.createStarted();
-            for (String bundleUrl : bundles) {
+            for (CatalogBundle bundleUrl : bundles) {
                 osgi.get().registerBundle(bundleUrl);
             }
             if (log.isDebugEnabled()) 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java b/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java
index ccac60b..0948149 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java
@@ -18,6 +18,8 @@
  */
 package brooklyn.catalog.internal;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
@@ -29,6 +31,8 @@ import brooklyn.util.xstream.XmlSerializer;
 public class CatalogXmlSerializer extends XmlSerializer<Object> {
 
     public CatalogXmlSerializer() {
+        xstream.addDefaultImplementation(ArrayList.class, Collection.class);
+        
         xstream.aliasType("list", List.class);
         xstream.aliasType("map", Map.class);
 
@@ -54,7 +58,7 @@ public class CatalogXmlSerializer extends XmlSerializer<Object> {
         xstream.registerConverter(new EnumCaseForgivingSingleValueConverter(CatalogScanningModes.class));
 
         xstream.aliasType("libraries", CatalogLibrariesDto.class);
-        xstream.addImplicitCollection(CatalogLibrariesDto.class, "bundles", "bundle", String.class);
+        xstream.addImplicitCollection(CatalogLibrariesDto.class, "bundles", "bundle", CatalogBundleDto.class);
 
         // Note: the management context is being omitted because it is unnecessary for
         // representations of catalogues generated with this serializer.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java b/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java
index 74cc6cf..bb763d5 100644
--- a/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java
+++ b/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java
@@ -19,22 +19,22 @@
 package brooklyn.management.classloading;
 
 import java.net.URL;
-import java.util.List;
+import java.util.Collection;
 
+import brooklyn.catalog.CatalogItem.CatalogBundle;
 import brooklyn.management.ManagementContext;
 import brooklyn.management.ha.OsgiManager;
 import brooklyn.management.internal.ManagementContextInternal;
-import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
 
 import com.google.common.base.Objects;
 
 public class OsgiBrooklynClassLoadingContext extends AbstractBrooklynClassLoadingContext {
 
-    private final List<String> bundles;
+    private final Collection<CatalogBundle> bundles;
     private final String catalogItemId;
 
-    public OsgiBrooklynClassLoadingContext(ManagementContext mgmt, String catalogItemId, List<String> bundles) {
+    public OsgiBrooklynClassLoadingContext(ManagementContext mgmt, String catalogItemId, Collection<CatalogBundle> bundles) {
         super(mgmt);
         this.bundles = bundles;
         this.catalogItemId = catalogItemId;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/core/src/main/java/brooklyn/management/ha/OsgiManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ha/OsgiManager.java b/core/src/main/java/brooklyn/management/ha/OsgiManager.java
index 44b30af..47b0225 100644
--- a/core/src/main/java/brooklyn/management/ha/OsgiManager.java
+++ b/core/src/main/java/brooklyn/management/ha/OsgiManager.java
@@ -30,6 +30,7 @@ import org.osgi.framework.launch.Framework;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import brooklyn.catalog.CatalogItem.CatalogBundle;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.ConfigKey;
 import brooklyn.util.collections.MutableMap;
@@ -38,6 +39,8 @@ import brooklyn.util.guava.Maybe;
 import brooklyn.util.os.Os;
 import brooklyn.util.os.Os.DeletionResult;
 import brooklyn.util.osgi.Osgis;
+import brooklyn.util.osgi.Osgis.BundleFinder;
+import brooklyn.util.osgi.Osgis.VersionedName;
 
 import com.google.common.base.Throwables;
 import com.google.common.collect.Iterables;
@@ -52,10 +55,10 @@ public class OsgiManager {
     
     protected Framework framework;
     protected File osgiTempDir;
-    
+
     // we could manage without this map but it is useful to validate what is a user-supplied url
-    protected Map<String,String> urlToBundleIdentifier = MutableMap.of();
-    
+    protected Map<String,VersionedName> urlToBundleIdentifier = MutableMap.of();
+
     public void start() {
         try {
             // TODO any extra startup args?
@@ -88,49 +91,83 @@ public class OsgiManager {
         framework = null;
     }
 
-    public void registerBundle(String bundleUrl) {
+    public synchronized void registerBundle(CatalogBundle bundle) {
         try {
-            String nv = urlToBundleIdentifier.get(bundleUrl);
+            if (checkBundleInstalledThrowIfInconsistent(bundle)) {
+                return;
+            }
+
+            Bundle b = Osgis.install(framework, bundle.getUrl());
+
+            checkCorrectlyInstalled(bundle, b);
+
+            urlToBundleIdentifier.put(bundle.getUrl(), new VersionedName(b));
+        } catch (BundleException e) {
+            log.debug("Bundle from "+bundle+" failed to install (rethrowing): "+e);
+            throw Throwables.propagate(e);
+        }
+    }
+
+    private void checkCorrectlyInstalled(CatalogBundle bundle, Bundle b) {
+        List<Bundle> matches = Osgis.bundleFinder(framework)
+                .symbolicName(b.getSymbolicName())
+                .version(b.getVersion().toString())
+                .findAll();
+        String nv = b.getSymbolicName()+":"+b.getVersion().toString();
+        if (matches.isEmpty()) {
+            log.error("OSGi could not find bundle "+nv+" in search after installing it from "+bundle.getUrl());
+        } else if (matches.size()==1) {
+            log.debug("Bundle from "+bundle.getUrl()+" successfully installed as " + nv + " ("+b+")");
+        } else {
+            log.warn("OSGi has multiple bundles matching "+nv+", when just installed from "+bundle.getUrl()+": "+matches+"; "
+                + "brooklyn will prefer the URL-based bundle for top-level references but any dependencies or "
+                + "import-packages will be at the mercy of OSGi. "
+                + "It is recommended to use distinct versions for different bundles, and the same URL for the same bundles.");
+        }
+    }
+
+    private boolean checkBundleInstalledThrowIfInconsistent(CatalogBundle bundle) {
+        String bundleUrl = bundle.getUrl();
+        if (bundleUrl != null) {
+            VersionedName nv = urlToBundleIdentifier.get(bundleUrl);
             if (nv!=null) {
-                if (Osgis.bundleFinder(framework).id(nv).requiringFromUrl(bundleUrl).find().isPresent()) {
+                if (bundle.isNamed() && !nv.equals(bundle.getName(), bundle.getVersion())) {
+                    throw new IllegalStateException("Bundle from "+bundleUrl+" already installed as "+nv+" but user explicitly requested "+bundle);
+                }
+                Maybe<Bundle> installedBundle = Osgis.bundleFinder(framework).requiringFromUrl(bundleUrl).find();
+                if (installedBundle.isPresent()) {
+                    if (bundle.isNamed()) {
+                        Bundle b = installedBundle.get();
+                        if (!nv.equals(b.getSymbolicName(), b.getVersion().toString())) {
+                            log.error("Bundle from "+bundleUrl+" already installed as "+nv+" but reports "+b.getSymbolicName()+":"+b.getVersion());
+                        }
+                    }
                     log.trace("Bundle from "+bundleUrl+" already installed as "+nv+"; not re-registering");
-                    return;
+                    return true;
                 } else {
-                    log.debug("Bundle "+nv+" from "+bundleUrl+" is known in map but not installed; perhaps in the process of installing?");
+                    log.error("Bundle "+nv+" from "+bundleUrl+" is known in map but not installed; perhaps in the process of installing?");
                 }
             }
-            
-            Bundle b = Osgis.install(framework, bundleUrl);
-            nv = b.getSymbolicName()+":"+b.getVersion().toString();
-            
-            List<Bundle> matches = Osgis.bundleFinder(framework).id(nv).findAll();
-            if (matches.isEmpty()) {
-                log.error("OSGi could not find bundle "+nv+" in search after installing it from "+bundleUrl);
-            } else if (matches.size()==1) {
-                log.debug("Bundle from "+bundleUrl+" successfully installed as " + nv + " ("+b+")");
+        } else {
+            Maybe<Bundle> installedBundle = Osgis.bundleFinder(framework).symbolicName(bundle.getName()).version(bundle.getVersion()).find();
+            if (installedBundle.isPresent()) {
+                log.trace("Bundle "+bundle+" installed from "+installedBundle.get().getLocation());
             } else {
-                log.warn("OSGi has multiple bundles matching "+nv+", when just installed from "+bundleUrl+": "+matches+"; "
-                    + "brooklyn will prefer the URL-based bundle for top-level references but any dependencies or "
-                    + "import-packages will be at the mercy of OSGi. "
-                    + "It is recommended to use distinct versions for different bundles, and the same URL for the same bundles.");
+                throw new IllegalStateException("Bundle "+bundle+" not previously registered, but URL is empty.");
             }
-            urlToBundleIdentifier.put(bundleUrl, nv);
-            
-        } catch (BundleException e) {
-            log.debug("Bundle from "+bundleUrl+" failed to install (rethrowing): "+e);
-            throw Throwables.propagate(e);
+            return true;
         }
+        return false;
     }
 
-    public <T> Maybe<Class<T>> tryResolveClass(String type, String... bundleUrlsOrNameVersionString) {
-        return tryResolveClass(type, Arrays.asList(bundleUrlsOrNameVersionString));
+    public <T> Maybe<Class<T>> tryResolveClass(String type, CatalogBundle... catalogBundles) {
+        return tryResolveClass(type, Arrays.asList(catalogBundles));
     }
-    public <T> Maybe<Class<T>> tryResolveClass(String type, Iterable<String> bundleUrlsOrNameVersionString) {
-        Map<String,Throwable> bundleProblems = MutableMap.of();
-        for (String bundleUrlOrNameVersionString: bundleUrlsOrNameVersionString) {
+    public <T> Maybe<Class<T>> tryResolveClass(String type, Iterable<CatalogBundle> catalogBundles) {
+        Map<CatalogBundle,Throwable> bundleProblems = MutableMap.of();
+        for (CatalogBundle catalogBundle: catalogBundles) {
             try {
-                Maybe<Bundle> bundle = findBundle(bundleUrlOrNameVersionString);
-                
+                Maybe<Bundle> bundle = findBundle(catalogBundle);
                 if (bundle.isPresent()) {
                     Bundle b = bundle.get();
                     Class<T> clazz;
@@ -147,17 +184,17 @@ public class OsgiManager {
                     }
                     return Maybe.of(clazz);
                 } else {
-                    bundleProblems.put(bundleUrlOrNameVersionString, ((Maybe.Absent<?>)bundle).getException());
+                    bundleProblems.put(catalogBundle, ((Maybe.Absent<?>)bundle).getException());
                 }
                 
             } catch (Exception e) {
                 // should come from classloading now; name formatting or missing bundle errors will be caught above 
                 Exceptions.propagateIfFatal(e);
-                bundleProblems.put(bundleUrlOrNameVersionString, e);
+                bundleProblems.put(catalogBundle, e);
 
                 Throwable cause = e.getCause();
                 if (cause != null && cause.getMessage().contains("Unresolved constraint in bundle")) {
-                    log.warn("Unresolved constraint resolving OSGi bundle "+bundleUrlOrNameVersionString+" to load "+type+": "+cause.getMessage());
+                    log.warn("Unresolved constraint resolving OSGi bundle "+catalogBundle+" to load "+type+": "+cause.getMessage());
                     if (log.isDebugEnabled()) log.debug("Trace for OSGi resolution failure", e);
                 }
             }
@@ -173,22 +210,20 @@ public class OsgiManager {
         }
     }
 
-    /** finds an installed bundle with the given URL or OSGi identifier ("symbolicName:version" string) */
-    public Maybe<Bundle> findBundle(String bundleUrlOrNameVersionString) {
-        String bundleNameVersion = urlToBundleIdentifier.get(bundleUrlOrNameVersionString);
-        if (bundleNameVersion==null) {
-            Maybe<String[]> nv = Osgis.parseOsgiIdentifier(bundleUrlOrNameVersionString);
-            if (nv.isPresent())
-                bundleNameVersion = bundleUrlOrNameVersionString;
+    public Maybe<Bundle> findBundle(CatalogBundle catalogBundle) {
+        BundleFinder bundleFinder = Osgis.bundleFinder(framework);
+        if (catalogBundle.isNamed()) {
+            bundleFinder.symbolicName(catalogBundle.getName()).version(catalogBundle.getVersion());
+        } else {
+            bundleFinder.requiringFromUrl(catalogBundle.getUrl());
         }
-        Maybe<Bundle> bundle = Osgis.bundleFinder(framework).id(bundleNameVersion).preferringFromUrl(bundleUrlOrNameVersionString).find();
-        return bundle;
+        return bundleFinder.find();
     }
 
-    public URL getResource(String name, Iterable<String> bundleUrlsOrNameVersionString) {
-        for (String bundleUrlOrNameVersionString: bundleUrlsOrNameVersionString) {
+    public URL getResource(String name, Iterable<CatalogBundle> catalogBundles) {
+        for (CatalogBundle catalogBundle: catalogBundles) {
             try {
-                Maybe<Bundle> bundle = findBundle(bundleUrlOrNameVersionString);
+                Maybe<Bundle> bundle = findBundle(catalogBundle);
                 if (bundle.isPresent()) {
                     URL result = bundle.get().getResource(name);
                     if (result!=null) return result;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/core/src/main/java/brooklyn/util/osgi/Osgis.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/osgi/Osgis.java b/core/src/main/java/brooklyn/util/osgi/Osgis.java
index f7508f6..98720a6 100644
--- a/core/src/main/java/brooklyn/util/osgi/Osgis.java
+++ b/core/src/main/java/brooklyn/util/osgi/Osgis.java
@@ -57,6 +57,7 @@ import org.osgi.framework.wiring.BundleCapability;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import brooklyn.catalog.CatalogItem.CatalogBundle;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
@@ -91,6 +92,34 @@ public class Osgis {
     private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF";
     private static final Set<String> SYSTEM_BUNDLES = MutableSet.of();
 
+    public static class VersionedName {
+        private String symbolicName;
+        private Version version;
+        public VersionedName(Bundle b) {
+            this.symbolicName = b.getSymbolicName();
+            this.version = b.getVersion();
+        }
+        public VersionedName(String symbolicName, Version version) {
+            this.symbolicName = symbolicName;
+            this.version = version;
+        }
+        @Override public String toString() {
+            return symbolicName + ":" + Strings.toString(version);
+        }
+        public boolean equals(String sn, String v) {
+            return symbolicName.equals(sn) && (version == null && v == null || version != null && version.toString().equals(v));
+        }
+        public boolean equals(String sn, Version v) {
+            return symbolicName.equals(sn) && (version == null && v == null || version != null && version.equals(v));
+        }
+        protected String getSymbolicName() {
+            return symbolicName;
+        }
+        protected Version getVersion() {
+            return version;
+        }
+    }
+    
     public static class BundleFinder {
         protected final Framework framework;
         protected String symbolicName;
@@ -117,14 +146,29 @@ public class Osgis {
             if (Strings.isBlank(symbolicNameOptionallyWithVersion))
                 return this;
             
-            Maybe<String[]> partsM = parseOsgiIdentifier(symbolicNameOptionallyWithVersion);
-            if (partsM.isAbsent())
+            Maybe<VersionedName> nv = parseOsgiIdentifier(symbolicNameOptionallyWithVersion);
+            if (nv.isAbsent())
                 throw new IllegalArgumentException("Cannot parse symbolic-name:version string '"+symbolicNameOptionallyWithVersion+"'");
-            String[] parts = partsM.get();
-            
-            symbolicName(parts[0]);
-            if (parts.length >= 2) version(parts[1]);
-            
+
+            return id(nv.get());
+        }
+
+        private BundleFinder id(VersionedName nv) {
+            symbolicName(nv.getSymbolicName());
+            if (nv.getVersion() != null) {
+                version(nv.getVersion().toString());
+            }
+            return this;
+        }
+
+        public BundleFinder bundle(CatalogBundle bundle) {
+            if (bundle.isNamed()) {
+                symbolicName(bundle.getName());
+                version(bundle.getVersion());
+            }
+            if (bundle.getUrl() != null) {
+                requiringFromUrl(bundle.getUrl());
+            }
             return this;
         }
 
@@ -567,7 +611,7 @@ public class Osgis {
     /** Takes a string which might be of the form "symbolic-name" or "symbolic-name:version" (or something else entirely)
      * and returns an array of 1 or 2 string items being the symbolic name or symbolic name and version if possible
      * (or returning {@link Maybe#absent()} if not, with a suitable error message). */
-    public static Maybe<String[]> parseOsgiIdentifier(String symbolicNameOptionalWithVersion) {
+    public static Maybe<VersionedName> parseOsgiIdentifier(String symbolicNameOptionalWithVersion) {
         if (Strings.isBlank(symbolicNameOptionalWithVersion))
             return Maybe.absent("OSGi identifier is blank");
         
@@ -575,13 +619,14 @@ public class Osgis {
         if (parts.length>2)
             return Maybe.absent("OSGi identifier has too many parts; max one ':' symbol");
         
+        Version v = null;
         try {
-            Version.parseVersion(parts[1]);
+            v = Version.parseVersion(parts[1]);
         } catch (IllegalArgumentException e) {
             return Maybe.absent("OSGi identifier has invalid version string");
         }
         
-        return Maybe.of(parts);
+        return Maybe.of(new VersionedName(parts[0], v));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
index 9eb56fd..84e3fc5 100644
--- a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
+++ b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
@@ -28,9 +28,9 @@ import io.brooklyn.camp.test.mock.web.MockWebPlatform;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,6 +40,7 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.CatalogItem.CatalogBundle;
 import brooklyn.catalog.CatalogPredicates;
 import brooklyn.catalog.internal.BasicBrooklynCatalog;
 import brooklyn.catalog.internal.CatalogDto;
@@ -58,7 +59,6 @@ import brooklyn.test.entity.LocalManagementContextForTests;
 import brooklyn.test.entity.TestApplication;
 import brooklyn.test.entity.TestEntity;
 import brooklyn.util.ResourceUtils;
-import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.stream.Streams;
@@ -66,7 +66,6 @@ import brooklyn.util.stream.Streams;
 import com.google.common.base.Joiner;
 import com.google.common.base.Predicates;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
 
 /** Tests of lightweight CAMP integration. Since the "real" integration is in brooklyn-camp project,
  * but some aspects of CAMP we want to be able to test here. */
@@ -161,9 +160,11 @@ public class CampYamlLiteTest {
         CatalogItem<Object, Object> retrievedItem = Iterables.getOnlyElement(retrievedItems);
         Assert.assertEquals(retrievedItem, realItem);
 
-        Set<String> expectedBundles = Sets.newHashSet(OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL);
-        Assert.assertEquals(retrievedItem.getLibraries().getBundles(), expectedBundles);
-        // Assert.assertEquals(retrievedItem.getVersion(), "0.9");
+        Collection<CatalogBundle> bundles = retrievedItem.getLibraries().getBundles();
+        Assert.assertEquals(bundles.size(), 1);
+        CatalogBundle bundle = Iterables.getOnlyElement(bundles);
+        Assert.assertEquals(bundle.getUrl(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL);
+        Assert.assertEquals(bundle.getVersion(), "0.1.0");
 
         EntitySpec<?> spec1 = (EntitySpec<?>) mgmt.getCatalog().createSpec(retrievedItem);
         assertNotNull(spec1);
@@ -231,8 +232,10 @@ public class CampYamlLiteTest {
         assertEquals(item.getId(), registeredTypeName);
 
         // and let's check we have libraries
-        List<String> libs = item.getLibraries().getBundles();
-        assertEquals(libs, MutableList.of(bundleUrl));
+        Collection<CatalogBundle> libs = item.getLibraries().getBundles();
+        assertEquals(libs.size(), 1);
+        CatalogBundle bundle = Iterables.getOnlyElement(libs);
+        assertEquals(bundle.getUrl(), bundleUrl);
 
         // now let's check other things on the item
         assertEquals(item.getDisplayName(), "My Catalog App");

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
index 8e6d704..e232116 100644
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
+++ b/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
@@ -91,19 +91,24 @@ public class CatalogDtoTest {
                         "example for what can be expressed, and how", "contents-built-in-test"));
         root.setClasspathScanForEntities(CatalogScanningModes.NONE);
 
+        String bundleUrl = MavenRetriever.localUrl(BrooklynMavenArtifacts.artifact("", "brooklyn-core", "jar", "tests"));
         CatalogDo testEntitiesJavaCatalog = new CatalogDo(
                 managementContext,
                 CatalogDto.newNamedInstance("Test Entities from Java", null, "test-java"));
         testEntitiesJavaCatalog.setClasspathScanForEntities(CatalogScanningModes.NONE);
-        testEntitiesJavaCatalog.addToClasspath(MavenRetriever.localUrl(BrooklynMavenArtifacts.artifact("", "brooklyn-core", "jar", "tests")));
-        testEntitiesJavaCatalog.addEntry(CatalogItemBuilder.newTemplate(TestApplication.class.getCanonicalName(), "Test App from JAR").build());
-        testEntitiesJavaCatalog.addEntry(CatalogItemBuilder.newEntity(TestEntity.class.getCanonicalName(), "Test Entity from JAR").build());
+        testEntitiesJavaCatalog.addToClasspath(bundleUrl);
+        testEntitiesJavaCatalog.addEntry(CatalogItemBuilder.newTemplate(TestApplication.class.getCanonicalName(), "Test App from JAR")
+                .javaType(TestApplication.class.getCanonicalName())
+                .build());
+        testEntitiesJavaCatalog.addEntry(CatalogItemBuilder.newEntity(TestEntity.class.getCanonicalName(), "Test Entity from JAR")
+                .javaType(TestEntity.class.getCanonicalName())
+                .build());
         root.addCatalog(testEntitiesJavaCatalog.dto);
 
         CatalogDo testEntitiesJavaCatalogScanning = new CatalogDo(
                 managementContext,
                 CatalogDto.newNamedInstance("Test Entities from Java Scanning", null, "test-java-scan"));
-        testEntitiesJavaCatalogScanning.addToClasspath(MavenRetriever.localUrl(BrooklynMavenArtifacts.artifact("", "brooklyn-core", "jar", "tests")));
+        testEntitiesJavaCatalogScanning.addToClasspath(bundleUrl);
         testEntitiesJavaCatalogScanning.setClasspathScanForEntities(CatalogScanningModes.ANNOTATIONS);
         root.addCatalog(testEntitiesJavaCatalogScanning.dto);
 
@@ -114,7 +119,7 @@ public class CatalogDtoTest {
         osgiCatalog.setClasspathScanForEntities(CatalogScanningModes.NONE);
         CatalogEntityItemDto osgiEntity = CatalogItemBuilder.newEntity(TestEntity.class.getCanonicalName(), "Test Entity from OSGi").build();
         // NB: this is not actually an OSGi bundle, but it's okay as we don't instantiate the bundles ahead of time (currently)
-        osgiEntity.libraries.addBundle(MavenRetriever.localUrl(BrooklynMavenArtifacts.artifact("", "brooklyn-core", "jar", "tests")));
+        osgiEntity.libraries.addBundle(null, null, bundleUrl);
         testEntitiesJavaCatalog.addEntry(osgiEntity);
         root.addCatalog(osgiCatalog.dto);
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/core/src/test/java/brooklyn/catalog/internal/CatalogLoadTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogLoadTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogLoadTest.java
index e9102c2..976223e 100644
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogLoadTest.java
+++ b/core/src/test/java/brooklyn/catalog/internal/CatalogLoadTest.java
@@ -20,15 +20,16 @@ package brooklyn.catalog.internal;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
 
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import brooklyn.catalog.CatalogItem.CatalogBundle;
+import brooklyn.util.ResourceUtils;
+
 import com.google.common.base.Joiner;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-
-import brooklyn.util.ResourceUtils;
 
 public class CatalogLoadTest {
 
@@ -57,8 +58,18 @@ public class CatalogLoadTest {
         assertEquals(template.getJavaType(), "com.example.ExampleApp");
         assertEquals(template.getLibraries().getBundles().size(), 2,
                 "Template bundles=" + Joiner.on(", ").join(template.getLibraries().getBundles()));
-        assertEquals(Sets.newHashSet(template.getLibraries().getBundles()),
-                Sets.newHashSet("file://path/to/bundle.jar", "http://www.url.com/for/bundle.jar"));
+        
+        boolean foundBundle1 = false, foundBundle2 = false;
+        for (CatalogBundle bundle : template.getLibraries().getBundles()) {
+            if (bundle.getUrl().equals("file://path/to/bundle.jar")) {
+                foundBundle1 = true;
+            }
+            if (bundle.getUrl().equals("http://www.url.com/for/bundle.jar")) {
+                foundBundle2 = true;
+            }
+        }
+        assertTrue(foundBundle1);
+        assertTrue(foundBundle2);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java b/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java
index 94b9e9e..6d2cbc8 100644
--- a/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java
+++ b/core/src/test/java/brooklyn/management/osgi/OsgiStandaloneTest.java
@@ -62,6 +62,8 @@ public class OsgiStandaloneTest {
 
     public static final String BROOKLYN_TEST_OSGI_ENTITIES_PATH = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_PATH;
     public static final String BROOKLYN_TEST_OSGI_ENTITIES_URL = "classpath:"+BROOKLYN_TEST_OSGI_ENTITIES_PATH;
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_NAME = "org.apache.brooklyn.test.resources.osgi.brooklyn-test-osgi-entities";
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_VERSION = "0.1.0";
 
     protected Framework framework = null;
     private File storageTempDir;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/core/src/test/resources/brooklyn/camp/lite/test-app-service-blueprint.yaml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/brooklyn/camp/lite/test-app-service-blueprint.yaml b/core/src/test/resources/brooklyn/camp/lite/test-app-service-blueprint.yaml
index 8740ca5..846f299 100644
--- a/core/src/test/resources/brooklyn/camp/lite/test-app-service-blueprint.yaml
+++ b/core/src/test/resources/brooklyn/camp/lite/test-app-service-blueprint.yaml
@@ -34,5 +34,5 @@ brooklyn.catalog:
   version: 0.9
   libraries:
   - name: lib1
-    version: v1
+    version: 0.1.0
     url: classpath:/brooklyn/osgi/brooklyn-test-osgi-entities.jar
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/core/src/test/resources/brooklyn/catalog/internal/osgi-catalog.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/brooklyn/catalog/internal/osgi-catalog.xml b/core/src/test/resources/brooklyn/catalog/internal/osgi-catalog.xml
index 52a9764..651dfc5 100644
--- a/core/src/test/resources/brooklyn/catalog/internal/osgi-catalog.xml
+++ b/core/src/test/resources/brooklyn/catalog/internal/osgi-catalog.xml
@@ -24,8 +24,8 @@
     <template name="Entity name" version="9.1.3" type="com.example.ExampleApp">
         <description>An example application</description>
         <libraries>
-            <bundle>file://path/to/bundle.jar</bundle>
-            <bundle>http://www.url.com/for/bundle.jar</bundle>
+            <bundle><url>file://path/to/bundle.jar</url></bundle>
+            <bundle><url>http://www.url.com/for/bundle.jar</url></bundle>
         </libraries>
     </template>
 </catalog>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
index 61e3e4f..e5c233a 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
@@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 
+import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.Entities;
@@ -148,5 +149,8 @@ public abstract class AbstractYamlTest {
     private String join(String[] catalogYaml) {
         return Joiner.on("\n").join(catalogYaml);
     }
-    
+
+    protected String ver(String id) {
+        return id + CatalogUtils.VERSION_DELIMITER + TEST_VERSION;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ReferencedYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ReferencedYamlTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ReferencedYamlTest.java
index 05bb8c0..048fa8f 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ReferencedYamlTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ReferencedYamlTest.java
@@ -95,6 +95,7 @@ public class ReferencedYamlTest extends AbstractYamlTest {
         addCatalogItem(
             "brooklyn.catalog:",
             "  id: yaml.reference",
+            "  version: " + TEST_VERSION,
             "services:",
             "- type: classpath://yaml-ref-entity.yaml");
         
@@ -102,7 +103,7 @@ public class ReferencedYamlTest extends AbstractYamlTest {
         Entity app = createAndStartApplication(
             "services:",
             "- name: " + entityName,
-            "  type: yaml.reference");
+            "  type: " + ver("yaml.reference"));
         
         checkChildEntitySpec(app, entityName);
     }
@@ -112,6 +113,7 @@ public class ReferencedYamlTest extends AbstractYamlTest {
         addCatalogItem(
             "brooklyn.catalog:",
             "  id: yaml.basic",
+            "  version: " + TEST_VERSION,
             "services:",
             "- type: brooklyn.entity.basic.BasicEntity");
         
@@ -134,6 +136,7 @@ public class ReferencedYamlTest extends AbstractYamlTest {
         addCatalogItem(
             "brooklyn.catalog:",
             "  id: " + parentCatalogId,
+            "  version: " + TEST_VERSION,
             "  libraries:",
             "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
             "",
@@ -142,7 +145,7 @@ public class ReferencedYamlTest extends AbstractYamlTest {
 
         Entity app = createAndStartApplication(
             "services:",
-                "- type: " + parentCatalogId);
+                "- type: " + ver(parentCatalogId));
         
         Collection<Entity> children = app.getChildren();
         Assert.assertEquals(children.size(), 1);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
index 9281e61..d4b6474 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
@@ -42,7 +42,7 @@ public class CatalogOsgiVersionMoreEntityTest extends AbstractYamlTest {
     @Test
     public void testMoreEntityV1() throws Exception {
         addCatalogItem(getLocalResource("more-entity-v1-osgi-catalog.yaml"));
-        Entity app = createAndStartApplication("services: [ { type: more-entity } ]");
+        Entity app = createAndStartApplication("services: [ { type: 'more-entity:1.0' } ]");
         Entity moreEntity = Iterables.getOnlyElement(app.getChildren());
         
         Assert.assertEquals(moreEntity.getCatalogItemId(), "more-entity");
@@ -56,7 +56,7 @@ public class CatalogOsgiVersionMoreEntityTest extends AbstractYamlTest {
     public void testMoreEntityV1WithPolicy() throws Exception {
         addCatalogItem(getLocalResource("simple-policy-osgi-catalog.yaml"));
         addCatalogItem(getLocalResource("more-entity-v1-with-policy-osgi-catalog.yaml"));
-        Entity app = createAndStartApplication("services: [ { type: more-entity } ]");
+        Entity app = createAndStartApplication("services: [ { type: 'more-entity:1.0' } ]");
         Entity moreEntity = Iterables.getOnlyElement(app.getChildren());
         
         Assert.assertEquals(moreEntity.getCatalogItemId(), "more-entity");
@@ -70,7 +70,7 @@ public class CatalogOsgiVersionMoreEntityTest extends AbstractYamlTest {
     @Test
     public void testMoreEntityV2() throws Exception {
         addCatalogItem(getLocalResource("more-entity-v2-osgi-catalog.yaml"));
-        Entity app = createAndStartApplication("services: [ { type: more-entity } ]");
+        Entity app = createAndStartApplication("services: [ { type: 'more-entity:1.0' } ]");
         Entity moreEntity = Iterables.getOnlyElement(app.getChildren());
         
         Assert.assertEquals(moreEntity.getCatalogItemId(), "more-entity");
@@ -88,7 +88,7 @@ public class CatalogOsgiVersionMoreEntityTest extends AbstractYamlTest {
     public void testMoreEntityV2ThenV1GivesV1() throws Exception {
         addCatalogItem(getLocalResource("more-entity-v2-osgi-catalog.yaml"));
         addCatalogItem(getLocalResource("more-entity-v1-osgi-catalog.yaml"));
-        Entity app = createAndStartApplication("services: [ { type: more-entity } ]");
+        Entity app = createAndStartApplication("services: [ { type: 'more-entity:1.0' } ]");
         Entity moreEntity = Iterables.getOnlyElement(app.getChildren());
         
         OsgiVersionMoreEntityTest.assertV1EffectorCall(moreEntity);
@@ -102,7 +102,7 @@ public class CatalogOsgiVersionMoreEntityTest extends AbstractYamlTest {
     public void testMoreEntityV1ThenV2GivesV2() throws Exception {
         addCatalogItem(getLocalResource("more-entity-v1-osgi-catalog.yaml"));
         addCatalogItem(getLocalResource("more-entity-v2-osgi-catalog.yaml"));
-        Entity app = createAndStartApplication("services: [ { type: more-entity } ]");
+        Entity app = createAndStartApplication("services: [ { type: 'more-entity:1.0' } ]");
         Entity moreEntity = Iterables.getOnlyElement(app.getChildren());
         
         OsgiVersionMoreEntityTest.assertV2EffectorCall(moreEntity);
@@ -113,8 +113,8 @@ public class CatalogOsgiVersionMoreEntityTest extends AbstractYamlTest {
     public void testMoreEntityBothV1AndV2() throws Exception {
         addCatalogItem(getLocalResource("more-entity-v1-called-v1-osgi-catalog.yaml"));
         addCatalogItem(getLocalResource("more-entity-v2-osgi-catalog.yaml"));
-        Entity v1 = createAndStartApplication("services: [ { type: more-entity-v1 } ]");
-        Entity v2 = createAndStartApplication("services: [ { type: more-entity } ]");
+        Entity v1 = createAndStartApplication("services: [ { type: 'more-entity-v1:1.0' } ]");
+        Entity v2 = createAndStartApplication("services: [ { type: 'more-entity:1.0' } ]");
         
         Entity moreEntityV1 = Iterables.getOnlyElement(v1.getChildren());
         Entity moreEntityV2 = Iterables.getOnlyElement(v2.getChildren());

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
index dfbc96b..f5ad95c 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
@@ -25,10 +25,10 @@ import io.brooklyn.camp.brooklyn.AbstractYamlTest;
 
 import java.util.Collection;
 
+import org.junit.Assert;
 import org.testng.annotations.Test;
 
 import brooklyn.catalog.CatalogItem;
-import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.BasicEntity;
 import brooklyn.management.osgi.OsgiStandaloneTest;
@@ -79,7 +79,7 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
             String yaml = "name: simple-app-yaml\n" +
                           "location: localhost\n" +
                           "services: \n" +
-                          "  - serviceType: " + registeredTypeName;
+                          "  - serviceType: " + ver(registeredTypeName);
             try {
                 createAndStartApplication(yaml);
             } catch (UnsupportedOperationException e) {
@@ -115,7 +115,7 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
         String referencedRegisteredTypeName = "my.catalog.app.id.child.referenced";
         String referrerRegisteredTypeName = "my.catalog.app.id.child.referring";
         addCatalogOSGiEntity(referencedRegisteredTypeName, SIMPLE_ENTITY_TYPE);
-        addCatalogChildOSGiEntity(referrerRegisteredTypeName, referencedRegisteredTypeName);
+        addCatalogChildOSGiEntity(referrerRegisteredTypeName, ver(referencedRegisteredTypeName));
 
         Entity app = createAndStartApplication(
             "name: simple-app-yaml",
@@ -123,7 +123,7 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
             "services:",
             "- serviceType: "+BasicEntity.class.getName(),
             "  brooklyn.children:",
-            "  - type: " + referrerRegisteredTypeName);
+            "  - type: " + ver(referrerRegisteredTypeName));
 
         Collection<Entity> children = app.getChildren();
         assertEquals(children.size(), 1);
@@ -160,19 +160,159 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
     public void testLaunchApplicationChildLoopCatalogIdFails() throws Exception {
         String referrerRegisteredTypeName = "my.catalog.app.id.child.referring";
         try {
-            addCatalogChildOSGiEntity(referrerRegisteredTypeName, referrerRegisteredTypeName);
+            addCatalogChildOSGiEntity(referrerRegisteredTypeName, ver(referrerRegisteredTypeName));
             fail("Expected to throw IllegalStateException");
         } catch (IllegalStateException e) {
             assertTrue(e.getMessage().contains("Could not find "+referrerRegisteredTypeName));
         }
     }
 
+    @Test
+    public void testReferenceInstalledBundleByName() {
+        String firstItemId = "my.catalog.app.id.register_bundle";
+        String secondItemId = "my.catalog.app.id.reference_bundle";
+        addCatalogItem(
+            "brooklyn.catalog:",
+            "  id: " + firstItemId,
+            "  version: " + TEST_VERSION,
+            "  libraries:",
+            "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
+            "",
+            "services:",
+            "- type: " + SIMPLE_ENTITY_TYPE);
+        deleteCatalogEntity(firstItemId);
+
+        addCatalogItem(
+            "brooklyn.catalog:",
+            "  id: " + secondItemId,
+            "  version: " + TEST_VERSION,
+            "  libraries:",
+            "  - name: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_NAME,
+            "    version: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_VERSION,
+            "",
+            "services:",
+            "- type: " + SIMPLE_ENTITY_TYPE);
+
+        deleteCatalogEntity(secondItemId);
+    }
+
+    @Test
+    public void testReferenceNonInstalledBundledByNameFails() {
+        String nonExistentId = "none-existent-id";
+        String nonExistentVersion = "9.9.9";
+        try {
+            addCatalogItem(
+                "brooklyn.catalog:",
+                "  id: my.catalog.app.id.non_existing.ref",
+                "  version: " + TEST_VERSION,
+                "  libraries:",
+                "  - name: " + nonExistentId,
+                "    version: " + nonExistentVersion,
+                "",
+                "services:",
+                "- type: " + SIMPLE_ENTITY_TYPE);
+            fail();
+        } catch (IllegalStateException e) {
+            Assert.assertEquals(e.getMessage(), "Bundle CatalogBundleDto{name=" + nonExistentId + ", version=" + nonExistentVersion + ", url=null} not already registered by name:version, but URL is empty.");
+        }
+    }
+
+    @Test
+    public void testPartialBundleReferenceFails() {
+        try {
+            addCatalogItem(
+                "brooklyn.catalog:",
+                "  id: my.catalog.app.id.non_existing.ref",
+                "  version: " + TEST_VERSION,
+                "  libraries:",
+                "  - name: io.brooklyn.brooklyn-test-osgi-entities",
+                "",
+                "services:",
+                "- type: " + SIMPLE_ENTITY_TYPE);
+            fail();
+        } catch (NullPointerException e) {
+            Assert.assertEquals(e.getMessage(), "version");
+        }
+        try {
+            addCatalogItem(
+                "brooklyn.catalog:",
+                "  id: my.catalog.app.id.non_existing.ref",
+                "  version: " + TEST_VERSION,
+                "  libraries:",
+                "  - version: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_VERSION,
+                "",
+                "services:",
+                "- type: " + SIMPLE_ENTITY_TYPE);
+            fail();
+        } catch (NullPointerException e) {
+            Assert.assertEquals(e.getMessage(), "name");
+        }
+    }
+
+    @Test
+    public void testFullBundleReference() {
+        String itemId = "my.catalog.app.id.full_ref";
+        addCatalogItem(
+            "brooklyn.catalog:",
+            "  id: " + itemId,
+            "  version: " + TEST_VERSION,
+            "  libraries:",
+            "  - name: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_NAME,
+            "    version: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_VERSION,
+            "    url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
+            "",
+            "services:",
+            "- type: " + SIMPLE_ENTITY_TYPE);
+        deleteCatalogEntity(itemId);
+    }
+
+    /**
+     * Test that the name:version contained in the OSGi bundle will
+     * override the values supplied in the YAML.
+     */
+    @Test
+    public void testFullBundleReferenceUrlMetaOverridesLocalNameVersion() {
+        String firstItemId = "my.catalog.app.id.register_bundle";
+        String secondItemId = "my.catalog.app.id.reference_bundle";
+        String nonExistentId = "non_existent_id";
+        String nonExistentVersion = "9.9.9";
+        addCatalogItem(
+            "brooklyn.catalog:",
+            "  id: " + firstItemId,
+            "  version: " + TEST_VERSION,
+            "  libraries:",
+            "  - name: " + nonExistentId,
+            "    version: " + nonExistentVersion,
+            "    url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
+            "",
+            "services:",
+            "- type: " + SIMPLE_ENTITY_TYPE);
+        deleteCatalogEntity(firstItemId);
+
+        try {
+            addCatalogItem(
+                "brooklyn.catalog:",
+                "  id: " + secondItemId,
+                "  version: " + TEST_VERSION,
+                "  libraries:",
+                "  - name: " + nonExistentId,
+                "    version: " + nonExistentVersion,
+                "",
+                "services:",
+                "- type: " + SIMPLE_ENTITY_TYPE);
+            fail();
+        } catch (IllegalStateException e) {
+            assertEquals(e.getMessage(), "Bundle CatalogBundleDto{name=" + nonExistentId + ", version=" + nonExistentVersion + ", url=null} " +
+                    "not already registered by name:version, but URL is empty.");
+        }
+    }
+
     private void registerAndLaunchAndAssertSimpleEntity(String registeredTypeName, String serviceType) throws Exception {
         addCatalogOSGiEntity(registeredTypeName, serviceType);
         String yaml = "name: simple-app-yaml\n" +
                       "location: localhost\n" +
                       "services: \n" +
-                      "  - serviceType: "+registeredTypeName;
+                      "  - serviceType: "+ver(registeredTypeName);
         Entity app = createAndStartApplication(yaml);
 
         Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
@@ -207,7 +347,7 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
             "  name: My Catalog App",
             "  description: My description",
             "  icon_url: classpath://path/to/myicon.jpg",
-            "  version: 0.1.2",
+            "  version: " + TEST_VERSION,
             "  libraries:",
             "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
             "",
@@ -217,7 +357,4 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
             "  - type: " + serviceType);
     }
 
-    private String ver(String id) {
-        return id + CatalogUtils.VERSION_DELIMITER + TEST_VERSION;
-    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
index e704ea1..9ba5bee 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
@@ -60,7 +60,7 @@ public class CatalogYamlPolicyTest extends AbstractYamlTest {
             "services: ",
             "  - type: brooklyn.entity.basic.BasicEntity\n" +
             "    brooklyn.policies:\n" +
-            "    - type: " + registeredTypeName,
+            "    - type: " + ver(registeredTypeName),
             "      brooklyn.config:",
             "        config2: config2 override",
             "        config3: config3");
@@ -87,19 +87,19 @@ public class CatalogYamlPolicyTest extends AbstractYamlTest {
             "  name: My Catalog App",
             "  description: My description",
             "  icon_url: classpath://path/to/myicon.jpg",
-            "  version: 0.1.2",
+            "  version: " + TEST_VERSION,
             "  libraries:",
             "  - url: " + OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL,
             "",
             "services:",
             "- type: " + SIMPLE_ENTITY_TYPE,
             "  brooklyn.policies:",
-            "  - type: " + referencedRegisteredTypeName);
+            "  - type: " + ver(referencedRegisteredTypeName));
 
         String yaml = "name: simple-app-yaml\n" +
                       "location: localhost\n" +
                       "services: \n" +
-                      "  - serviceType: "+referrerRegisteredTypeName;
+                      "  - serviceType: "+ ver(referrerRegisteredTypeName);
 
         Entity app = createAndStartApplication(yaml);
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-with-policy-osgi-catalog.yaml
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-with-policy-osgi-catalog.yaml b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-with-policy-osgi-catalog.yaml
index 79657d8..3c03bd0 100644
--- a/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-with-policy-osgi-catalog.yaml
+++ b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v1-with-policy-osgi-catalog.yaml
@@ -19,7 +19,7 @@
 services:
 - type: brooklyn.osgi.tests.more.MoreEntity
   brooklyn.policies:
-  - type: simple-policy
+  - type: simple-policy:1.0
 
 brooklyn.catalog:
   id: more-entity

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v2-osgi-catalog.yaml
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v2-osgi-catalog.yaml b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v2-osgi-catalog.yaml
index 7b29439..74323fa 100644
--- a/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v2-osgi-catalog.yaml
+++ b/usage/camp/src/test/resources/io/brooklyn/camp/brooklyn/catalog/more-entity-v2-osgi-catalog.yaml
@@ -21,7 +21,7 @@ services:
 
 brooklyn.catalog:
   id: more-entity
-  version: 2.0
+  version: 1.0
   # see OsgiTestResources
   libraries:
   - classpath:/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.2.0.jar

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/usage/camp/src/test/resources/yaml-ref-app.yaml
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/yaml-ref-app.yaml b/usage/camp/src/test/resources/yaml-ref-app.yaml
index a26a4ab..8e1f740 100644
--- a/usage/camp/src/test/resources/yaml-ref-app.yaml
+++ b/usage/camp/src/test/resources/yaml-ref-app.yaml
@@ -18,4 +18,4 @@
 name: Basic app
 services:
 - name: service
-  type: brooklyn.entity.basic.BasicApplication
\ No newline at end of file
+  type: brooklyn.entity.basic.BasicApplication

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/usage/camp/src/test/resources/yaml-ref-catalog.yaml
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/yaml-ref-catalog.yaml b/usage/camp/src/test/resources/yaml-ref-catalog.yaml
index 220deb9..545fd1a 100644
--- a/usage/camp/src/test/resources/yaml-ref-catalog.yaml
+++ b/usage/camp/src/test/resources/yaml-ref-catalog.yaml
@@ -18,4 +18,4 @@
 name: Basic app
 services:
 - name: service
-  type: yaml.basic
\ No newline at end of file
+  type: yaml.basic:0.1.2

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/usage/camp/src/test/resources/yaml-ref-entity.yaml
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/yaml-ref-entity.yaml b/usage/camp/src/test/resources/yaml-ref-entity.yaml
index 8d27427..69d311f 100644
--- a/usage/camp/src/test/resources/yaml-ref-entity.yaml
+++ b/usage/camp/src/test/resources/yaml-ref-entity.yaml
@@ -18,4 +18,4 @@
 name: Basic entity
 services:
 - name: service
-  type: brooklyn.entity.basic.BasicEntity
\ No newline at end of file
+  type: brooklyn.entity.basic.BasicEntity

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/268aaf6b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
index 771f8ff..8e33ebb 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
@@ -25,6 +25,7 @@ import java.awt.Image;
 import java.awt.Toolkit;
 import java.io.IOException;
 import java.net.URI;
+import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
@@ -39,14 +40,16 @@ import org.testng.annotations.Test;
 import org.testng.reporters.Files;
 
 import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.CatalogItem.CatalogBundle;
+import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.management.osgi.OsgiStandaloneTest;
 import brooklyn.policy.autoscaling.AutoScalerPolicy;
 import brooklyn.rest.domain.CatalogEntitySummary;
 import brooklyn.rest.domain.CatalogItemSummary;
 import brooklyn.rest.domain.CatalogPolicySummary;
 import brooklyn.rest.testing.BrooklynRestResourceTest;
-import brooklyn.util.collections.MutableList;
 
+import com.google.common.collect.Iterables;
 import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.api.client.GenericType;
 
@@ -99,14 +102,16 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
     Assert.assertNotNull(entityItem.getPlanYaml());
     Assert.assertTrue(entityItem.getPlanYaml().contains("brooklyn.test.entity.TestEntity"));
     
+    assertEquals(entityItem.getId(), ver(registeredTypeName));
     assertEquals(entityItem.getSymbolicName(), registeredTypeName);
     assertEquals(entityItem.getVersion(), TEST_VERSION);
     
     // and internally let's check we have libraries
     CatalogItem<?, ?> item = getManagementContext().getCatalog().getCatalogItem(registeredTypeName, TEST_VERSION);
     Assert.assertNotNull(item);
-    List<String> libs = item.getLibraries().getBundles();
-    assertEquals(libs, MutableList.of(bundleUrl));
+    Collection<CatalogBundle> libs = item.getLibraries().getBundles();
+    assertEquals(libs.size(), 1);
+    assertEquals(Iterables.getOnlyElement(libs).getUrl(), bundleUrl);
 
     // now let's check other things on the item
     assertEquals(entityItem.getName(), "My Catalog App");
@@ -129,7 +134,7 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
         "  id: " + registeredTypeName + "\n"+
         "  name: My Catalog App\n"+
         "  description: My description\n"+
-        "  version: 0.1.2\n"+
+        "  version: " + TEST_VERSION + "\n" +
         "  libraries:\n"+
         "  - url: " + bundleUrl + "\n"+
         "\n"+
@@ -142,6 +147,7 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
     assertEquals(entityItem.getRegisteredType(), registeredTypeName);
     Assert.assertNotNull(entityItem.getPlanYaml());
     Assert.assertTrue(entityItem.getPlanYaml().contains(policyType));
+    assertEquals(entityItem.getId(), ver(registeredTypeName));
     assertEquals(entityItem.getSymbolicName(), registeredTypeName);
     assertEquals(entityItem.getVersion(), TEST_VERSION);
   }
@@ -260,4 +266,8 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
             .get(ClientResponse.class);
     assertEquals(getPostDeleteResponse.getStatus(), Response.Status.NOT_FOUND.getStatusCode());
   }
+  
+  private static String ver(String id) {
+      return id + CatalogUtils.VERSION_DELIMITER + TEST_VERSION;
+  }
 }


[06/18] incubator-brooklyn git commit: Catalog versioning - Fail on adding catalog item with same version.

Posted by he...@apache.org.
Catalog versioning - Fail on adding catalog item with same version.

Allow for override if forceUpdate is set.


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

Branch: refs/heads/master
Commit: 8d3a8c913e3aeb702609d778859bb509f4e809d7
Parents: 53809f3
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Wed Jul 23 23:44:03 2014 +0300
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 11:49:50 2014 +0200

----------------------------------------------------------------------
 .../java/brooklyn/catalog/BrooklynCatalog.java    | 13 ++++++++++++-
 .../catalog/internal/BasicBrooklynCatalog.java    | 18 +++++++++++++++++-
 .../brooklyn/camp/brooklyn/AbstractYamlTest.java  |  8 +++++++-
 .../brooklyn/catalog/CatalogYamlEntityTest.java   | 16 ++++++++++++++++
 4 files changed, 52 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8d3a8c91/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
index c5d0a63..5b0a5cc 100644
--- a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
+++ b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
@@ -78,12 +78,23 @@ public interface BrooklynCatalog {
 
     /**
      * Adds an item (represented in yaml) to the catalog.
-     * 
+     * Fails if the same version exists in catalog.
+     *
      * @throws IllegalArgumentException if the yaml was invalid
      */
     CatalogItem<?,?> addItem(String yaml);
     
     /**
+     * Adds an item (represented in yaml) to the catalog.
+     * 
+     * @param forceUpdate If true allows catalog update even when an
+     * item exists with the same symbolicName and version
+     *
+     * @throws IllegalArgumentException if the yaml was invalid
+     */
+    CatalogItem<?,?> addItem(String yaml, boolean forceUpdate);
+    
+    /**
      * adds an item to the 'manual' catalog;
      * this does not update the classpath or have a record to the java Class
      *

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8d3a8c91/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
index eb2e793..e560ae2 100644
--- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
@@ -507,10 +507,17 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
 
     @Override
     public CatalogItem<?,?> addItem(String yaml) {
+        return addItem(yaml, false);
+    }
+
+    @Override
+    public CatalogItem<?,?> addItem(String yaml, boolean forceUpdate) {
         log.debug("Adding manual catalog item to "+mgmt+": "+yaml);
         checkNotNull(yaml, "yaml");
-        if (manualAdditionsCatalog==null) loadManualAdditionsCatalog();
         CatalogItemDtoAbstract<?,?> itemDto = getAbstractCatalogItem(yaml);
+        checkItemNotExists(itemDto, forceUpdate);
+
+        if (manualAdditionsCatalog==null) loadManualAdditionsCatalog();
         manualAdditionsCatalog.addEntry(itemDto);
 
         // Ensure the cache is populated and it is persisted by the management context
@@ -525,8 +532,16 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         return itemDto;
     }
 
+    private void checkItemNotExists(CatalogItem<?,?> itemDto, boolean forceUpdate) {
+        if (!forceUpdate && getCatalogItemDo(itemDto.getId(), itemDto.getVersion()) != null) {
+            throw new IllegalStateException("Updating existing catalog entries is forbidden: " +
+                    itemDto.getId() + ":" + itemDto.getVersion() + ". Use forceUpdate argument to override.");
+        }
+    }
+
     @Override @Deprecated /** @deprecated see super */
     public void addItem(CatalogItem<?,?> item) {
+        //assume forceUpdate for backwards compatibility
         log.debug("Adding manual catalog item to "+mgmt+": "+item);
         checkNotNull(item, "item");
         CatalogUtils.installLibraries(mgmt, item.getLibraries());
@@ -536,6 +551,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
 
     @Override @Deprecated /** @deprecated see super */
     public CatalogItem<?,?> addItem(Class<?> type) {
+        //assume forceUpdate for backwards compatibility
         log.debug("Adding manual catalog item to "+mgmt+": "+type);
         checkNotNull(type, "type");
         if (manualAdditionsCatalog==null) loadManualAdditionsCatalog();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8d3a8c91/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
index e5c233a..fd856ca 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
@@ -50,6 +50,7 @@ public abstract class AbstractYamlTest {
     private ManagementContext brooklynMgmt;
     protected BrooklynCampPlatform platform;
     protected BrooklynCampPlatformLauncherNoServer launcher;
+    private boolean forceUpdate;
     
     public AbstractYamlTest() {
         super();
@@ -59,6 +60,7 @@ public abstract class AbstractYamlTest {
     
     @BeforeMethod(alwaysRun = true)
     public void setUp() {
+        forceUpdate = false;
         launcher = new BrooklynCampPlatformLauncherNoServer() {
             @Override
             protected LocalManagementContext newMgmtContext() {
@@ -135,7 +137,7 @@ public abstract class AbstractYamlTest {
     }
 
     protected void addCatalogItem(String catalogYaml) {
-        mgmt().getCatalog().addItem(catalogYaml);
+        mgmt().getCatalog().addItem(catalogYaml, forceUpdate);
     }
 
     protected void deleteCatalogEntity(String catalogItem) {
@@ -153,4 +155,8 @@ public abstract class AbstractYamlTest {
     protected String ver(String id) {
         return id + CatalogUtils.VERSION_DELIMITER + TEST_VERSION;
     }
+
+    public void forceCatalogUpdate() {
+        forceUpdate = true;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8d3a8c91/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
index dc51569..dce7a9b 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
@@ -307,6 +307,22 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
                     "not already registered by name:version, but URL is empty.");
         }
     }
+    
+    @Test(expectedExceptions = IllegalStateException.class)
+    public void testUpdatingItemFails() {
+        String id = "my.catalog.app.id.duplicate";
+        addCatalogOSGiEntity(id);
+        addCatalogOSGiEntity(id);
+    }
+
+    @Test
+    public void testForcedUpdatingItem() {
+        String id = "my.catalog.app.id.duplicate";
+        addCatalogOSGiEntity(id);
+        forceCatalogUpdate();
+        addCatalogOSGiEntity(id);
+        deleteCatalogEntity(id);
+    }
 
     @Test
     public void testCreateSpecFromCatalogItem() {


[17/18] incubator-brooklyn git commit: Catalog versioning - address review comments; OSGi rebinding

Posted by he...@apache.org.
Catalog versioning - address review comments; OSGi rebinding

When the type for an item being deserialized can't be find try iterating through all available catalog items as a last resort.


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

Branch: refs/heads/master
Commit: cf3a9eb0ed97fef0562f023465e9a5b7edbf601a
Parents: 8bd76ad
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Thu Nov 13 21:44:15 2014 +0200
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 21:44:15 2014 +0200

----------------------------------------------------------------------
 .../internal/CatalogBundleConverter.java        |  4 ++
 .../catalog/internal/CatalogItemComparator.java |  4 ++
 .../internal/CatalogItemDtoAbstract.java        |  6 +-
 .../AbstractBrooklynObjectRebindSupport.java    |  5 +-
 .../entity/rebind/RebindManagerImpl.java        | 58 +++++++++++++++-----
 .../BrooklynMementoPersisterToObjectStore.java  |  5 +-
 .../CatalogItemLibrariesConverter.java          |  4 ++
 .../internal/BrooklynFeatureEnablement.java     |  9 +++
 8 files changed, 73 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf3a9eb0/core/src/main/java/brooklyn/catalog/internal/CatalogBundleConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogBundleConverter.java b/core/src/main/java/brooklyn/catalog/internal/CatalogBundleConverter.java
index fe77d02..5ef4a96 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogBundleConverter.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogBundleConverter.java
@@ -28,6 +28,10 @@ import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
 import com.thoughtworks.xstream.mapper.Mapper;
 
 
+/**
+ *  Convert old-style catalog.xml file formats to the latest version.
+ *  The code is needed only during transition to the new version, can be removed after a while.
+ */
 @Deprecated
 public class CatalogBundleConverter implements Converter {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf3a9eb0/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
index a202b92..d8a2b2e 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
@@ -23,6 +23,10 @@ import java.util.Comparator;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.util.text.NaturalOrderComparator;
 
+/**
+ * When using the comparator to sort - first using symbolicName
+ * and if equal puts larger versions first, snapshots at the back.
+ */
 public class CatalogItemComparator implements Comparator<CatalogItem<?, ?>> {
     private static final String SNAPSHOT = "SNAPSHOT";
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf3a9eb0/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
index 9b434cc..fde647b 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
@@ -111,9 +111,9 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
 
     @Override
     public String getVersion() {
-        //xstream doesn't call constructors
-        //the object is used directly (instead of a memento) when
-        //deserializing directly from catalog.xml
+        // The property is set to NO_VERSION when the object is initialized so it's not supposed to be null ever.
+        // But xstream doesn't call constructors when reading from the catalog.xml file which results in null value
+        // for the version property. That's why we have to fix it in the getter.
         if (version != null) {
             return version;
         } else {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf3a9eb0/core/src/main/java/brooklyn/entity/rebind/AbstractBrooklynObjectRebindSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/AbstractBrooklynObjectRebindSupport.java b/core/src/main/java/brooklyn/entity/rebind/AbstractBrooklynObjectRebindSupport.java
index 9cfaf97..edd70c3 100644
--- a/core/src/main/java/brooklyn/entity/rebind/AbstractBrooklynObjectRebindSupport.java
+++ b/core/src/main/java/brooklyn/entity/rebind/AbstractBrooklynObjectRebindSupport.java
@@ -48,10 +48,7 @@ public abstract class AbstractBrooklynObjectRebindSupport<T extends Memento> imp
         if (LOG.isTraceEnabled()) LOG.trace("Reconstructing: {}", memento.toVerboseString());
 
         instance.setDisplayName(memento.getDisplayName());
-        //check if not already forced at entity creation time
-        if (instance.getCatalogItemId() == null) {
-            instance.setCatalogItemId(memento.getCatalogItemId());
-        }
+        //catalogItemId already set when creating the object
         addConfig(rebindContext, memento);
         addTags(rebindContext, memento);
         addCustoms(rebindContext, memento);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf3a9eb0/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
index 07c60a5..5ad0f7d 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
@@ -66,7 +66,6 @@ import brooklyn.location.basic.LocationInternal;
 import brooklyn.management.ExecutionContext;
 import brooklyn.management.Task;
 import brooklyn.management.classloading.BrooklynClassLoadingContext;
-import brooklyn.management.classloading.JavaBrooklynClassLoadingContext;
 import brooklyn.management.ha.HighAvailabilityManagerImpl;
 import brooklyn.management.ha.ManagementNodeState;
 import brooklyn.management.internal.EntityManagerInternal;
@@ -94,6 +93,7 @@ import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.RuntimeInterruptedException;
 import brooklyn.util.flags.FlagUtils;
+import brooklyn.util.guava.Maybe;
 import brooklyn.util.javalang.Reflections;
 import brooklyn.util.task.BasicExecutionContext;
 import brooklyn.util.task.ScheduledTask;
@@ -660,7 +660,7 @@ public class RebindManagerImpl implements RebindManager {
             for (Map.Entry<String, EntityMementoManifest> entry : mementoManifest.getEntityIdToManifest().entrySet()) {
                 String entityId = entry.getKey();
                 EntityMementoManifest entityManifest = entry.getValue();
-                String catalogItemId = findCatalogItemId(mementoManifest.getEntityIdToManifest(), entityManifest);
+                String catalogItemId = findCatalogItemId(classLoader, mementoManifest.getEntityIdToManifest(), entityManifest);
                 
                 if (LOG.isTraceEnabled()) LOG.trace("RebindManager instantiating entity {}", entityId);
                 
@@ -955,7 +955,7 @@ public class RebindManagerImpl implements RebindManager {
         }
     }
 
-    private String findCatalogItemId(Map<String, EntityMementoManifest> entityIdToManifest, EntityMementoManifest entityManifest) {
+    private String findCatalogItemId(ClassLoader cl, Map<String, EntityMementoManifest> entityIdToManifest, EntityMementoManifest entityManifest) {
         if (entityManifest.getCatalogItemId() != null) {
             return entityManifest.getCatalogItemId();
         }
@@ -998,20 +998,33 @@ public class RebindManagerImpl implements RebindManager {
                     ptr = null;
                 }
             }
+
+            //As a last resort go through all catalog items trying to load the type and use the first that succeeds.
+            //But first check if can be loaded from the default classpath
+            try {
+                cl.loadClass(entityManifest.getType());
+                return null;
+            } catch (ClassNotFoundException e) {
+            }
+
+            for (CatalogItem<?, ?> item : catalog.getCatalogItems()) {
+                BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(managementContext, item);
+                boolean canLoadClass = loader.tryLoadClass(entityManifest.getType()).isPresent();
+                if (canLoadClass) {
+                    return item.getId();
+                }
+            }
         }
         return null;
     }
 
     private BrooklynClassLoadingContext getLoadingContextFromCatalogItemId(String catalogItemId, ClassLoader classLoader, RebindContext rebindContext) {
-        if (catalogItemId != null) {
-            CatalogItem<?, ?> catalogItem = rebindContext.getCatalogItem(catalogItemId);
-            if (catalogItem != null) {
-                return CatalogUtils.newClassLoadingContext(managementContext, catalogItem);
-            } else {
-                throw new IllegalStateException("Failed to load catalog item " + catalogItemId + " required for rebinding.");
-            }
+        Preconditions.checkNotNull(catalogItemId, "catalogItemId required (should not be null)");
+        CatalogItem<?, ?> catalogItem = rebindContext.getCatalogItem(catalogItemId);
+        if (catalogItem != null) {
+            return CatalogUtils.newClassLoadingContext(managementContext, catalogItem);
         } else {
-            return JavaBrooklynClassLoadingContext.create(managementContext, classLoader);
+            throw new IllegalStateException("Failed to load catalog item " + catalogItemId + " required for rebinding.");
         }
     }
 
@@ -1132,16 +1145,33 @@ public class RebindManagerImpl implements RebindManager {
         @SuppressWarnings("unchecked")
         private <T extends BrooklynObject> Class<? extends T> load(Class<T> bType, String jType, String catalogItemId, String contextSuchAsId) {
             checkNotNull(jType, "Type of %s (%s) must not be null", contextSuchAsId, bType.getSimpleName());
-            if (catalogItemId==null) {
+            if (catalogItemId != null) {
+                BrooklynClassLoadingContext loader = getLoadingContextFromCatalogItemId(catalogItemId, classLoader, rebindContext);
+                return loader.loadClass(jType, bType);
+            } else {
                 // we have previously used reflections; not sure if that's needed?
                 try {
                     return (Class<T>)reflections.loadClass(jType);
                 } catch (Exception e) {
                     LOG.warn("Unable to load "+jType+" using reflections; will try standard context");
                 }
+
+                if (BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_INFER_CATALOG_ITEM_ON_REBIND)) {
+                    //Try loading from whichever catalog bundle succeeds.
+                    BrooklynCatalog catalog = managementContext.getCatalog();
+                    for (CatalogItem<?, ?> item : catalog.getCatalogItems()) {
+                        BrooklynClassLoadingContext catalogLoader = CatalogUtils.newClassLoadingContext(managementContext, item);
+                        Maybe<Class<?>> catalogClass = catalogLoader.tryLoadClass(jType);
+                        if (catalogClass.isPresent()) {
+                            return (Class<? extends T>) catalogClass.get();
+                        }
+                    }
+                    throw new IllegalStateException("No catalogItemId specified and can't load class from either classpath of catalog items");
+                } else {
+                    throw new IllegalStateException("No catalogItemId specified and can't load class from classpath");
+                }
+
             }
-            BrooklynClassLoadingContext loader = getLoadingContextFromCatalogItemId(catalogItemId, classLoader, rebindContext);
-            return loader.loadClass(jType, bType);
         }
 
         /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf3a9eb0/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
index 2c1e323..fdaca78 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
@@ -172,7 +172,10 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
         if (item==null || item.getCatalogItemId()==null) {
             return null;
         }
-        CatalogItem<?, ?> catalogItem = lookupContext.lookupCatalogItem(item.getCatalogItemId());
+        CatalogItem<?, ?> catalogItem = CatalogUtils.getCatalogItemOptionalVersion(lookupContext.lookupManagementContext(), item.getCatalogItemId());
+        if (catalogItem == null) {
+            throw new IllegalStateException("Catalog item " + item.getCatalogItemId() + " not found. Can't deserialize object " + objectId + " of type " + type);
+        }
         return ClassLoaderFromBrooklynClassLoadingContext.of(CatalogUtils.newClassLoadingContext(lookupContext.lookupManagementContext(), catalogItem));
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf3a9eb0/core/src/main/java/brooklyn/entity/rebind/persister/CatalogItemLibrariesConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/CatalogItemLibrariesConverter.java b/core/src/main/java/brooklyn/entity/rebind/persister/CatalogItemLibrariesConverter.java
index ea0399b..91b99d6 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/CatalogItemLibrariesConverter.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/CatalogItemLibrariesConverter.java
@@ -31,6 +31,10 @@ import com.thoughtworks.xstream.converters.UnmarshallingContext;
 import com.thoughtworks.xstream.io.HierarchicalStreamReader;
 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
 
+/**
+ *  Convert old-style rebind file formats to the latest version.
+ *  The code is needed only during transition to the new version, can be removed after a while.
+ */
 @Deprecated
 public class CatalogItemLibrariesConverter implements Converter {
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf3a9eb0/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java b/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java
index 88c5f6d..0ce1b54 100644
--- a/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java
+++ b/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java
@@ -73,6 +73,15 @@ public class BrooklynFeatureEnablement {
      */
     public static final String FEATURE_RENAME_THREADS = "brooklyn.executionManager.renameThreads";
 
+    /**
+     * When rebinding to store created from a previous version the catalogItemId properties will be missing which
+     * results in errors when OSGi bundles are used. When enabled the code tries to infer the catalogItemId from
+     * <ul>
+     *   <li> parent entities
+     *   <li> catalog items matching the type that needs to be deserialized
+     *   <li> iterating through all catalog items and checking if they can provide the needed type
+     * </ul>
+     */
     public static final String FEATURE_INFER_CATALOG_ITEM_ON_REBIND = "brooklyn.backwardCompatibility.feature.inferCatalogItemOnRebind";
     
     private static final Map<String, Boolean> FEATURE_ENABLEMENTS = Maps.newLinkedHashMap();


[04/18] incubator-brooklyn git commit: Catalog versioning - Using AssemblyTemplate.getName() for getting catalog items is wrong.

Posted by he...@apache.org.
Catalog versioning - Using AssemblyTemplate.getName() for getting catalog items is wrong.

Catalog items are registered by id+version in the brooklyn.config section,
the name is unrelated which means that the items were never found.
The code path in BrooklynEntityMatcher is not needed anyway, already
handled by the instantiator.


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

Branch: refs/heads/master
Commit: 53809f3d29790cda11e4efd9ee951a963057d4a5
Parents: 268aaf6
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Thu Jul 24 12:29:31 2014 +0300
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 11:49:49 2014 +0200

----------------------------------------------------------------------
 .../brooklyn/spi/creation/BrooklynEntityMatcher.java     |  5 -----
 .../camp/brooklyn/catalog/CatalogYamlEntityTest.java     | 11 +++++++++++
 2 files changed, 11 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/53809f3d/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java
index 54bd4a6..db64c0c 100644
--- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java
+++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityMatcher.java
@@ -68,11 +68,6 @@ public class BrooklynEntityMatcher implements PdpMatcher {
     protected String lookupType(Object deploymentPlanItem) {
         if (deploymentPlanItem instanceof Service) {
             Service service = (Service)deploymentPlanItem;
-            
-            String name = service.getName();
-            if (mgmt.getCatalog().getCatalogItem(name) != null) {
-                return name;
-            }
 
             String serviceType = service.getServiceType();
             BrooklynClassLoadingContext loader = BasicBrooklynCatalog.BrooklynLoaderTracker.getLoader();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/53809f3d/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
index f5ad95c..dc51569 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
@@ -28,6 +28,7 @@ import java.util.Collection;
 import org.junit.Assert;
 import org.testng.annotations.Test;
 
+import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.BasicEntity;
@@ -307,6 +308,16 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
         }
     }
 
+    @Test
+    public void testCreateSpecFromCatalogItem() {
+        String id = "my.catalog.app.id.create_spec";
+        addCatalogOSGiEntity(id);
+        BrooklynCatalog catalog = mgmt().getCatalog();
+        CatalogItem<?, ?> item = catalog.getCatalogItem(id, TEST_VERSION);
+        Object spec = catalog.createSpec(item);
+        Assert.assertNotNull(spec);
+    }
+
     private void registerAndLaunchAndAssertSimpleEntity(String registeredTypeName, String serviceType) throws Exception {
         addCatalogOSGiEntity(registeredTypeName, serviceType);
         String yaml = "name: simple-app-yaml\n" +


[15/18] incubator-brooklyn git commit: OSGi rebind - infer catalog item ID from entity type

Posted by he...@apache.org.
OSGi rebind - infer catalog item ID from entity type

The change allows rebinding to persistence stores from old versions where the catalog item ID used for creating the entities is not included in the serialized state.


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

Branch: refs/heads/master
Commit: e81a260e58a7b79edc96878f50a714b5a1a9d1b5
Parents: 40eee57
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Thu Nov 13 14:02:45 2014 +0200
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 14:02:45 2014 +0200

----------------------------------------------------------------------
 .../AbstractBrooklynObjectRebindSupport.java    |  5 +-
 .../entity/rebind/RebindManagerImpl.java        | 65 ++++++++++++++++----
 .../internal/BrooklynFeatureEnablement.java     |  3 +
 3 files changed, 61 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e81a260e/core/src/main/java/brooklyn/entity/rebind/AbstractBrooklynObjectRebindSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/AbstractBrooklynObjectRebindSupport.java b/core/src/main/java/brooklyn/entity/rebind/AbstractBrooklynObjectRebindSupport.java
index 5d8bcd6..9cfaf97 100644
--- a/core/src/main/java/brooklyn/entity/rebind/AbstractBrooklynObjectRebindSupport.java
+++ b/core/src/main/java/brooklyn/entity/rebind/AbstractBrooklynObjectRebindSupport.java
@@ -48,7 +48,10 @@ public abstract class AbstractBrooklynObjectRebindSupport<T extends Memento> imp
         if (LOG.isTraceEnabled()) LOG.trace("Reconstructing: {}", memento.toVerboseString());
 
         instance.setDisplayName(memento.getDisplayName());
-        instance.setCatalogItemId(memento.getCatalogItemId());
+        //check if not already forced at entity creation time
+        if (instance.getCatalogItemId() == null) {
+            instance.setCatalogItemId(memento.getCatalogItemId());
+        }
         addConfig(rebindContext, memento);
         addTags(rebindContext, memento);
         addCustoms(rebindContext, memento);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e81a260e/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
index 74cfc12..177dca6 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
@@ -21,6 +21,7 @@ package brooklyn.entity.rebind;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.io.IOException;
+import java.lang.reflect.Field;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -35,14 +36,15 @@ import org.slf4j.LoggerFactory;
 
 import brooklyn.basic.AbstractBrooklynObject;
 import brooklyn.basic.BrooklynObject;
+import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.CatalogLoadMode;
 import brooklyn.catalog.internal.BasicBrooklynCatalog;
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.BrooklynLogging;
+import brooklyn.config.BrooklynLogging.LoggingLevel;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.ConfigKey;
-import brooklyn.config.BrooklynLogging.LoggingLevel;
 import brooklyn.enricher.basic.AbstractEnricher;
 import brooklyn.entity.Application;
 import brooklyn.entity.Entity;
@@ -656,13 +658,17 @@ public class RebindManagerImpl implements RebindManager {
                 String entityId = entry.getKey();
                 EntityMementoManifest entityManifest = entry.getValue();
                 String catalogItemId = findCatalogItemId(mementoManifest.getEntityIdToManifest(), entityManifest);
+                
                 if (LOG.isTraceEnabled()) LOG.trace("RebindManager instantiating entity {}", entityId);
                 
                 try {
                     Entity entity = newEntity(entityId, entityManifest.getType(), getLoadingContextFromCatalogItemId(catalogItemId, classLoader, rebindContext));
                     ((EntityInternal)entity).getManagementSupport().setReadOnly( rebindContext.isReadOnly(entity) );
                     rebindContext.registerEntity(entityId, entity);
-                    
+
+                    if (forceCatalogItem(entityManifest)) {
+                        ((EntityInternal)entity).setCatalogItemId(catalogItemId);
+                    }
                 } catch (Exception e) {
                     exceptionHandler.onCreateFailed(BrooklynObjectType.ENTITY, entityId, entityManifest.getType(), e);
                 }
@@ -948,17 +954,54 @@ public class RebindManagerImpl implements RebindManager {
             RebindTracker.reset();
         }
     }
-    
+
+    private boolean forceCatalogItem(EntityMementoManifest entityManifest) {
+        return entityManifest.getCatalogItemId() == null && 
+                BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_INFER_CATALOG_ITEM_ON_REBIND);
+    }
+
     private String findCatalogItemId(Map<String, EntityMementoManifest> entityIdToManifest, EntityMementoManifest entityManifest) {
-        EntityMementoManifest ptr = entityManifest;
-        while (ptr != null) {
-            if (ptr.getCatalogItemId() != null) {
-                return ptr.getCatalogItemId();
+        if (entityManifest.getCatalogItemId() != null) {
+            return entityManifest.getCatalogItemId();
+        }
+
+        if (BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_INFER_CATALOG_ITEM_ON_REBIND)) {
+            //First check if any of the parent entities has a catalogItemId set.
+            EntityMementoManifest ptr = entityManifest;
+            while (ptr != null) {
+                if (ptr.getCatalogItemId() != null) {
+                    CatalogItem<?, ?> catalogItem = CatalogUtils.getCatalogItemOptionalVersion(managementContext, ptr.getCatalogItemId());
+                    if (catalogItem != null) {
+                        return catalogItem.getId();
+                    } else {
+                        //Couldn't find a catalog item with this id, but return it anyway and
+                        //let the caller deal with the error.
+                        return ptr.getCatalogItemId();
+                    }
+                }
+                if (ptr.getParent() != null) {
+                    ptr = entityIdToManifest.get(ptr.getParent());
+                } else {
+                    ptr = null;
+                }
             }
-            if (ptr.getParent() != null) {
-                ptr = entityIdToManifest.get(ptr.getParent());
-            } else {
-                ptr = null;
+
+            //If no parent entity has the catalogItemId set try to match them by the type we are trying to load.
+            //The current convention is to set catalog item IDs to the java type (for both plain java or CAMP plan) they represent.
+            //This will be applicable only the first time the store is rebinded, while the catalog items don't have the default
+            //version appended to their IDs, but then we will have catalogItemId set on entities so not neede further anyways.
+            BrooklynCatalog catalog = managementContext.getCatalog();
+            ptr = entityManifest;
+            while (ptr != null) {
+                CatalogItem<?, ?> catalogItem = catalog.getCatalogItem(ptr.getType(), BrooklynCatalog.DEFAULT_VERSION);
+                if (catalogItem != null) {
+                    return catalogItem.getId();
+                }
+                if (ptr.getParent() != null) {
+                    ptr = entityIdToManifest.get(ptr.getParent());
+                } else {
+                    ptr = null;
+                }
             }
         }
         return null;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e81a260e/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java b/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java
index 819c2d1..88c5f6d 100644
--- a/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java
+++ b/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java
@@ -72,6 +72,8 @@ public class BrooklynFeatureEnablement {
      * Defaults to false if system property is not set.
      */
     public static final String FEATURE_RENAME_THREADS = "brooklyn.executionManager.renameThreads";
+
+    public static final String FEATURE_INFER_CATALOG_ITEM_ON_REBIND = "brooklyn.backwardCompatibility.feature.inferCatalogItemOnRebind";
     
     private static final Map<String, Boolean> FEATURE_ENABLEMENTS = Maps.newLinkedHashMap();
 
@@ -90,6 +92,7 @@ public class BrooklynFeatureEnablement {
         setDefault(FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY, false);
         setDefault(FEATURE_USE_BROOKLYN_LIVE_OBJECTS_DATAGRID_STORAGE, false);
         setDefault(FEATURE_RENAME_THREADS, false);
+        setDefault(FEATURE_INFER_CATALOG_ITEM_ON_REBIND, true);
     }
     
     static {


[18/18] incubator-brooklyn git commit: This closes #312

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


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

Branch: refs/heads/master
Commit: 6791d51d4b8e35c7e5a306ed4fb0984746b1385e
Parents: 50e3cbc cf3a9eb
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Nov 13 22:22:57 2014 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu Nov 13 22:22:57 2014 +0000

----------------------------------------------------------------------
 .../java/brooklyn/catalog/BrooklynCatalog.java  |  48 +-
 .../main/java/brooklyn/catalog/CatalogItem.java |  37 +-
 .../mementos/BrooklynMementoPersister.java      |   4 +
 .../brooklyn/mementos/CatalogItemMemento.java   |   8 +-
 .../main/java/brooklyn/basic/BrooklynTypes.java |   2 +-
 .../java/brooklyn/catalog/CatalogLoadMode.java  |   1 -
 .../brooklyn/catalog/CatalogPredicates.java     |  16 +-
 .../catalog/internal/BasicBrooklynCatalog.java  | 247 +++++++---
 .../internal/CatalogBundleConverter.java        |  63 +++
 .../catalog/internal/CatalogBundleDto.java      |  74 +++
 .../catalog/internal/CatalogClasspathDo.java    |  17 +-
 .../internal/CatalogConfigurationDto.java       |  44 --
 .../brooklyn/catalog/internal/CatalogDo.java    |  18 +-
 .../catalog/internal/CatalogItemBuilder.java    |  56 ++-
 .../catalog/internal/CatalogItemComparator.java |  54 +++
 .../catalog/internal/CatalogItemDo.java         |  20 +-
 .../internal/CatalogItemDtoAbstract.java        | 186 ++++++--
 .../catalog/internal/CatalogLibrariesDo.java    |   5 +-
 .../catalog/internal/CatalogLibrariesDto.java   |  54 +--
 .../brooklyn/catalog/internal/CatalogUtils.java |  95 +++-
 .../catalog/internal/CatalogXmlSerializer.java  |  13 +-
 .../AbstractBrooklynObjectRebindSupport.java    |   2 +-
 .../rebind/BasicCatalogItemRebindSupport.java   |   3 +-
 .../rebind/RebindContextLookupContext.java      |  31 ++
 .../entity/rebind/RebindManagerImpl.java        | 456 ++++++++++++-------
 .../rebind/dto/BasicCatalogItemMemento.java     |  46 +-
 .../entity/rebind/dto/MementosGenerators.java   |  19 +-
 .../BrooklynMementoPersisterInMemory.java       |  20 +
 .../BrooklynMementoPersisterToMultiFile.java    |  15 +-
 .../BrooklynMementoPersisterToObjectStore.java  |  71 ++-
 .../CatalogItemLibrariesConverter.java          |  68 +++
 .../rebind/persister/XmlMementoSerializer.java  |   8 +-
 .../internal/BrooklynFeatureEnablement.java     |  12 +
 ...ssLoaderFromBrooklynClassLoadingContext.java |  64 +++
 .../OsgiBrooklynClassLoadingContext.java        |   8 +-
 .../brooklyn/management/ha/OsgiManager.java     | 147 ++++--
 .../src/main/java/brooklyn/util/osgi/Osgis.java |  66 ++-
 .../brooklyn/camp/lite/CampYamlLiteTest.java    |  51 ++-
 .../catalog/internal/CatalogDtoTest.java        |  41 +-
 .../brooklyn/catalog/internal/CatalogItems.java |  87 ----
 .../catalog/internal/CatalogLoadTest.java       |  25 +-
 .../catalog/internal/CatalogScanTest.java       |  13 +-
 .../entity/rebind/RebindCatalogItemTest.java    |  11 +-
 ...talogWhenCatalogPersistenceDisabledTest.java |   2 +-
 .../entity/rebind/RebindTestFixture.java        |  18 +-
 .../persister/XmlMementoSerializerTest.java     |  35 +-
 .../management/osgi/OsgiStandaloneTest.java     |   2 +
 .../osgi/OsgiVersionMoreEntityTest.java         | 130 ++++--
 .../camp/lite/test-app-service-blueprint.yaml   |   2 +-
 .../brooklyn/catalog/internal/osgi-catalog.xml  |   4 +-
 .../BrooklynComponentTemplateResolver.java      |   3 +-
 .../BrooklynEntityDecorationResolver.java       |  12 +-
 .../spi/creation/BrooklynEntityMatcher.java     |   5 -
 .../lookup/AbstractTemplateBrooklynLookup.java  |   8 +-
 .../camp/brooklyn/AbstractYamlTest.java         |  19 +-
 .../camp/brooklyn/ReferencedYamlTest.java       |   7 +-
 .../CatalogOsgiVersionMoreEntityTest.java       |  26 +-
 .../brooklyn/catalog/CatalogYamlEntityTest.java | 277 +++++++++--
 .../brooklyn/catalog/CatalogYamlPolicyTest.java |  40 +-
 ...more-entity-v1-with-policy-osgi-catalog.yaml |   2 +-
 .../catalog/more-entity-v2-osgi-catalog.yaml    |   2 +-
 usage/camp/src/test/resources/yaml-ref-app.yaml |   2 +-
 .../src/test/resources/yaml-ref-catalog.yaml    |   2 +-
 .../src/test/resources/yaml-ref-entity.yaml     |   2 +-
 usage/jsgui/src/main/webapp/assets/css/base.css |   9 +-
 .../src/main/webapp/assets/js/model/entity.js   |  15 +
 .../src/main/webapp/assets/js/view/catalog.js   |  61 ++-
 .../assets/tpl/catalog/details-entity.html      |   8 +-
 .../webapp/assets/tpl/catalog/nav-entry.html    |   2 +-
 .../main/java/brooklyn/rest/api/CatalogApi.java |  80 +++-
 .../rest/domain/CatalogEntitySummary.java       |   7 +-
 .../rest/domain/CatalogItemSummary.java         |  48 +-
 .../rest/domain/CatalogPolicySummary.java       |   7 +-
 .../rest/resources/ApplicationResource.java     |   3 +-
 .../rest/resources/CatalogResource.java         | 125 +++--
 .../rest/transform/CatalogTransformer.java      |  24 +-
 .../rest/util/BrooklynRestResourceUtils.java    |   3 +-
 .../rest/resources/ApiDocResourceTest.java      |   2 +-
 .../rest/resources/CatalogResourceTest.java     |  85 ++--
 .../main/java/brooklyn/util/io/FileUtil.java    |   2 +
 80 files changed, 2392 insertions(+), 1015 deletions(-)
----------------------------------------------------------------------



[09/18] incubator-brooklyn git commit: Catalog versioning - groundwork for returning the latest version if no version specified

Posted by he...@apache.org.
Catalog versioning - groundwork for returning the latest version if no version specified


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

Branch: refs/heads/master
Commit: 415619a3c2e230c6c700159ad97a001b584da9d7
Parents: 5661ac4
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Mon Nov 10 18:56:39 2014 +0200
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 11:49:52 2014 +0200

----------------------------------------------------------------------
 .../java/brooklyn/catalog/BrooklynCatalog.java  |  3 +-
 .../catalog/internal/BasicBrooklynCatalog.java  | 40 +++++++++++++++++---
 .../internal/CatalogItemDtoAbstract.java        |  2 +-
 .../internal/CatalogItemVersionComparator.java  | 16 ++++++++
 .../brooklyn/catalog/internal/CatalogUtils.java | 32 +++++++++++++++-
 .../catalog/internal/CatalogDtoTest.java        |  2 +-
 .../entity/rebind/RebindTestFixture.java        |  3 +-
 .../BrooklynComponentTemplateResolver.java      |  5 +--
 .../BrooklynEntityDecorationResolver.java       |  8 ++--
 .../lookup/AbstractTemplateBrooklynLookup.java  |  9 +----
 .../camp/brooklyn/AbstractYamlTest.java         |  2 +-
 .../rest/resources/ApplicationResource.java     |  3 +-
 .../rest/resources/CatalogResource.java         | 22 ++++++-----
 .../rest/util/BrooklynRestResourceUtils.java    |  3 +-
 .../rest/resources/CatalogResourceTest.java     |  2 +-
 15 files changed, 112 insertions(+), 40 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/415619a3/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
index ce0d928..cac3038 100644
--- a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
+++ b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
@@ -24,6 +24,7 @@ import java.util.NoSuchElementException;
 import com.google.common.base.Predicate;
 
 public interface BrooklynCatalog {
+    static String DEFAULT_VERSION = "0.0.0_DEFAULT_VERSION";
 
     /** @return The item with the given {@link brooklyn.catalog.CatalogItem#getSymbolicName()
      * symbolicName}, or null if not found.
@@ -47,7 +48,7 @@ public interface BrooklynCatalog {
      * @throws NoSuchElementException if not found */
     void deleteCatalogItem(String symbolicName, String version);
 
-    /** variant of {@link #getCatalogItem(String)} which checks (and casts) type for convenience
+    /** variant of {@link #getCatalogItem(String, String)} which checks (and casts) type for convenience
      * (returns null if type does not match)
      * @deprecated since 0.7.0 use {@link #getCatalogItem(Class<T>, String, String)} */
     @Deprecated

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/415619a3/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
index 43da70f..f74e24c 100644
--- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
@@ -68,6 +68,7 @@ import com.google.common.base.Predicates;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.collect.Iterables;
 
 public class BasicBrooklynCatalog implements BrooklynCatalog {
@@ -168,7 +169,13 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     }
 
     protected CatalogItemDo<?,?> getCatalogItemDo(String symbolicName, String version) {
-        String versionedId = CatalogUtils.getVersionedId(symbolicName, version);
+        String fixedVersionId = getFixedVersionId(symbolicName, version);
+        if (fixedVersionId == null) {
+            //no items with symbolicName exist
+            return null;
+        }
+
+        String versionedId = CatalogUtils.getVersionedId(symbolicName, fixedVersionId);
         CatalogItemDo<?, ?> item = null;
         //TODO should remove "manual additions" bucket; just have one map a la osgi
         if (manualAdditionsCatalog!=null) item = manualAdditionsCatalog.getIdCache().get(versionedId);
@@ -176,10 +183,28 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         return item;
     }
     
+    private String getFixedVersionId(String symbolicName, String version) {
+        if (!DEFAULT_VERSION.equals(version)) {
+            return version;
+        } else {
+            return getDefaultVersion(symbolicName);
+        }
+    }
+
+    private String getDefaultVersion(String symbolicName) {
+        Iterable<CatalogItem<Object, Object>> versions = getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName)));
+        ImmutableSortedSet<CatalogItem<?, ?>> orderedVersions = ImmutableSortedSet.orderedBy(new CatalogItemVersionComparator()).addAll(versions).build();
+        if (!orderedVersions.isEmpty()) {
+            return orderedVersions.iterator().next().getVersion();
+        } else {
+            return null;
+        }
+    }
+
     @Override
     @Deprecated
     public CatalogItem<?,?> getCatalogItem(String id) {
-        return getCatalogItem(id, NO_VERSION);
+        return getCatalogItem(id, DEFAULT_VERSION);
     }
     
     @Override
@@ -194,6 +219,8 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     @Override
     @Deprecated
     public void deleteCatalogItem(String id) {
+        //Delete only if installed through the
+        //deprecated methods. Don't support DEFAULT_VERSION for delete.
         deleteCatalogItem(id, NO_VERSION);
     }
 
@@ -201,7 +228,10 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     public void deleteCatalogItem(String id, String version) {
         log.debug("Deleting manual catalog item from "+mgmt+": "+id + ":" + version);
         checkNotNull(id, "id");
-        checkNotNull(id, "version");
+        checkNotNull(version, "version");
+        if (DEFAULT_VERSION.equals(version)) {
+            throw new IllegalStateException("Deleting items with unspecified version (argument DEFAULT_VERSION) not supported.");
+        }
         CatalogItem<?, ?> item = getCatalogItem(id, version);
         CatalogItemDtoAbstract<?,?> itemDto = getAbstractCatalogItem(item);
         if (itemDto == null) {
@@ -224,7 +254,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     @Override
     @Deprecated
     public <T,SpecT> CatalogItem<T,SpecT> getCatalogItem(Class<T> type, String id) {
-        return getCatalogItem(type, id, NO_VERSION);
+        return getCatalogItem(type, id, DEFAULT_VERSION);
     }
     
     @SuppressWarnings("unchecked")
@@ -417,7 +447,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         symbolicName = (String) catalog.getMaybe("symbolicName").orNull();
         if (Strings.isBlank(symbolicName)) {
             symbolicName = (String) catalog.getMaybe("id").orNull();
-            if (Strings.isNonBlank(symbolicName) && symbolicName.indexOf(CatalogUtils.VERSION_DELIMITER) != -1) {
+            if (Strings.isNonBlank(symbolicName) && CatalogUtils.looksLikeVersionedId(symbolicName)) {
                 symbolicName = CatalogUtils.getIdFromVersionedId(symbolicName);
                 version = CatalogUtils.getVersionFromVersionedId(symbolicName);
             }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/415619a3/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
index f834f15..f10aee1 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
@@ -325,7 +325,7 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
                     name = null;
                     version = null;
                     url = inlineRef;
-                } else if (inlineRef.indexOf(CatalogUtils.VERSION_DELIMITER) != -1) {
+                } else if (CatalogUtils.looksLikeVersionedId(inlineRef)) {
                     //looks like a name+version ref
                     name = CatalogUtils.getIdFromVersionedId(inlineRef);
                     version = CatalogUtils.getVersionFromVersionedId(inlineRef);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/415619a3/core/src/main/java/brooklyn/catalog/internal/CatalogItemVersionComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemVersionComparator.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemVersionComparator.java
new file mode 100644
index 0000000..3e279a4
--- /dev/null
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemVersionComparator.java
@@ -0,0 +1,16 @@
+package brooklyn.catalog.internal;
+
+import java.util.Comparator;
+
+import brooklyn.catalog.CatalogItem;
+
+public class CatalogItemVersionComparator implements Comparator<CatalogItem<?, ?>> {
+
+    protected CatalogItemVersionComparator() {}
+
+    @Override
+    public int compare(CatalogItem<?, ?> o1, CatalogItem<?, ?> o2) {
+        return -o1.getVersion().compareTo(o2.getVersion());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/415619a3/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
index 25c8d8d..849075f 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
@@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory;
 
 import brooklyn.basic.BrooklynObject;
 import brooklyn.basic.BrooklynObjectInternal;
+import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.CatalogItem.CatalogBundle;
 import brooklyn.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
@@ -141,9 +142,13 @@ public class CatalogUtils {
             log.debug(message, args);
     }
 
+    public static boolean looksLikeVersionedId(String versionedId) {
+        return versionedId.indexOf(VERSION_DELIMITER) != -1;
+    }
+
     public static String getIdFromVersionedId(String versionedId) {
         if (versionedId == null) return null;
-        int versionDelimiterPos = versionedId.lastIndexOf(CatalogUtils.VERSION_DELIMITER);
+        int versionDelimiterPos = versionedId.lastIndexOf(VERSION_DELIMITER);
         if (versionDelimiterPos != -1) {
             return versionedId.substring(0, versionDelimiterPos);
         } else {
@@ -153,7 +158,7 @@ public class CatalogUtils {
 
     public static String getVersionFromVersionedId(String versionedId) {
         if (versionedId == null) return null;
-        int versionDelimiterPos = versionedId.lastIndexOf(CatalogUtils.VERSION_DELIMITER);
+        int versionDelimiterPos = versionedId.lastIndexOf(VERSION_DELIMITER);
         if (versionDelimiterPos != -1) {
             return versionedId.substring(versionDelimiterPos+1);
         } else {
@@ -165,4 +170,27 @@ public class CatalogUtils {
         return id + VERSION_DELIMITER + version;
     }
 
+    //TODO Don't really like this, but it's better to have it here than on the interface to keep the API's 
+    //surface minimal. Could instead have the interface methods accept VerionedId object and have the helpers
+    //construct it as needed.
+    public static CatalogItem<?, ?> getCatalogItemOptionalVersion(ManagementContext mgmt, String versionedId) {
+        if (looksLikeVersionedId(versionedId)) {
+            String id = getIdFromVersionedId(versionedId);
+            String version = getVersionFromVersionedId(versionedId);
+            return mgmt.getCatalog().getCatalogItem(id, version);
+        } else {
+            return mgmt.getCatalog().getCatalogItem(versionedId, BrooklynCatalog.DEFAULT_VERSION);
+        }
+    }
+
+    public static <T,SpecT> CatalogItem<T, SpecT> getCatalogItemOptionalVersion(ManagementContext mgmt, Class<T> type, String versionedId) {
+        if (looksLikeVersionedId(versionedId)) {
+            String id = getIdFromVersionedId(versionedId);
+            String version = getVersionFromVersionedId(versionedId);
+            return mgmt.getCatalog().getCatalogItem(type, id, version);
+        } else {
+            return mgmt.getCatalog().getCatalogItem(type, versionedId, BrooklynCatalog.DEFAULT_VERSION);
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/415619a3/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
index 07585b2..8ab75d5 100644
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
+++ b/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
@@ -135,7 +135,7 @@ public class CatalogDtoTest {
     public void testVersionedIdSplitter() {
         String id = "simple.id";
         String version = "0.1.2";
-        String versionedId = id + CatalogUtils.VERSION_DELIMITER + version;
+        String versionedId = CatalogUtils.getVersionedId(id, version);
         
         Assert.assertNull(CatalogUtils.getIdFromVersionedId(null));
         Assert.assertNull(CatalogUtils.getVersionFromVersionedId(null));

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/415619a3/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java b/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
index f97fb85..2b221d1 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
@@ -35,6 +35,7 @@ import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityFunctions;
 import brooklyn.entity.basic.StartableApplication;
 import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
 import brooklyn.entity.rebind.persister.FileBasedObjectStore;
@@ -53,6 +54,7 @@ import brooklyn.util.time.Duration;
 
 import com.google.api.client.util.Sets;
 import com.google.common.annotations.Beta;
+import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
@@ -181,7 +183,6 @@ public abstract class RebindTestFixture<T extends StartableApplication> {
         return rebind(checkSerializable, false);
     }
 
-    @SuppressWarnings("unchecked")
     protected T rebind(boolean checkSerializable, boolean terminateOrigManagementContext) throws Exception {
         return rebind(checkSerializable, terminateOrigManagementContext, (File)null);
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/415619a3/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
index b384d98..e641b50 100644
--- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
+++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
@@ -191,10 +191,7 @@ public class BrooklynComponentTemplateResolver {
     public CatalogItem<Entity,EntitySpec<?>> getCatalogItem() {
         String type = getBrooklynType();
         if (type != null) {
-            BrooklynCatalog catalog = loader.getManagementContext().getCatalog();
-            return catalog.getCatalogItem(Entity.class,
-                    CatalogUtils.getIdFromVersionedId(type),
-                    CatalogUtils.getVersionFromVersionedId(type));
+            return CatalogUtils.getCatalogItemOptionalVersion(loader.getManagementContext(), Entity.class,  type);
         } else {
             return null;
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/415619a3/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
index 13c6454..8fd080b 100644
--- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
+++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
@@ -122,12 +122,12 @@ public abstract class BrooklynEntityDecorationResolver<DT> {
             decorations.add(spec);
         }
         private CatalogItem<?, ?> getPolicyCatalogItem(BrooklynCatalog catalog, String policyType) {
-            String id = CatalogUtils.getIdFromVersionedId(policyType);
-            String version = CatalogUtils.getVersionFromVersionedId(policyType);
-            if (id != null) {
+            if (CatalogUtils.looksLikeVersionedId(policyType)) {
+                String id = CatalogUtils.getIdFromVersionedId(policyType);
+                String version = CatalogUtils.getVersionFromVersionedId(policyType);
                 return catalog.getCatalogItem(id, version);
             } else {
-                return catalog.getCatalogItem(policyType);
+                return catalog.getCatalogItem(policyType, BrooklynCatalog.DEFAULT_VERSION);
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/415619a3/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/lookup/AbstractTemplateBrooklynLookup.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/lookup/AbstractTemplateBrooklynLookup.java b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/lookup/AbstractTemplateBrooklynLookup.java
index c329193..54a3214 100644
--- a/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/lookup/AbstractTemplateBrooklynLookup.java
+++ b/usage/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/lookup/AbstractTemplateBrooklynLookup.java
@@ -25,6 +25,7 @@ import io.brooklyn.camp.spi.collection.ResolvableLink;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.Entity;
@@ -50,13 +51,7 @@ public abstract class AbstractTemplateBrooklynLookup<T extends AbstractResource>
     }
 
     private CatalogItem<?, ?> getCatalogItem(String versionedId) {
-        String id = CatalogUtils.getIdFromVersionedId(versionedId);
-        String version = CatalogUtils.getVersionFromVersionedId(versionedId);
-        if (id != null) {
-            return bmc.getCatalog().getCatalogItem(id, version);
-        } else {
-            return bmc.getCatalog().getCatalogItem(id);
-        }
+        return CatalogUtils.getCatalogItemOptionalVersion(bmc, versionedId);
     }
 
     public abstract T adapt(CatalogItem<?,?> item);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/415619a3/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
index fd856ca..aa1922c 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/AbstractYamlTest.java
@@ -153,7 +153,7 @@ public abstract class AbstractYamlTest {
     }
 
     protected String ver(String id) {
-        return id + CatalogUtils.VERSION_DELIMITER + TEST_VERSION;
+        return CatalogUtils.getVersionedId(id, TEST_VERSION);
     }
 
     public void forceCatalogUpdate() {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/415619a3/usage/rest-server/src/main/java/brooklyn/rest/resources/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/ApplicationResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/ApplicationResource.java
index 6b9be05..9511ebe 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/ApplicationResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/ApplicationResource.java
@@ -44,6 +44,7 @@ import org.codehaus.jackson.node.ObjectNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.Application;
 import brooklyn.entity.Entity;
 import brooklyn.entity.Group;
@@ -384,7 +385,7 @@ public class ApplicationResource extends AbstractBrooklynRestResource implements
     }
 
     private void checkEntityTypeIsValid(String type) {
-        if (brooklyn().getCatalog().getCatalogItem(type) == null) {
+        if (CatalogUtils.getCatalogItemOptionalVersion(mgmt(), type) == null) {
             try {
                 brooklyn().getCatalog().getRootClassLoader().loadClass(type);
             } catch (ClassNotFoundException e) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/415619a3/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
index c874e14..188825f 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
@@ -22,6 +22,7 @@ import java.io.InputStream;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.NoSuchElementException;
 import java.util.Set;
 
 import javax.annotation.Nullable;
@@ -33,6 +34,7 @@ import javax.ws.rs.core.Response.Status;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.CatalogPredicates;
 import brooklyn.catalog.internal.BasicBrooklynCatalog;
@@ -126,20 +128,20 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
     @Override
     @Deprecated
     public void deleteEntity(String entityId) throws Exception {
-        CatalogItem<?,?> result = brooklyn().getCatalog().getCatalogItem(entityId);
-        if (result==null) {
+        try {
+            brooklyn().getCatalog().deleteCatalogItem(entityId);
+        } catch (NoSuchElementException e) {
             throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId);
         }
-        brooklyn().getCatalog().deleteCatalogItem(entityId);
     }
 
     @Override
     public void deleteEntity(String entityId, String version) throws Exception {
-      CatalogItem<?,?> result = brooklyn().getCatalog().getCatalogItem(entityId, version);
-      if (result==null) {
-        throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId);
+      try {
+          brooklyn().getCatalog().deleteCatalogItem(entityId, version);
+      } catch (NoSuchElementException e) {
+          throw WebResourceUtils.notFound("Entity with id '%s:%s' not found", entityId, version);
       }
-      brooklyn().getCatalog().deleteCatalogItem(entityId, version);
     }
 
     @Override
@@ -160,7 +162,7 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
         //Or we could provide asEntity/asPolicy cast methods on the CataloItem doing a safety check internally
         @SuppressWarnings("unchecked")
         CatalogItem<? extends Entity,EntitySpec<?>> result =
-                (CatalogItem<? extends Entity,EntitySpec<?>>) brooklyn().getCatalog().getCatalogItem(entityId);
+                (CatalogItem<? extends Entity,EntitySpec<?>>) brooklyn().getCatalog().getCatalogItem(entityId, BrooklynCatalog.DEFAULT_VERSION);
 
         if (result==null) {
             throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId);
@@ -203,7 +205,7 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
     public CatalogItemSummary getPolicy(String policyId) {
         @SuppressWarnings("unchecked")
         CatalogItem<? extends Policy, PolicySpec<?>> result =
-                (CatalogItem<? extends Policy, PolicySpec<?>>) brooklyn().getCatalog().getCatalogItem(policyId);
+                (CatalogItem<? extends Policy, PolicySpec<?>>) brooklyn().getCatalog().getCatalogItem(policyId, BrooklynCatalog.DEFAULT_VERSION);
 
         if (result==null) {
             throw WebResourceUtils.notFound("Policy with id '%s' not found", policyId);
@@ -243,7 +245,7 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
     @Override
     @Deprecated
     public Response getIcon(String itemId) {
-        CatalogItem<?,?> result = brooklyn().getCatalog().getCatalogItem(itemId);
+        CatalogItem<?,?> result = brooklyn().getCatalog().getCatalogItem(itemId, BrooklynCatalog.DEFAULT_VERSION);
         return getCatalogItemIcon(result);
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/415619a3/usage/rest-server/src/main/java/brooklyn/rest/util/BrooklynRestResourceUtils.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/util/BrooklynRestResourceUtils.java b/usage/rest-server/src/main/java/brooklyn/rest/util/BrooklynRestResourceUtils.java
index 52f9df8..67519ec 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/util/BrooklynRestResourceUtils.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/util/BrooklynRestResourceUtils.java
@@ -41,6 +41,7 @@ import org.slf4j.LoggerFactory;
 import brooklyn.basic.BrooklynTypes;
 import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.config.ConfigKey;
 import brooklyn.enricher.Enrichers;
 import brooklyn.entity.Application;
@@ -366,7 +367,7 @@ public class BrooklynRestResourceUtils {
 
     protected Map<?, ?> getRenderingConfigurationFor(String catalogId) {
         MutableMap<Object, Object> result = MutableMap.of();
-        CatalogItem<?,?> item = mgmt.getCatalog().getCatalogItem(catalogId);
+        CatalogItem<?,?> item = CatalogUtils.getCatalogItemOptionalVersion(mgmt, catalogId);
         if (item==null) return result;
         
         result.addIfNotNull("iconUrl", item.getIconUrl());

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/415619a3/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
index a610cda..5eae763 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
@@ -265,6 +265,6 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
   }
   
   private static String ver(String id) {
-      return id + CatalogUtils.VERSION_DELIMITER + TEST_VERSION;
+      return CatalogUtils.getVersionedId(id, TEST_VERSION);
   }
 }


[07/18] incubator-brooklyn git commit: Catalog versioning - Clean up the CatalogItem interface

Posted by he...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/test/java/brooklyn/catalog/internal/CatalogLoadTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogLoadTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogLoadTest.java
index 976223e..f36d4c9 100644
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogLoadTest.java
+++ b/core/src/test/java/brooklyn/catalog/internal/CatalogLoadTest.java
@@ -56,11 +56,11 @@ public class CatalogLoadTest {
         assertEquals(template.getDisplayName(), "Entity name");
         assertEquals(template.getVersion(), "9.1.3");
         assertEquals(template.getJavaType(), "com.example.ExampleApp");
-        assertEquals(template.getLibraries().getBundles().size(), 2,
-                "Template bundles=" + Joiner.on(", ").join(template.getLibraries().getBundles()));
+        assertEquals(template.getLibraries().size(), 2,
+                "Template bundles=" + Joiner.on(", ").join(template.getLibraries()));
         
         boolean foundBundle1 = false, foundBundle2 = false;
-        for (CatalogBundle bundle : template.getLibraries().getBundles()) {
+        for (CatalogBundle bundle : template.getLibraries()) {
             if (bundle.getUrl().equals("file://path/to/bundle.jar")) {
                 foundBundle1 = true;
             }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
index a599755..91372f0 100644
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
+++ b/core/src/test/java/brooklyn/catalog/internal/CatalogScanTest.java
@@ -99,18 +99,19 @@ public class CatalogScanTest {
         loadAnnotationsOnlyCatalog();
         BrooklynCatalog c = annotsCatalog;
         
-        Iterable<CatalogItem<Object,Object>> bases = c.getCatalogItems(CatalogPredicates.name(Predicates.containsPattern("MyBaseEntity")));
+        Iterable<CatalogItem<Object,Object>> bases = c.getCatalogItems(CatalogPredicates.displayName(Predicates.containsPattern("MyBaseEntity")));
         Assert.assertEquals(Iterables.size(bases), 0, "should have been empty: "+bases);
         
-        Iterable<CatalogItem<Object,Object>> asdfjkls = c.getCatalogItems(CatalogPredicates.name(Predicates.containsPattern("__asdfjkls__shouldnotbefound")));
+        Iterable<CatalogItem<Object,Object>> asdfjkls = c.getCatalogItems(CatalogPredicates.displayName(Predicates.containsPattern("__asdfjkls__shouldnotbefound")));
         Assert.assertEquals(Iterables.size(asdfjkls), 0);
         
-        Iterable<CatalogItem<Object,Object>> silly1 = c.getCatalogItems(CatalogPredicates.name(Predicates.equalTo("MySillyAppTemplate")));
+        Iterable<CatalogItem<Object,Object>> silly1 = c.getCatalogItems(CatalogPredicates.displayName(Predicates.equalTo("MySillyAppTemplate")));
         Iterable<CatalogItem<Object,Object>> silly2 = c.getCatalogItems(CatalogPredicates.javaType(Predicates.equalTo(MySillyAppTemplate.class.getName())));
-        Assert.assertEquals(Iterables.getOnlyElement(silly1), Iterables.getOnlyElement(silly2));
+        CatalogItem<Object, Object> silly1El = Iterables.getOnlyElement(silly1);
+        Assert.assertEquals(silly1El, Iterables.getOnlyElement(silly2));
         
-        CatalogItem<Application,EntitySpec<? extends Application>> s1 = c.getCatalogItem(Application.class, silly1.iterator().next().getId());
-        Assert.assertEquals(s1, Iterables.getOnlyElement(silly1));
+        CatalogItem<Application,EntitySpec<? extends Application>> s1 = c.getCatalogItem(Application.class, silly1El.getSymbolicName(), silly1El.getVersion());
+        Assert.assertEquals(s1, silly1El);
         
         Assert.assertEquals(s1.getDescription(), "Some silly app test");
         

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
index a601bb7..8d44e3d 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogItemTest.java
@@ -150,7 +150,7 @@ public class RebindCatalogItemTest extends RebindTestFixtureWithApp {
         // persistence window. Because BrooklynMementoPersisterToObjectStore applies writes/deletes
         // asynchronously the winner is down to a race and the test might pass or fail.
         origManagementContext.getRebindManager().forcePersistNow();
-        origManagementContext.getCatalog().deleteCatalogItem(toRemove.getId(), toRemove.getVersion());
+        origManagementContext.getCatalog().deleteCatalogItem(toRemove.getSymbolicName(), toRemove.getVersion());
         assertEquals(Iterables.size(origManagementContext.getCatalog().getCatalogItems()), 0);
         rebindAndAssertCatalogsAreEqual();
         assertEquals(Iterables.size(newManagementContext.getCatalog().getCatalogItems()), 0);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
index 728126e..8a64e6c 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogWhenCatalogPersistenceDisabledTest.java
@@ -82,7 +82,7 @@ public class RebindCatalogWhenCatalogPersistenceDisabledTest extends RebindTestF
     public void testModificationsToCatalogAreNotPersistedWhenCatalogPersistenceFeatureIsDisabled() throws Exception {
         assertEquals(Iterables.size(origManagementContext.getCatalog().getCatalogItems()), 1);
         CatalogItem<Object, Object> toRemove = Iterables.getOnlyElement(origManagementContext.getCatalog().getCatalogItems());
-        origManagementContext.getCatalog().deleteCatalogItem(toRemove.getId(), toRemove.getVersion());
+        origManagementContext.getCatalog().deleteCatalogItem(toRemove.getSymbolicName(), toRemove.getVersion());
         assertEquals(Iterables.size(origManagementContext.getCatalog().getCatalogItems()), 0);
 
         rebind();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java b/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
index 2973276..f97fb85 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
@@ -249,11 +249,9 @@ public abstract class RebindTestFixture<T extends StartableApplication> {
     }
 
     private Set<String> getCatalogItemIds(Iterable<CatalogItem<Object, Object>> catalogItems) {
-        Set<String> itemIds = Sets.<String>newHashSet();
-        for (CatalogItem<?, ?> item : catalogItems) {
-            itemIds.add(item.getId() + ":" + item.getVersion());
-        }
-        return itemIds;
+        return FluentIterable.from(catalogItems)
+                .transform(EntityFunctions.id())
+                .copyInto(Sets.<String>newHashSet());
    }
 
     protected void assertCatalogItemsEqual(CatalogItem<?, ?> actual, CatalogItem<?, ?> expected) {
@@ -268,11 +266,7 @@ public abstract class RebindTestFixture<T extends StartableApplication> {
         assertEquals(actual.getCatalogItemJavaType(), expected.getCatalogItemJavaType());
         assertEquals(actual.getCatalogItemType(), expected.getCatalogItemType());
         assertEquals(actual.getSpecType(), expected.getSpecType());
-        assertEquals(actual.getRegisteredTypeName(), expected.getRegisteredTypeName());
-        if (actual.getLibraries() != null && expected.getLibraries() != null) {
-            assertEquals(actual.getLibraries().getBundles(), expected.getLibraries().getBundles());
-        } else {
-            assertEquals(actual.getLibraries(), expected.getLibraries());
-        }
+        assertEquals(actual.getSymbolicName(), expected.getSymbolicName());
+        assertEquals(actual.getLibraries(), expected.getLibraries());
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java b/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
index 37c9815..7ae59f6 100644
--- a/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
@@ -35,7 +35,7 @@ import org.testng.annotations.Test;
 
 import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.internal.CatalogItemBuilder;
-import brooklyn.catalog.internal.CatalogLibrariesDto;
+import brooklyn.catalog.internal.CatalogItemDtoAbstract;
 import brooklyn.entity.Entity;
 import brooklyn.entity.Feed;
 import brooklyn.entity.basic.Entities;
@@ -161,12 +161,12 @@ public class XmlMementoSerializerTest {
         final TestApplication app = TestApplication.Factory.newManagedInstanceForTests();
         ManagementContext managementContext = app.getManagementContext();
         try {
-            CatalogItem<?, ?> catalogItem = CatalogItemBuilder.newEntity("registeredtypename", "0.0.1")
+            CatalogItem<?, ?> catalogItem = CatalogItemBuilder.newEntity("symbolicName", "0.0.1")
                     .displayName("test catalog item")
                     .description("description")
                     .plan("yaml plan")
                     .iconUrl("iconUrl")
-                    .libraries(CatalogLibrariesDto.from(ImmutableList.of("library-url")))
+                    .libraries(CatalogItemDtoAbstract.parseLibraries(ImmutableList.of("library-url")))
                     .build();
             serializer.setLookupContext(new LookupContextImpl(managementContext,
                     ImmutableList.<Entity>of(), ImmutableList.<Location>of(), ImmutableList.<Policy>of(),

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java b/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java
index 92fdb47..84f181b 100644
--- a/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java
+++ b/core/src/test/java/brooklyn/management/osgi/OsgiVersionMoreEntityTest.java
@@ -35,7 +35,7 @@ import org.testng.annotations.Test;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.internal.CatalogEntityItemDto;
 import brooklyn.catalog.internal.CatalogItemBuilder;
-import brooklyn.catalog.internal.CatalogLibrariesDto;
+import brooklyn.catalog.internal.CatalogItemDtoAbstract;
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.Entities;
@@ -138,11 +138,11 @@ public class OsgiVersionMoreEntityTest {
         return newCatalogItemWithNameAndType(type, version, type, libraries);
     }
     static CatalogEntityItemDto newCatalogItemWithNameAndType(String symName, String version, String type, String ...libraries) {
+        @SuppressWarnings("deprecation")
         CatalogEntityItemDto c1 = CatalogItemBuilder.newEntity(symName, version)
                 .javaType(type)
-                .libraries(CatalogLibrariesDto.from(Arrays.asList(libraries)))
+                .libraries(CatalogItemDtoAbstract.parseLibraries(Arrays.asList(libraries)))
                 .build();
-        c1.setCatalogItemId(type);
         return c1;
     }
 
@@ -153,7 +153,7 @@ public class OsgiVersionMoreEntityTest {
         // not a great test as we set the ID here; but:
         // YAML test will do better;
         // and we can check that downstream items are loaded correctly
-        spec.catalogItemId(c2.getRegisteredTypeName());
+        spec.catalogItemId(c2.getId());
         Entity me = app.createAndManageChild(spec);
         return me;
     }
@@ -195,7 +195,7 @@ public class OsgiVersionMoreEntityTest {
         
         // test load and instantiate
         Entity me = addItemFromCatalog(c2);
-        Assert.assertEquals(me.getCatalogItemId(), OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY);
+        Assert.assertEquals(me.getCatalogItemId(), CatalogUtils.getVersionedId(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, TEST_VERSION));
         
         assertV1MethodCall(me);
         assertV1EffectorCall(me);
@@ -204,14 +204,14 @@ public class OsgiVersionMoreEntityTest {
         BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, c2);
         @SuppressWarnings({ "unchecked", "rawtypes" })
         Entity me2 = me.addChild(EntitySpec.create( (Class)loader.loadClass(c2.getJavaType()) ));
-        Assert.assertEquals(me2.getCatalogItemId(), OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY);
+        Assert.assertEquals(me2.getCatalogItemId(), CatalogUtils.getVersionedId(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, TEST_VERSION));
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
     protected PolicySpec<?> getPolicySpec(CatalogItem<?, ?> cp) {
         BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, cp);
         PolicySpec spec = PolicySpec.create( (Class)loader.loadClass(cp.getJavaType()) );
-        spec.catalogItemId(cp.getRegisteredTypeName());
+        spec.catalogItemId(cp.getId());
         return spec;
     }
 
@@ -237,7 +237,7 @@ public class OsgiVersionMoreEntityTest {
         Assert.assertNotNull(catalogItemId);
         // must be the actual source bundle
         Assert.assertFalse(catalogItemId.equals(me.getCatalogItemId()), "catalog item id is: "+catalogItemId);
-        Assert.assertTrue(catalogItemId.equals(OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_POLICY), "catalog item id is: "+catalogItemId);
+        Assert.assertTrue(catalogItemId.equals(CatalogUtils.getVersionedId(OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_POLICY, TEST_VERSION)), "catalog item id is: "+catalogItemId);
     }
 
     @Test
@@ -270,7 +270,7 @@ public class OsgiVersionMoreEntityTest {
         
         // test load and instantiate
         Entity me = addItemFromCatalog(c2);
-        Assert.assertEquals(me.getCatalogItemId(), OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY);
+        Assert.assertEquals(me.getCatalogItemId(), CatalogUtils.getVersionedId(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY, TEST_VERSION));
         
         assertV2MethodCall(me);
         assertV2EffectorCall(me);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
index d4b6474..016a83a 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityTest.java
@@ -45,7 +45,7 @@ public class CatalogOsgiVersionMoreEntityTest extends AbstractYamlTest {
         Entity app = createAndStartApplication("services: [ { type: 'more-entity:1.0' } ]");
         Entity moreEntity = Iterables.getOnlyElement(app.getChildren());
         
-        Assert.assertEquals(moreEntity.getCatalogItemId(), "more-entity");
+        Assert.assertEquals(moreEntity.getCatalogItemId(), "more-entity:1.0");
         OsgiVersionMoreEntityTest.assertV1EffectorCall(moreEntity);
         OsgiVersionMoreEntityTest.assertV1MethodCall(moreEntity);
     }
@@ -59,12 +59,12 @@ public class CatalogOsgiVersionMoreEntityTest extends AbstractYamlTest {
         Entity app = createAndStartApplication("services: [ { type: 'more-entity:1.0' } ]");
         Entity moreEntity = Iterables.getOnlyElement(app.getChildren());
         
-        Assert.assertEquals(moreEntity.getCatalogItemId(), "more-entity");
+        Assert.assertEquals(moreEntity.getCatalogItemId(), "more-entity:1.0");
         
         Assert.assertEquals(moreEntity.getPolicies().size(), 1, "wrong policies: "+moreEntity.getPolicies());
         Policy policy = Iterables.getOnlyElement(moreEntity.getPolicies());
         // it was loaded by yaml w ref to catalog, so should have the simple-policy catalog-id
-        Assert.assertEquals(policy.getCatalogItemId(), "simple-policy");
+        Assert.assertEquals(policy.getCatalogItemId(), "simple-policy:1.0");
     }
 
     @Test
@@ -73,20 +73,21 @@ public class CatalogOsgiVersionMoreEntityTest extends AbstractYamlTest {
         Entity app = createAndStartApplication("services: [ { type: 'more-entity:1.0' } ]");
         Entity moreEntity = Iterables.getOnlyElement(app.getChildren());
         
-        Assert.assertEquals(moreEntity.getCatalogItemId(), "more-entity");
+        Assert.assertEquals(moreEntity.getCatalogItemId(), "more-entity:1.0");
         OsgiVersionMoreEntityTest.assertV2EffectorCall(moreEntity);
         OsgiVersionMoreEntityTest.assertV2MethodCall(moreEntity);
         
         Assert.assertEquals(moreEntity.getPolicies().size(), 1, "wrong policies: "+moreEntity.getPolicies());
         Policy policy = Iterables.getOnlyElement(moreEntity.getPolicies());
         // it was loaded from the java so should have the base more-entity catalog id
-        Assert.assertEquals(policy.getCatalogItemId(), "more-entity");
+        Assert.assertEquals(policy.getCatalogItemId(), "more-entity:1.0");
     }
 
     @Test
     /** TODO this test works if we assume most recent version wins, but semantics TBC */
     public void testMoreEntityV2ThenV1GivesV1() throws Exception {
         addCatalogItem(getLocalResource("more-entity-v2-osgi-catalog.yaml"));
+        forceCatalogUpdate();
         addCatalogItem(getLocalResource("more-entity-v1-osgi-catalog.yaml"));
         Entity app = createAndStartApplication("services: [ { type: 'more-entity:1.0' } ]");
         Entity moreEntity = Iterables.getOnlyElement(app.getChildren());
@@ -101,6 +102,7 @@ public class CatalogOsgiVersionMoreEntityTest extends AbstractYamlTest {
     @Test
     public void testMoreEntityV1ThenV2GivesV2() throws Exception {
         addCatalogItem(getLocalResource("more-entity-v1-osgi-catalog.yaml"));
+        forceCatalogUpdate();
         addCatalogItem(getLocalResource("more-entity-v2-osgi-catalog.yaml"));
         Entity app = createAndStartApplication("services: [ { type: 'more-entity:1.0' } ]");
         Entity moreEntity = Iterables.getOnlyElement(app.getChildren());

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
index dce7a9b..ab6c7dd 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
@@ -44,12 +44,12 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
 
     @Test
     public void testAddCatalogItem() throws Exception {
-        String registeredTypeName = "my.catalog.app.id.load";
-        addCatalogOSGiEntity(registeredTypeName);
-        CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(registeredTypeName, TEST_VERSION);
-        assertEquals(item.getRegisteredTypeName(), registeredTypeName);
+        String symbolicName = "my.catalog.app.id.load";
+        addCatalogOSGiEntity(symbolicName);
+        CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(symbolicName, TEST_VERSION);
+        assertEquals(item.getSymbolicName(), symbolicName);
 
-        deleteCatalogEntity(registeredTypeName);
+        deleteCatalogEntity(symbolicName);
     }
 
     @Test
@@ -68,55 +68,55 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
 
     @Test
     public void testLaunchApplicationReferencingCatalog() throws Exception {
-        String registeredTypeName = "my.catalog.app.id.launch";
-        registerAndLaunchAndAssertSimpleEntity(registeredTypeName, SIMPLE_ENTITY_TYPE);
+        String symbolicName = "my.catalog.app.id.launch";
+        registerAndLaunchAndAssertSimpleEntity(symbolicName, SIMPLE_ENTITY_TYPE);
     }
 
     @Test
     public void testLaunchApplicationReferencingUnversionedCatalogFail() throws Exception {
-        String registeredTypeName = "my.catalog.app.id.fail";
-        addCatalogOSGiEntity(registeredTypeName, SIMPLE_ENTITY_TYPE);
+        String symbolicName = "my.catalog.app.id.fail";
+        addCatalogOSGiEntity(symbolicName, SIMPLE_ENTITY_TYPE);
         try {
             String yaml = "name: simple-app-yaml\n" +
                           "location: localhost\n" +
                           "services: \n" +
-                          "  - serviceType: " + ver(registeredTypeName);
+                          "  - serviceType: " + ver(symbolicName);
             try {
                 createAndStartApplication(yaml);
             } catch (UnsupportedOperationException e) {
                 assertTrue(e.getMessage().endsWith("cannot be matched"));
             }
         } finally {
-            deleteCatalogEntity(registeredTypeName);
+            deleteCatalogEntity(symbolicName);
         }
     }
 
     @Test
     public void testLaunchApplicationWithCatalogReferencingOtherCatalog() throws Exception {
-        String referencedRegisteredTypeName = "my.catalog.app.id.referenced";
-        String referrerRegisteredTypeName = "my.catalog.app.id.referring";
-        addCatalogOSGiEntity(referencedRegisteredTypeName, SIMPLE_ENTITY_TYPE);
-        addCatalogOSGiEntity(referrerRegisteredTypeName, ver(referencedRegisteredTypeName));
+        String referencedSymbolicName = "my.catalog.app.id.referenced";
+        String referrerSymbolicName = "my.catalog.app.id.referring";
+        addCatalogOSGiEntity(referencedSymbolicName, SIMPLE_ENTITY_TYPE);
+        addCatalogOSGiEntity(referrerSymbolicName, ver(referencedSymbolicName));
 
         String yaml = "name: simple-app-yaml\n" +
                       "location: localhost\n" +
                       "services: \n" +
-                      "  - serviceType: " + ver(referrerRegisteredTypeName);
+                      "  - serviceType: " + ver(referrerSymbolicName);
         Entity app = createAndStartApplication(yaml);
 
         Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
         assertEquals(simpleEntity.getEntityType().getName(), SIMPLE_ENTITY_TYPE);
 
-        deleteCatalogEntity(referencedRegisteredTypeName);
-        deleteCatalogEntity(referrerRegisteredTypeName);
+        deleteCatalogEntity(referencedSymbolicName);
+        deleteCatalogEntity(referrerSymbolicName);
     }
 
     @Test
     public void testLaunchApplicationChildWithCatalogReferencingOtherCatalog() throws Exception {
-        String referencedRegisteredTypeName = "my.catalog.app.id.child.referenced";
-        String referrerRegisteredTypeName = "my.catalog.app.id.child.referring";
-        addCatalogOSGiEntity(referencedRegisteredTypeName, SIMPLE_ENTITY_TYPE);
-        addCatalogChildOSGiEntity(referrerRegisteredTypeName, ver(referencedRegisteredTypeName));
+        String referencedSymbolicName = "my.catalog.app.id.child.referenced";
+        String referrerSymbolicName = "my.catalog.app.id.child.referring";
+        addCatalogOSGiEntity(referencedSymbolicName, SIMPLE_ENTITY_TYPE);
+        addCatalogChildOSGiEntity(referrerSymbolicName, ver(referencedSymbolicName));
 
         Entity app = createAndStartApplication(
             "name: simple-app-yaml",
@@ -124,7 +124,7 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
             "services:",
             "- serviceType: "+BasicEntity.class.getName(),
             "  brooklyn.children:",
-            "  - type: " + ver(referrerRegisteredTypeName));
+            "  - type: " + ver(referrerSymbolicName));
 
         Collection<Entity> children = app.getChildren();
         assertEquals(children.size(), 1);
@@ -139,32 +139,32 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
         Entity grandGrandChild = Iterables.getOnlyElement(grandGrandChildren);
         assertEquals(grandGrandChild.getEntityType().getName(), SIMPLE_ENTITY_TYPE);
 
-        deleteCatalogEntity(referencedRegisteredTypeName);
-        deleteCatalogEntity(referrerRegisteredTypeName);
+        deleteCatalogEntity(referencedSymbolicName);
+        deleteCatalogEntity(referrerSymbolicName);
     }
 
     @Test
     public void testLaunchApplicationWithTypeUsingJavaColonPrefix() throws Exception {
-        String registeredTypeName = SIMPLE_ENTITY_TYPE;
+        String symbolicName = SIMPLE_ENTITY_TYPE;
         String serviceName = "java:"+SIMPLE_ENTITY_TYPE;
-        registerAndLaunchAndAssertSimpleEntity(registeredTypeName, serviceName);
+        registerAndLaunchAndAssertSimpleEntity(symbolicName, serviceName);
     }
 
     @Test
     public void testLaunchApplicationLoopWithJavaTypeName() throws Exception {
-        String registeredTypeName = SIMPLE_ENTITY_TYPE;
+        String symbolicName = SIMPLE_ENTITY_TYPE;
         String serviceName = SIMPLE_ENTITY_TYPE;
-        registerAndLaunchAndAssertSimpleEntity(registeredTypeName, serviceName);
+        registerAndLaunchAndAssertSimpleEntity(symbolicName, serviceName);
     }
 
     @Test
     public void testLaunchApplicationChildLoopCatalogIdFails() throws Exception {
-        String referrerRegisteredTypeName = "my.catalog.app.id.child.referring";
+        String referrerSymbolicName = "my.catalog.app.id.child.referring";
         try {
-            addCatalogChildOSGiEntity(referrerRegisteredTypeName, ver(referrerRegisteredTypeName));
+            addCatalogChildOSGiEntity(referrerSymbolicName, ver(referrerSymbolicName));
             fail("Expected to throw IllegalStateException");
         } catch (IllegalStateException e) {
-            assertTrue(e.getMessage().contains("Could not find "+referrerRegisteredTypeName));
+            assertTrue(e.getMessage().contains("Could not find "+referrerSymbolicName));
         }
     }
 
@@ -334,28 +334,28 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
         Assert.assertNotNull(spec);
     }
 
-    private void registerAndLaunchAndAssertSimpleEntity(String registeredTypeName, String serviceType) throws Exception {
-        addCatalogOSGiEntity(registeredTypeName, serviceType);
+    private void registerAndLaunchAndAssertSimpleEntity(String symbolicName, String serviceType) throws Exception {
+        addCatalogOSGiEntity(symbolicName, serviceType);
         String yaml = "name: simple-app-yaml\n" +
                       "location: localhost\n" +
                       "services: \n" +
-                      "  - serviceType: "+ver(registeredTypeName);
+                      "  - serviceType: "+ver(symbolicName);
         Entity app = createAndStartApplication(yaml);
 
         Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
         assertEquals(simpleEntity.getEntityType().getName(), SIMPLE_ENTITY_TYPE);
 
-        deleteCatalogEntity(registeredTypeName);
+        deleteCatalogEntity(symbolicName);
     }
 
-    private void addCatalogOSGiEntity(String registeredTypeName) {
-        addCatalogOSGiEntity(registeredTypeName, SIMPLE_ENTITY_TYPE);
+    private void addCatalogOSGiEntity(String symbolicName) {
+        addCatalogOSGiEntity(symbolicName, SIMPLE_ENTITY_TYPE);
     }
 
-    private void addCatalogOSGiEntity(String registeredTypeName, String serviceType) {
+    private void addCatalogOSGiEntity(String symbolicName, String serviceType) {
         addCatalogItem(
             "brooklyn.catalog:",
-            "  id: " + registeredTypeName,
+            "  id: " + symbolicName,
             "  name: My Catalog App",
             "  description: My description",
             "  icon_url: classpath://path/to/myicon.jpg",
@@ -367,10 +367,10 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
             "- type: " + serviceType);
     }
 
-    private void addCatalogChildOSGiEntity(String registeredTypeName, String serviceType) {
+    private void addCatalogChildOSGiEntity(String symbolicName, String serviceType) {
         addCatalogItem(
             "brooklyn.catalog:",
-            "  id: " + registeredTypeName,
+            "  id: " + symbolicName,
             "  name: My Catalog App",
             "  description: My description",
             "  icon_url: classpath://path/to/myicon.jpg",

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
index 9ba5bee..8dbc3ab 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlPolicyTest.java
@@ -40,27 +40,27 @@ public class CatalogYamlPolicyTest extends AbstractYamlTest {
     public void testAddCatalogItem() throws Exception {
         assertEquals(countCatalogPolicies(), 0);
 
-        String registeredTypeName = "my.catalog.policy.id.load";
-        addCatalogOSGiPolicy(registeredTypeName, SIMPLE_POLICY_TYPE);
+        String symbolicName = "my.catalog.policy.id.load";
+        addCatalogOSGiPolicy(symbolicName, SIMPLE_POLICY_TYPE);
 
-        CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(registeredTypeName, TEST_VERSION);
-        assertEquals(item.getRegisteredTypeName(), registeredTypeName);
+        CatalogItem<?, ?> item = mgmt().getCatalog().getCatalogItem(symbolicName, TEST_VERSION);
+        assertEquals(item.getSymbolicName(), symbolicName);
         assertEquals(countCatalogPolicies(), 1);
 
-        deleteCatalogEntity(registeredTypeName);
+        deleteCatalogEntity(symbolicName);
     }
 
     @Test
     public void testLaunchApplicationReferencingPolicy() throws Exception {
-        String registeredTypeName = "my.catalog.policy.id.launch";
-        addCatalogOSGiPolicy(registeredTypeName, SIMPLE_POLICY_TYPE);
+        String symbolicName = "my.catalog.policy.id.launch";
+        addCatalogOSGiPolicy(symbolicName, SIMPLE_POLICY_TYPE);
         Entity app = createAndStartApplication(
             "name: simple-app-yaml",
             "location: localhost",
             "services: ",
             "  - type: brooklyn.entity.basic.BasicEntity\n" +
             "    brooklyn.policies:\n" +
-            "    - type: " + ver(registeredTypeName),
+            "    - type: " + ver(symbolicName),
             "      brooklyn.config:",
             "        config2: config2 override",
             "        config3: config3");
@@ -72,18 +72,18 @@ public class CatalogYamlPolicyTest extends AbstractYamlTest {
         assertEquals(policy.getConfig(new BasicConfigKey<String>(String.class, "config2")), "config2 override");
         assertEquals(policy.getConfig(new BasicConfigKey<String>(String.class, "config3")), "config3");
 
-        deleteCatalogEntity(registeredTypeName);
+        deleteCatalogEntity(symbolicName);
     }
 
     @Test
     public void testLaunchApplicationWithCatalogReferencingOtherCatalog() throws Exception {
-        String referencedRegisteredTypeName = "my.catalog.policy.id.referenced";
-        String referrerRegisteredTypeName = "my.catalog.policy.id.referring";
-        addCatalogOSGiPolicy(referencedRegisteredTypeName, SIMPLE_POLICY_TYPE);
+        String referencedSymbolicName = "my.catalog.policy.id.referenced";
+        String referrerSymbolicName = "my.catalog.policy.id.referring";
+        addCatalogOSGiPolicy(referencedSymbolicName, SIMPLE_POLICY_TYPE);
 
         addCatalogItem(
             "brooklyn.catalog:",
-            "  id: " + referrerRegisteredTypeName,
+            "  id: " + referrerSymbolicName,
             "  name: My Catalog App",
             "  description: My description",
             "  icon_url: classpath://path/to/myicon.jpg",
@@ -94,12 +94,12 @@ public class CatalogYamlPolicyTest extends AbstractYamlTest {
             "services:",
             "- type: " + SIMPLE_ENTITY_TYPE,
             "  brooklyn.policies:",
-            "  - type: " + ver(referencedRegisteredTypeName));
+            "  - type: " + ver(referencedSymbolicName));
 
         String yaml = "name: simple-app-yaml\n" +
                       "location: localhost\n" +
                       "services: \n" +
-                      "  - serviceType: "+ ver(referrerRegisteredTypeName);
+                      "  - serviceType: "+ ver(referrerSymbolicName);
 
         Entity app = createAndStartApplication(yaml);
 
@@ -107,13 +107,13 @@ public class CatalogYamlPolicyTest extends AbstractYamlTest {
         Policy policy = Iterables.getOnlyElement(simpleEntity.getPolicies());
         assertEquals(policy.getPolicyType().getName(), SIMPLE_POLICY_TYPE);
 
-        deleteCatalogEntity(referencedRegisteredTypeName);
+        deleteCatalogEntity(referencedSymbolicName);
     }
 
-    private void addCatalogOSGiPolicy(String registeredTypeName, String serviceType) {
+    private void addCatalogOSGiPolicy(String symbolicName, String serviceType) {
         addCatalogItem(
             "brooklyn.catalog:",
-            "  id: " + registeredTypeName,
+            "  id: " + symbolicName,
             "  name: My Catalog Policy",
             "  description: My description",
             "  icon_url: classpath://path/to/myicon.jpg",

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/usage/jsgui/src/main/webapp/assets/js/model/entity.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/model/entity.js b/usage/jsgui/src/main/webapp/assets/js/model/entity.js
index 547e329..18e1773 100644
--- a/usage/jsgui/src/main/webapp/assets/js/model/entity.js
+++ b/usage/jsgui/src/main/webapp/assets/js/model/entity.js
@@ -31,7 +31,7 @@ define(["underscore", "backbone"], function (_, Backbone) {
         getVersionedAttr: function(name) {
             var attr = this.get(name);
             var version = this.get('version');
-            if (version && version != '0.0.0') {
+            if (version && version != '0.0.0_SNAPSHOT') {
                 return attr + ':' + version;
             } else {
                 return attr;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/catalog.js b/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
index cb83b3b..cf14684 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
@@ -313,17 +313,18 @@ define([
             return this;
         },
 
-        singleItemTempalter: function(name, active, isChild, model, index) {
+        singleItemTemplater: function(isChild, model, index) {
             var args = _.extend({
                     cid: model.cid,
                     isChild: isChild,
-                    extraClasses: (activeDetailsView == name && model.cid == active) ? "active" : ""
+                    extraClasses: (activeDetailsView == this.name && model.cid == this.activeCid) ? "active" : ""
                 }, this.entryTemplateArgs(model));
+            console.log(args);
             return this.template(args);
         },
 
         renderEntries: function() {
-            var elements = this.collection.map(_.partial(this.singleItemTemplater, this.name, this.activeCid, false), this);
+            var elements = this.collection.map(_.partial(this.singleItemTemplater, false), this);
             this.updateContent(elements.join(''));
         },
 
@@ -370,7 +371,7 @@ define([
     
     var AccordionEntityView = AccordionItemView.extend({
         renderEntries: function() {
-            var symbolicNameFn = function(model) {return model.get("symbolicName")};
+            var symbolicNameFn = function(model) {return model.get("type")};
             var groups = this.collection.groupBy(symbolicNameFn);
             var orderedIds = _.uniq(this.collection.map(symbolicNameFn), true);
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogEntitySummary.java
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogEntitySummary.java b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogEntitySummary.java
index a0e6017..2933451 100644
--- a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogEntitySummary.java
+++ b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogEntitySummary.java
@@ -34,9 +34,7 @@ public class CatalogEntitySummary extends CatalogItemSummary {
             @JsonProperty("symbolicName") String symbolicName,
             @JsonProperty("version") String version,
             @JsonProperty("name") String name,
-            @JsonProperty("registeredType") String registeredType,
             @JsonProperty("javaType") String javaType,
-            @JsonProperty("type") String type,
             @JsonProperty("planYaml") String planYaml,
             @JsonProperty("description") String description,
             @JsonProperty("iconUrl") String iconUrl,
@@ -45,7 +43,7 @@ public class CatalogEntitySummary extends CatalogItemSummary {
             @JsonProperty("effectors") Set<EffectorSummary> effectors,
             @JsonProperty("links") Map<String, URI> links
         ) {
-        super(symbolicName, version, name, registeredType, javaType, type, planYaml, description, iconUrl, links);
+        super(symbolicName, version, name, javaType, planYaml, description, iconUrl, links);
         this.config = config;
         this.sensors = sensors;
         this.effectors = effectors;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogItemSummary.java
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogItemSummary.java b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogItemSummary.java
index 042e659..553450c 100644
--- a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogItemSummary.java
+++ b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogItemSummary.java
@@ -33,13 +33,15 @@ import com.google.common.collect.ImmutableMap;
  * see also, subclasses */
 public class CatalogItemSummary implements HasId, HasName {
 
+    private final String id;
     private final String symbolicName;
     private final String version;
-    
-    // TODO too many types, see in CatalogItem
+
+    //needed for backwards compatibility only (json serializer works on fields, not getters)
+    @Deprecated
     private final String type;
+    
     private final String javaType;
-    private final String registeredType;
     
     private final String name;
     @JsonSerialize(include=Inclusion.NON_EMPTY)
@@ -53,21 +55,19 @@ public class CatalogItemSummary implements HasId, HasName {
     public CatalogItemSummary(
             @JsonProperty("symbolicName") String symbolicName,
             @JsonProperty("version") String version,
-            @JsonProperty("name") String name,
-            @JsonProperty("registeredType") String registeredType,
+            @JsonProperty("name") String displayName,
             @JsonProperty("javaType") String javaType,
-            @JsonProperty("type") String highLevelType,
             @JsonProperty("planYaml") String planYaml,
             @JsonProperty("description") String description,
             @JsonProperty("iconUrl") String iconUrl,
             @JsonProperty("links") Map<String, URI> links
         ) {
+        this.id = symbolicName + ":" + version;
         this.symbolicName = symbolicName;
+        this.type = symbolicName;
         this.version = version;
-        this.name = name;
+        this.name = displayName;
         this.javaType = javaType;
-        this.registeredType = registeredType;
-        this.type = highLevelType;
         this.planYaml = planYaml;
         this.description = description;
         this.iconUrl = iconUrl;
@@ -76,7 +76,7 @@ public class CatalogItemSummary implements HasId, HasName {
 
     @Override
     public String getId() {
-        return symbolicName + ":" + version;
+        return id;
     }
 
     public String getSymbolicName() {
@@ -87,22 +87,18 @@ public class CatalogItemSummary implements HasId, HasName {
         return version;
     }
 
-    public String getType() {
-        return type;
-    }
-
     public String getJavaType() {
         return javaType;
     }
 
-    public String getRegisteredType() {
-        return registeredType;
+    public String getType() {
+        return type;
     }
 
     public String getPlanYaml() {
         return planYaml;
     }
-    
+
     @Override
     public String getName() {
         return name;
@@ -130,7 +126,7 @@ public class CatalogItemSummary implements HasId, HasName {
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(symbolicName, version, name, type);
+        return Objects.hashCode(symbolicName, version, name, javaType);
     }
     
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java
index 513cfa0..000bd85 100644
--- a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java
+++ b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java
@@ -34,14 +34,14 @@ public class CatalogPolicySummary extends CatalogItemSummary {
             @JsonProperty("symbolicName") String symbolicName,
             @JsonProperty("version") String version,
             @JsonProperty("name") String name,
-            @JsonProperty("type") String type,
+            @JsonProperty("javaType") String javaType,
             @JsonProperty("planYaml") String planYaml,
             @JsonProperty("description") String description,
             @JsonProperty("iconUrl") String iconUrl,
             @JsonProperty("config") Set<PolicyConfigSummary> config,
             @JsonProperty("links") Map<String, URI> links
         ) {
-        super(symbolicName, version, name, type, type, type, planYaml, description, iconUrl, links);
+        super(symbolicName, version, name, javaType, planYaml, description, iconUrl, links);
         // TODO expose config from policies
         this.config = (config == null) ? ImmutableSet.<PolicyConfigSummary>of() : config;
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
index 9621e9c..c874e14 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
@@ -112,10 +112,6 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
             return Response.created(URI.create("policies/" + itemId))
                     .entity(CatalogTransformer.catalogPolicySummary(brooklyn(), (CatalogItem<? extends Policy, PolicySpec<?>>) item))
                     .build();
-        case CONFIGURATION:
-            return Response.created(URI.create("configurations/" + itemId))
-                    .entity(CatalogTransformer.catalogEntitySummary(brooklyn(), (CatalogItem<? extends Entity, EntitySpec<?>>) item))
-                    .build();
         default:
             throw new IllegalStateException("Unsupported catalog item type "+item.getCatalogItemType()+": "+item);
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/usage/rest-server/src/main/java/brooklyn/rest/transform/CatalogTransformer.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/transform/CatalogTransformer.java b/usage/rest-server/src/main/java/brooklyn/rest/transform/CatalogTransformer.java
index 3634868..1bcdf00 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/transform/CatalogTransformer.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/transform/CatalogTransformer.java
@@ -70,27 +70,23 @@ public class CatalogTransformer {
         for (Effector<?> x: type.getEffectors())
             effectors.add(EffectorTransformer.effectorSummaryForCatalog(x));
 
-        return new CatalogEntitySummary(item.getId(), item.getVersion(), item.getDisplayName(),
-            item.getRegisteredTypeName(), item.getJavaType(), 
-            item.getRegisteredTypeName(),
-            item.getPlanYaml(),
-                item.getDescription(), tidyIconLink(b, item, item.getIconUrl()),
-                config, sensors, effectors,
-                makeLinks(item));
+        return new CatalogEntitySummary(item.getSymbolicName(), item.getVersion(), item.getDisplayName(),
+            item.getJavaType(), item.getPlanYaml(),
+            item.getDescription(), tidyIconLink(b, item, item.getIconUrl()),
+            config, sensors, effectors,
+            makeLinks(item));
     }
 
     public static CatalogItemSummary catalogItemSummary(BrooklynRestResourceUtils b, CatalogItem<?,?> item) {
-        return new CatalogItemSummary(item.getId(), item.getVersion(), item.getDisplayName(), 
-                item.getRegisteredTypeName(), item.getJavaType(), 
-                item.getRegisteredTypeName(),
-                item.getPlanYaml(),
+        return new CatalogItemSummary(item.getSymbolicName(), item.getVersion(), item.getDisplayName(),
+                item.getJavaType(), item.getPlanYaml(),
                 item.getDescription(), tidyIconLink(b, item, item.getIconUrl()), makeLinks(item));
     }
 
     public static CatalogPolicySummary catalogPolicySummary(BrooklynRestResourceUtils b, CatalogItem<? extends Policy,PolicySpec<?>> item) {
         Set<PolicyConfigSummary> config = ImmutableSet.of();
-        return new CatalogPolicySummary(item.getId(), item.getVersion(), item.getDisplayName(), item.getRegisteredTypeName(),
-                item.getPlanYaml(),
+        return new CatalogPolicySummary(item.getSymbolicName(), item.getVersion(), item.getDisplayName(),
+                item.getJavaType(), item.getPlanYaml(),
                 item.getDescription(), tidyIconLink(b, item, item.getIconUrl()), config,
                 makeLinks(item));
     }
@@ -101,7 +97,7 @@ public class CatalogTransformer {
     
     private static String tidyIconLink(BrooklynRestResourceUtils b, CatalogItem<?,?> item, String iconUrl) {
         if (b.isUrlServerSideAndSafe(iconUrl))
-            return "/v1/catalog/icon/"+item.getId();
+            return "/v1/catalog/icon/"+item.getSymbolicName() + "/" + item.getVersion();
         return iconUrl;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
index 8e33ebb..a610cda 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
@@ -74,11 +74,11 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
   @Test
   /** based on CampYamlLiteTest */
   public void testRegisterCustomEntityWithBundleWhereEntityIsFromCoreAndIconFromBundle() {
-    String registeredTypeName = "my.catalog.entity.id";
+    String symbolicName = "my.catalog.entity.id";
     String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
     String yaml =
         "brooklyn.catalog:\n"+
-        "  id: " + registeredTypeName + "\n"+
+        "  id: " + symbolicName + "\n"+
         "  name: My Catalog App\n"+
         "  description: My description\n"+
         "  icon_url: classpath:/brooklyn/osgi/tests/icon.gif\n"+
@@ -94,44 +94,42 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
 
     assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode());
 
-    CatalogEntitySummary entityItem = client().resource("/v1/catalog/entities/"+registeredTypeName + "/" + TEST_VERSION)
+    CatalogEntitySummary entityItem = client().resource("/v1/catalog/entities/"+symbolicName + "/" + TEST_VERSION)
             .get(CatalogEntitySummary.class);
 
-    assertEquals(entityItem.getRegisteredType(), registeredTypeName);
-    
     Assert.assertNotNull(entityItem.getPlanYaml());
     Assert.assertTrue(entityItem.getPlanYaml().contains("brooklyn.test.entity.TestEntity"));
     
-    assertEquals(entityItem.getId(), ver(registeredTypeName));
-    assertEquals(entityItem.getSymbolicName(), registeredTypeName);
+    assertEquals(entityItem.getId(), ver(symbolicName));
+    assertEquals(entityItem.getSymbolicName(), symbolicName);
     assertEquals(entityItem.getVersion(), TEST_VERSION);
     
     // and internally let's check we have libraries
-    CatalogItem<?, ?> item = getManagementContext().getCatalog().getCatalogItem(registeredTypeName, TEST_VERSION);
+    CatalogItem<?, ?> item = getManagementContext().getCatalog().getCatalogItem(symbolicName, TEST_VERSION);
     Assert.assertNotNull(item);
-    Collection<CatalogBundle> libs = item.getLibraries().getBundles();
+    Collection<CatalogBundle> libs = item.getLibraries();
     assertEquals(libs.size(), 1);
     assertEquals(Iterables.getOnlyElement(libs).getUrl(), bundleUrl);
 
     // now let's check other things on the item
     assertEquals(entityItem.getName(), "My Catalog App");
     assertEquals(entityItem.getDescription(), "My description");
-    assertEquals(entityItem.getIconUrl(), "/v1/catalog/icon/" + registeredTypeName);
+    assertEquals(entityItem.getIconUrl(), "/v1/catalog/icon/" + symbolicName + "/" + entityItem.getVersion());
     assertEquals(item.getIconUrl(), "classpath:/brooklyn/osgi/tests/icon.gif");
     
-    byte[] iconData = client().resource("/v1/catalog/icon/"+registeredTypeName + "/" + TEST_VERSION).get(byte[].class);
+    byte[] iconData = client().resource("/v1/catalog/icon/"+symbolicName + "/" + TEST_VERSION).get(byte[].class);
     assertEquals(iconData.length, 43);
   }
 
   @Test
   public void testRegisterOSGiPolicy() {
-    String registeredTypeName = "my.catalog.policy.id";
+    String symbolicName = "my.catalog.policy.id";
     String policyType = "brooklyn.osgi.tests.SimplePolicy";
     String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
 
     String yaml =
         "brooklyn.catalog:\n"+
-        "  id: " + registeredTypeName + "\n"+
+        "  id: " + symbolicName + "\n"+
         "  name: My Catalog App\n"+
         "  description: My description\n"+
         "  version: " + TEST_VERSION + "\n" +
@@ -144,11 +142,10 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
     CatalogPolicySummary entityItem = client().resource("/v1/catalog")
         .post(CatalogPolicySummary.class, yaml);
 
-    assertEquals(entityItem.getRegisteredType(), registeredTypeName);
     Assert.assertNotNull(entityItem.getPlanYaml());
     Assert.assertTrue(entityItem.getPlanYaml().contains(policyType));
-    assertEquals(entityItem.getId(), ver(registeredTypeName));
-    assertEquals(entityItem.getSymbolicName(), registeredTypeName);
+    assertEquals(entityItem.getId(), ver(symbolicName));
+    assertEquals(entityItem.getSymbolicName(), symbolicName);
     assertEquals(entityItem.getVersion(), TEST_VERSION);
   }
 
@@ -241,12 +238,12 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
   
   @Test
   public void testDeleteCustomEntityFromCatalog() {
-    String registeredTypeName = "my.catalog.app.id.to.subsequently.delete";
+    String symbolicName = "my.catalog.app.id.to.subsequently.delete";
     String yaml =
-        "name: "+registeredTypeName+"\n"+
+        "name: "+symbolicName+"\n"+
         // FIXME name above should be unnecessary when brooklyn.catalog below is working
         "brooklyn.catalog:\n"+
-        "  id: " + registeredTypeName + "\n"+
+        "  id: " + symbolicName + "\n"+
         "  name: My Catalog App To Be Deleted\n"+
         "  description: My description\n"+
         "  version: " + TEST_VERSION + "\n"+
@@ -257,12 +254,12 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
     client().resource("/v1/catalog")
             .post(ClientResponse.class, yaml);
     
-    ClientResponse deleteResponse = client().resource("/v1/catalog/entities/"+registeredTypeName+"/"+TEST_VERSION)
+    ClientResponse deleteResponse = client().resource("/v1/catalog/entities/"+symbolicName+"/"+TEST_VERSION)
             .delete(ClientResponse.class);
 
     assertEquals(deleteResponse.getStatus(), Response.Status.NO_CONTENT.getStatusCode());
 
-    ClientResponse getPostDeleteResponse = client().resource("/v1/catalog/entities/"+registeredTypeName+"/"+TEST_VERSION)
+    ClientResponse getPostDeleteResponse = client().resource("/v1/catalog/entities/"+symbolicName+"/"+TEST_VERSION)
             .get(ClientResponse.class);
     assertEquals(getPostDeleteResponse.getStatus(), Response.Status.NOT_FOUND.getStatusCode());
   }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/utils/common/src/main/java/brooklyn/util/io/FileUtil.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/io/FileUtil.java b/utils/common/src/main/java/brooklyn/util/io/FileUtil.java
index 6f816bc..aed8450 100644
--- a/utils/common/src/main/java/brooklyn/util/io/FileUtil.java
+++ b/utils/common/src/main/java/brooklyn/util/io/FileUtil.java
@@ -26,6 +26,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.List;
+import java.util.regex.Pattern;
 
 import org.apache.commons.io.FileUtils;
 import org.slf4j.Logger;
@@ -44,6 +45,11 @@ public class FileUtil {
 
     private static final Logger LOG = LoggerFactory.getLogger(FileUtil.class);
 
+    //Linux allows any characters except /
+    //Windows reserves the following set: < > : " / \ | ? *
+    //Object stores: ???, better be conservative
+    private static final Pattern FILE_NAME_BLACKLIST_CHARACTERS = Pattern.compile("[^\\w\\d \\-_.()\\[\\]$!]");
+
     private static boolean loggedSetFilePermissionsWarning = false;
     
     // When we move to java 7, we can use Files.setPosixFilePermissions
@@ -197,4 +203,8 @@ public class FileUtil {
             Streams.closeQuietly(errgobbler);
         }
     }
+
+    public static String getSafeFileName(String str) {
+        return FILE_NAME_BLACKLIST_CHARACTERS.matcher(str).replaceAll("_");
+    }
 }


[08/18] incubator-brooklyn git commit: Catalog versioning - Clean up the CatalogItem interface

Posted by he...@apache.org.
Catalog versioning - Clean up the CatalogItem interface

  * override id to return getCatalogItemId()
  * introduce symbolicName (analogous to old id/registeredTypeName)
  * deprecate registeredTypeName in favor of symbolicName
  * getCatalogItemId() should return symbolicName:version
  * deprecate useless CatalogLibraries interface
  * keep backwards compatibility for deserialization (registeredTypeName, libraries)
  * remove CONGIGURATION catalog item type

Clean up the CatalogItemSymmary REST interface and udate JSGUI to match

  * add symbolicName
  * remove registeredTypeName
  * keep id, but construct from other arguments
  * keep type, but alias to symbolicName


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

Branch: refs/heads/master
Commit: 5661ac492962a6a2b4b672b28d19a6698d26734b
Parents: 8d3a8c9
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Mon Nov 10 17:27:56 2014 +0200
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 11:49:51 2014 +0200

----------------------------------------------------------------------
 .../java/brooklyn/catalog/BrooklynCatalog.java  |  26 +--
 .../main/java/brooklyn/catalog/CatalogItem.java |  20 +-
 .../brooklyn/mementos/CatalogItemMemento.java   |   8 +-
 .../java/brooklyn/catalog/CatalogLoadMode.java  |   1 -
 .../brooklyn/catalog/CatalogPredicates.java     |  16 +-
 .../catalog/internal/BasicBrooklynCatalog.java  |  83 ++++----
 .../catalog/internal/CatalogBundleDto.java      |   8 +
 .../catalog/internal/CatalogClasspathDo.java    |  17 +-
 .../internal/CatalogConfigurationDto.java       |  44 -----
 .../brooklyn/catalog/internal/CatalogDo.java    |  36 +---
 .../catalog/internal/CatalogItemBuilder.java    |  46 +++--
 .../catalog/internal/CatalogItemDo.java         |  14 +-
 .../internal/CatalogItemDtoAbstract.java        | 189 ++++++++++++++-----
 .../catalog/internal/CatalogItemId.java         |  67 -------
 .../catalog/internal/CatalogLibrariesDo.java    |   1 +
 .../catalog/internal/CatalogLibrariesDto.java   |  70 +------
 .../brooklyn/catalog/internal/CatalogUtils.java |  32 ++--
 .../catalog/internal/CatalogXmlSerializer.java  |   5 +-
 .../rebind/BasicCatalogItemRebindSupport.java   |   5 +-
 .../rebind/dto/BasicCatalogItemMemento.java     |  55 ++++--
 .../entity/rebind/dto/MementosGenerators.java   |  19 +-
 .../BrooklynMementoPersisterToMultiFile.java    |  15 +-
 .../BrooklynMementoPersisterToObjectStore.java  |   4 +-
 .../brooklyn/camp/lite/CampYamlLiteTest.java    |  32 ++--
 .../catalog/internal/CatalogDtoTest.java        |  12 +-
 .../catalog/internal/CatalogLoadTest.java       |   6 +-
 .../catalog/internal/CatalogScanTest.java       |  13 +-
 .../entity/rebind/RebindCatalogItemTest.java    |   2 +-
 ...talogWhenCatalogPersistenceDisabledTest.java |   2 +-
 .../entity/rebind/RebindTestFixture.java        |  16 +-
 .../persister/XmlMementoSerializerTest.java     |   6 +-
 .../osgi/OsgiVersionMoreEntityTest.java         |  18 +-
 .../CatalogOsgiVersionMoreEntityTest.java       |  12 +-
 .../brooklyn/catalog/CatalogYamlEntityTest.java |  84 ++++-----
 .../brooklyn/catalog/CatalogYamlPolicyTest.java |  36 ++--
 .../src/main/webapp/assets/js/model/entity.js   |   2 +-
 .../src/main/webapp/assets/js/view/catalog.js   |   9 +-
 .../rest/domain/CatalogEntitySummary.java       |   4 +-
 .../rest/domain/CatalogItemSummary.java         |  32 ++--
 .../rest/domain/CatalogPolicySummary.java       |   4 +-
 .../rest/resources/CatalogResource.java         |   4 -
 .../rest/transform/CatalogTransformer.java      |  24 +--
 .../rest/resources/CatalogResourceTest.java     |  39 ++--
 .../main/java/brooklyn/util/io/FileUtil.java    |  10 +
 44 files changed, 546 insertions(+), 602 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
index 5b0a5cc..ce0d928 100644
--- a/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
+++ b/api/src/main/java/brooklyn/catalog/BrooklynCatalog.java
@@ -25,35 +25,37 @@ import com.google.common.base.Predicate;
 
 public interface BrooklynCatalog {
 
-    /** @return The item with the given ID or {@link brooklyn.catalog.CatalogItem#getRegisteredTypeName()
-     * registeredTypeName}, or null if not found.
+    /** @return The item with the given {@link brooklyn.catalog.CatalogItem#getSymbolicName()
+     * symbolicName}, or null if not found.
      * @deprecated since 0.7.0 use {@link #getCatalogItem(String, String)} */
     @Deprecated
-    CatalogItem<?,?> getCatalogItem(String idOrRegisteredTypeName);
+    CatalogItem<?,?> getCatalogItem(String symbolicName);
 
-    /** @return The item with the given ID or {@link brooklyn.catalog.CatalogItem#getRegisteredTypeName()
-     * registeredTypeName}, or null if not found. */
-    CatalogItem<?,?> getCatalogItem(String idOrRegisteredTypeName, String version);
+    /** @return The item with the given {@link brooklyn.catalog.CatalogItem#getSymbolicName()
+     * symbolicName}, or null if not found. */
+    CatalogItem<?,?> getCatalogItem(String symbolicName, String version);
 
-    /** @return Deletes the item with the given ID
+    /** @return Deletes the item with the given
+     *  {@link brooklyn.catalog.CatalogItem#getSymbolicName() symbolicName}
      * @throws NoSuchElementException if not found
      * @deprecated since 0.7.0 use {@link #deleteCatalogItem(String, String)} */
     @Deprecated
-    void deleteCatalogItem(String id);
+    void deleteCatalogItem(String symbolicName);
 
-    /** @return Deletes the item with the given ID and version
+    /** @return Deletes the item with the given {@link brooklyn.catalog.CatalogItem#getSymbolicName()
+     * symbolicName} and version
      * @throws NoSuchElementException if not found */
-    void deleteCatalogItem(String id, String version);
+    void deleteCatalogItem(String symbolicName, String version);
 
     /** variant of {@link #getCatalogItem(String)} which checks (and casts) type for convenience
      * (returns null if type does not match)
      * @deprecated since 0.7.0 use {@link #getCatalogItem(Class<T>, String, String)} */
     @Deprecated
-    <T,SpecT> CatalogItem<T,SpecT> getCatalogItem(Class<T> type, String id);
+    <T,SpecT> CatalogItem<T,SpecT> getCatalogItem(Class<T> type, String symbolicName);
 
     /** variant of {@link #getCatalogItem(String, String)} which checks (and casts) type for convenience
      * (returns null if type does not match) */
-    <T,SpecT> CatalogItem<T,SpecT> getCatalogItem(Class<T> type, String id, String version);
+    <T,SpecT> CatalogItem<T,SpecT> getCatalogItem(Class<T> type, String symbolicName, String version);
 
     /** @return All items in the catalog */
     <T,SpecT> Iterable<CatalogItem<T,SpecT>> getCatalogItems();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/api/src/main/java/brooklyn/catalog/CatalogItem.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/catalog/CatalogItem.java b/api/src/main/java/brooklyn/catalog/CatalogItem.java
index 1f67ccd..c58dcd3 100644
--- a/api/src/main/java/brooklyn/catalog/CatalogItem.java
+++ b/api/src/main/java/brooklyn/catalog/CatalogItem.java
@@ -20,7 +20,6 @@ package brooklyn.catalog;
 
 import java.util.Collection;
 
-import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 import brooklyn.basic.BrooklynObject;
@@ -34,7 +33,7 @@ import com.google.common.annotations.Beta;
 public interface CatalogItem<T,SpecT> extends BrooklynObject, Rebindable {
     
     public static enum CatalogItemType {
-        TEMPLATE, ENTITY, POLICY, CONFIGURATION
+        TEMPLATE, ENTITY, POLICY
     }
     
     public static interface CatalogBundle {
@@ -46,7 +45,7 @@ public interface CatalogItem<T,SpecT> extends BrooklynObject, Rebindable {
         public boolean isNamed();
     }
 
-    @Beta
+    @Deprecated
     public static interface CatalogItemLibraries {
         Collection<CatalogBundle> getBundles();
     }
@@ -59,25 +58,22 @@ public interface CatalogItem<T,SpecT> extends BrooklynObject, Rebindable {
     /** @return The type of the spec e.g. EntitySpec corresponding to {@link #getCatalogItemJavaType()} */
     public Class<SpecT> getSpecType();
     
-    /** @return The type name registered in the catalog for this item */
-    @Nonnull
-    public String getRegisteredTypeName();
-    
     /** @return The underlying java type of the item represented, or null if not known (e.g. if it comes from yaml) */
-    // TODO references to this should probably query getRegisteredType
     @Nullable public String getJavaType();
 
     /** @deprecated since 0.7.0. Use {@link #getDisplayName} */
     @Deprecated
     public String getName();
 
-    public String getDescription();
+    @Nullable public String getDescription();
+
+    @Nullable public String getIconUrl();
 
-    public String getIconUrl();
+    public String getSymbolicName();
 
     public String getVersion();
 
-    public CatalogItemLibraries getLibraries();
+    public Collection<CatalogBundle> getLibraries();
 
     public String toXmlString();
 
@@ -87,7 +83,7 @@ public interface CatalogItem<T,SpecT> extends BrooklynObject, Rebindable {
     @Override
     RebindSupport<CatalogItemMemento> getRebindSupport();
     
-    /** Built up from {@link #getId()} and {@link #getVersion()}.
+    /** Built up from {@link #getSymbolicName()} and {@link #getVersion()}.
      * 
      * (It is a bit self-referential having this method on this type of {@link BrooklynObject},
      * but it is easier this than making the interface hierarchy more complicated.) */

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/api/src/main/java/brooklyn/mementos/CatalogItemMemento.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/mementos/CatalogItemMemento.java b/api/src/main/java/brooklyn/mementos/CatalogItemMemento.java
index 9140d24..d3163e5 100644
--- a/api/src/main/java/brooklyn/mementos/CatalogItemMemento.java
+++ b/api/src/main/java/brooklyn/mementos/CatalogItemMemento.java
@@ -18,7 +18,7 @@
  */
 package brooklyn.mementos;
 
-import com.google.common.annotations.Beta;
+import java.util.Collection;
 
 import brooklyn.catalog.CatalogItem;
 
@@ -26,7 +26,7 @@ public interface CatalogItemMemento extends Memento {
 
     String getDescription();
 
-    String getRegisteredTypeName();
+    String getSymbolicName();
 
     String getIconUrl();
 
@@ -36,9 +36,7 @@ public interface CatalogItemMemento extends Memento {
 
     String getJavaType();
 
-    // necessary or part of plan yaml? Necessary for now, but .. ?
-    @Beta
-    CatalogItem.CatalogItemLibraries getLibraries();
+    Collection<CatalogItem.CatalogBundle> getBundles();
 
     CatalogItem.CatalogItemType getCatalogItemType();
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/catalog/CatalogLoadMode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/CatalogLoadMode.java b/core/src/main/java/brooklyn/catalog/CatalogLoadMode.java
index 9ea449a..259f545 100644
--- a/core/src/main/java/brooklyn/catalog/CatalogLoadMode.java
+++ b/core/src/main/java/brooklyn/catalog/CatalogLoadMode.java
@@ -18,7 +18,6 @@
  */
 package brooklyn.catalog;
 
-import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.rebind.persister.PersistMode;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/catalog/CatalogPredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/CatalogPredicates.java b/core/src/main/java/brooklyn/catalog/CatalogPredicates.java
index 5f9499a..25b943e 100644
--- a/core/src/main/java/brooklyn/catalog/CatalogPredicates.java
+++ b/core/src/main/java/brooklyn/catalog/CatalogPredicates.java
@@ -56,20 +56,30 @@ public class CatalogPredicates {
         }
     };
 
+    @Deprecated
     public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> name(final Predicate<? super String> filter) {
+        return displayName(filter);
+    }
+
+    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> displayName(final Predicate<? super String> filter) {
         return new Predicate<CatalogItem<T,SpecT>>() {
             @Override
             public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
-                return (item != null) && filter.apply(item.getName());
+                return (item != null) && filter.apply(item.getDisplayName());
             }
         };
     }
 
-    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> registeredType(final Predicate<? super String> filter) {
+    @Deprecated
+    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> registeredTypeName(final Predicate<? super String> filter) {
+        return symbolicName(filter);
+    }
+
+    public static <T,SpecT> Predicate<CatalogItem<T,SpecT>> symbolicName(final Predicate<? super String> filter) {
         return new Predicate<CatalogItem<T,SpecT>>() {
             @Override
             public boolean apply(@Nullable CatalogItem<T,SpecT> item) {
-                return (item != null) && filter.apply(item.getRegisteredTypeName());
+                return (item != null) && filter.apply(item.getSymbolicName());
             }
         };
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
index e560ae2..43da70f 100644
--- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
@@ -27,7 +27,7 @@ import io.brooklyn.camp.spi.pdp.Service;
 
 import java.io.FileNotFoundException;
 import java.util.Collection;
-import java.util.List;
+import java.util.Collections;
 import java.util.Map;
 import java.util.NoSuchElementException;
 
@@ -40,6 +40,7 @@ import brooklyn.basic.AbstractBrooklynObjectSpec;
 import brooklyn.camp.brooklyn.api.AssemblyTemplateSpecInstantiator;
 import brooklyn.catalog.BrooklynCatalog;
 import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.CatalogItem.CatalogBundle;
 import brooklyn.catalog.CatalogPredicates;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.entity.proxying.EntitySpec;
@@ -51,7 +52,6 @@ import brooklyn.policy.Policy;
 import brooklyn.policy.PolicySpec;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.FlagUtils;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.javalang.AggregateClassLoader;
 import brooklyn.util.javalang.LoadedClassLoader;
@@ -167,15 +167,12 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         return catalog;
     }
 
-    protected CatalogItemDo<?,?> getCatalogItemDo(String idOrRegisteredTypeName, String version) {
-        CatalogItemId versionedId = new CatalogItemId(idOrRegisteredTypeName, version);
+    protected CatalogItemDo<?,?> getCatalogItemDo(String symbolicName, String version) {
+        String versionedId = CatalogUtils.getVersionedId(symbolicName, version);
         CatalogItemDo<?, ?> item = null;
-        // TODO really need to remove redundancy of id v registered type;
-        // should also remove "manual additions" bucket; just have one map a la osgi
+        //TODO should remove "manual additions" bucket; just have one map a la osgi
         if (manualAdditionsCatalog!=null) item = manualAdditionsCatalog.getIdCache().get(versionedId);
         if (item == null) item = catalog.getIdCache().get(versionedId);
-        if (item == null && manualAdditionsCatalog!=null) item = manualAdditionsCatalog.getRegisteredTypeNameCache().get(versionedId);
-        if (item == null) item = catalog.getRegisteredTypeNameCache().get(versionedId);
         return item;
     }
     
@@ -186,10 +183,10 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     }
     
     @Override
-    public CatalogItem<?,?> getCatalogItem(String idOrRegisteredTypeName, String version) {
-        if (idOrRegisteredTypeName == null) return null;
+    public CatalogItem<?,?> getCatalogItem(String symbolicName, String version) {
+        if (symbolicName == null) return null;
         checkNotNull(version, "version");
-        CatalogItemDo<?, ?> itemDo = getCatalogItemDo(idOrRegisteredTypeName, version);
+        CatalogItemDo<?, ?> itemDo = getCatalogItemDo(symbolicName, version);
         if (itemDo == null) return null;
         return itemDo.getDto();
     }
@@ -260,7 +257,8 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     @SuppressWarnings("unchecked")
     @Override
     public <T, SpecT> SpecT createSpec(CatalogItem<T, SpecT> item) {
-        CatalogItemDo<T,SpecT> loadedItem = (CatalogItemDo<T, SpecT>) getCatalogItemDo(item.getId(), item.getVersion());
+        CatalogItemDo<T,SpecT> loadedItem = (CatalogItemDo<T, SpecT>) getCatalogItemDo(item.getSymbolicName(), item.getVersion());
+        if (loadedItem == null) return null;
         Class<SpecT> specType = loadedItem.getSpecType();
         if (specType==null) return null;
 
@@ -364,7 +362,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         if (log.isDebugEnabled())
             log.debug("Loading class for catalog item " + item);
         checkNotNull(item);
-        CatalogItemDo<?,?> loadedItem = getCatalogItemDo(item.getId(), item.getVersion());
+        CatalogItemDo<?,?> loadedItem = getCatalogItemDo(item.getSymbolicName(), item.getVersion());
         if (loadedItem==null) throw new NoSuchElementException("Unable to load '"+item.getId()+"' to instantiate it");
         return (Class<? extends T>) loadedItem.getJavaClass();
     }
@@ -394,7 +392,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     private CatalogItemDtoAbstract<?,?> getAbstractCatalogItem(String yaml) {
         DeploymentPlan plan = makePlanFromYaml(yaml);
 
-        CatalogLibrariesDto libraries = null;
+        Collection<CatalogBundle> libraries = Collections.emptyList();
 
         @SuppressWarnings("rawtypes")
         Maybe<Map> possibleCatalog = plan.getCustomAttribute("brooklyn.catalog", Map.class, true);
@@ -408,47 +406,60 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         Maybe<Object> possibleLibraries = catalog.getMaybe("libraries");
         if (possibleLibraries.isAbsent()) possibleLibraries = catalog.getMaybe("brooklyn.libraries");
         if (possibleLibraries.isPresentAndNonNull()) {
-            if (!(possibleLibraries.get() instanceof List))
+            if (!(possibleLibraries.get() instanceof Collection))
                 throw new IllegalArgumentException("Libraries should be a list, not "+possibleLibraries.get());
-            libraries = CatalogLibrariesDto.from((List<?>) possibleLibraries.get());
+            libraries = CatalogItemDtoAbstract.parseLibraries((Collection<?>) possibleLibraries.get());
         }
 
-        // TODO clear up the abundance of id, name, registered type, java type
-        String registeredTypeName = (String) catalog.getMaybe("id").orNull();
-        if (Strings.isBlank(registeredTypeName))
-            registeredTypeName = (String) catalog.getMaybe("name").orNull();
+        String symbolicName;
+        String version = null;
+
+        symbolicName = (String) catalog.getMaybe("symbolicName").orNull();
+        if (Strings.isBlank(symbolicName)) {
+            symbolicName = (String) catalog.getMaybe("id").orNull();
+            if (Strings.isNonBlank(symbolicName) && symbolicName.indexOf(CatalogUtils.VERSION_DELIMITER) != -1) {
+                symbolicName = CatalogUtils.getIdFromVersionedId(symbolicName);
+                version = CatalogUtils.getVersionFromVersionedId(symbolicName);
+            }
+        }
+        if (Strings.isBlank(symbolicName))
+            symbolicName = (String) catalog.getMaybe("name").orNull();
         // take name from plan if not specified in brooklyn.catalog section not supplied
-        if (Strings.isBlank(registeredTypeName)) {
-            registeredTypeName = plan.getName();
-            if (Strings.isBlank(registeredTypeName)) {
+        if (Strings.isBlank(symbolicName)) {
+            symbolicName = plan.getName();
+            if (Strings.isBlank(symbolicName)) {
                 if (plan.getServices().size()==1) {
                     Service svc = Iterables.getOnlyElement(plan.getServices());
-                    registeredTypeName = svc.getServiceType();
+                    symbolicName = svc.getServiceType();
                 }
             }
         }
 
         Maybe<Object> possibleVersion = catalog.getMaybe("version");
-        String version;
-        if (possibleVersion.isAbsent()) {
+        if (possibleVersion.isAbsent() && Strings.isBlank(version)) {
             throw new IllegalArgumentException("'version' attribute missing in 'brooklyn.catalog' section.");
+        } else if (possibleVersion.isPresent()) {
+            if (Strings.isNonBlank(version)) {
+                throw new IllegalArgumentException("Can't use both attribute 'version' and versioned id");
+            }
+            //could be coalesced to a number - can be one of Integer, Double, String
+            version = possibleVersion.get().toString();
         }
 
-        //could be coalesced to a number - can be one of Integer, Double, String
-        version = possibleVersion.get().toString();
-
         CatalogUtils.installLibraries(mgmt, libraries);
 
-        AbstractBrooklynObjectSpec<?, ?> spec = createSpec(plan, CatalogUtils.newClassLoadingContext(mgmt, "<not created yet>", libraries, getRootClassLoader()));
+        AbstractBrooklynObjectSpec<?, ?> spec = createSpec(plan, CatalogUtils.newClassLoadingContext(mgmt, CatalogUtils.getVersionedId(symbolicName, version), libraries, getRootClassLoader()));
 
-        CatalogItemBuilder<?> builder = createItemBuilder(spec, registeredTypeName, version)
+        CatalogItemBuilder<?> builder = createItemBuilder(spec, symbolicName, version)
             .libraries(libraries)
             .displayName(plan.getName())
             .description(plan.getDescription())
             .plan(yaml);
 
         // and populate other fields
-        Maybe<Object> name = catalog.getMaybe("name");
+        Maybe<Object> name = catalog.getMaybe("displayName");
+        if (name.isAbsent()) name = catalog.getMaybe("name");
+        if (name.isAbsent()) name = Maybe.<Object>fromNullable(plan.getName());
         if (name.isPresent()) builder.displayName((String) name.get());
 
         Maybe<Object> description = catalog.getMaybe("description");
@@ -460,12 +471,6 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
 
         CatalogItemDtoAbstract<?, ?> dto = builder.build();
         // Overwrite generated ID
-        if (catalog.getMaybe("id").isPresent()) {
-            String id = (String) catalog.getMaybe("id").get();
-            log.info("Overwriting id {} with id from yaml: {}", dto.getId(), id);
-            FlagUtils.setFieldsFromFlags(MutableMap.of("id", id), dto);
-        }
-        // TODO: Necessary?
         dto.setManagementContext((ManagementContextInternal) mgmt);
         return dto;
     }
@@ -533,7 +538,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     }
 
     private void checkItemNotExists(CatalogItem<?,?> itemDto, boolean forceUpdate) {
-        if (!forceUpdate && getCatalogItemDo(itemDto.getId(), itemDto.getVersion()) != null) {
+        if (!forceUpdate && getCatalogItemDo(itemDto.getSymbolicName(), itemDto.getVersion()) != null) {
             throw new IllegalStateException("Updating existing catalog entries is forbidden: " +
                     itemDto.getId() + ":" + itemDto.getVersion() + ". Use forceUpdate argument to override.");
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
index 6ef148e..9f65f80 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
@@ -19,6 +19,7 @@
 package brooklyn.catalog.internal;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
 
 import brooklyn.catalog.CatalogItem.CatalogBundle;
 
@@ -30,6 +31,13 @@ public class CatalogBundleDto implements CatalogBundle {
     public CatalogBundleDto() {}
 
     public CatalogBundleDto(String name, String version, String url) {
+        if (name == null && version == null) {
+            Preconditions.checkNotNull(url, "url");
+        } else {
+            Preconditions.checkNotNull(name, "name");
+            Preconditions.checkNotNull(version, "version");
+        }
+
         this.name = name;
         this.version = version;
         this.url = url;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDo.java
index 4ecca28..f6569d7 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDo.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogClasspathDo.java
@@ -22,7 +22,6 @@ import java.lang.reflect.Modifier;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.Arrays;
-import java.util.Map;
 import java.util.Set;
 
 import javax.annotation.Nullable;
@@ -39,9 +38,7 @@ import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.proxying.ImplementedBy;
 import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.policy.Policy;
-import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.flags.FlagUtils;
 import brooklyn.util.javalang.AggregateClassLoader;
 import brooklyn.util.javalang.ReflectionScanner;
 import brooklyn.util.javalang.UrlClassLoader;
@@ -244,15 +241,13 @@ public class CatalogClasspathDo {
     @Deprecated
     public CatalogItem<?,?> addCatalogEntry(CatalogItemDtoAbstract<?,?> item, Class<?> c) {
         Catalog annotations = c.getAnnotation(Catalog.class);
-        Map<String, Object> flags = MutableMap.<String, Object>of("id", c.getName());
-        FlagUtils.setFieldsFromFlags(flags, item);
-        item.registeredType = c.getName();
-        item.javaType = c.getName();
-        item.name = firstNonEmpty(c.getSimpleName(), c.getName());
+        item.setSymbolicName(c.getName());
+        item.setJavaType(c.getName());
+        item.setDisplayName(firstNonEmpty(c.getSimpleName(), c.getName()));
         if (annotations!=null) {
-            item.name = firstNonEmpty(annotations.name(), item.name);
-            item.description = firstNonEmpty(annotations.description());
-            item.iconUrl = firstNonEmpty(annotations.iconUrl());
+            item.setDisplayName(firstNonEmpty(annotations.name(), item.getDisplayName()));
+            item.setDescription(firstNonEmpty(annotations.description()));
+            item.setIconUrl(firstNonEmpty(annotations.iconUrl()));
         }
         if (log.isTraceEnabled())
             log.trace("adding to catalog: "+c+" (from catalog "+catalog+")");

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/catalog/internal/CatalogConfigurationDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogConfigurationDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogConfigurationDto.java
deleted file mode 100644
index 977fd36..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogConfigurationDto.java
+++ /dev/null
@@ -1,44 +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 brooklyn.catalog.internal;
-
-import brooklyn.config.ConfigKey;
-
-
-@SuppressWarnings("rawtypes")
-public class CatalogConfigurationDto extends CatalogItemDtoAbstract<ConfigKey,Void> {
-    
-    @Override
-    public CatalogItemType getCatalogItemType() {
-        return CatalogItemType.CONFIGURATION;
-    }
-
-    public Class<ConfigKey> getCatalogItemJavaType() { return ConfigKey.class; }
-
-    @Override
-    public String getRegisteredTypeName() {
-        return getJavaType();
-    }
-    
-    @Override
-    public Class<Void> getSpecType() {
-        return null;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
index e2249e0..0ea741d 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogDo.java
@@ -37,7 +37,6 @@ import brooklyn.util.net.Urls;
 import brooklyn.util.time.CountdownTimer;
 import brooklyn.util.time.Duration;
 
-import com.google.api.client.util.Maps;
 import com.google.common.base.Preconditions;
 
 public class CatalogDo {
@@ -51,8 +50,7 @@ public class CatalogDo {
     
     List<CatalogDo> childrenCatalogs = new ArrayList<CatalogDo>();
     CatalogClasspathDo classpath;
-    private Map<CatalogItemId, CatalogItemDo<?,?>> cacheById;
-    private Map<CatalogItemId, CatalogItemDo<?,?>> cacheByRegisteredTypeName;
+    private Map<String, CatalogItemDo<?,?>> cacheById;
 
     AggregateClassLoader childrenClassLoader = AggregateClassLoader.newInstanceWithNoLoaders();
     ClassLoader recursiveClassLoader;
@@ -125,7 +123,7 @@ public class CatalogDo {
         Iterable<CatalogItemDtoAbstract<?, ?>> entries = dto.getUniqueEntries();
         if (entries!=null) {
             for (CatalogItemDtoAbstract<?,?> entry : entries) {
-                CatalogUtils.installLibraries(mgmt, entry.getLibrariesDto());
+                CatalogUtils.installLibraries(mgmt, entry.getLibraries());
             }
         }
     }
@@ -159,26 +157,20 @@ public class CatalogDo {
         return childL;
     }
 
-    protected Map<CatalogItemId, CatalogItemDo<?,?>> getIdCache() {
-        Map<CatalogItemId, CatalogItemDo<?,?>> cache = this.cacheById;
-        if (cache==null) cache = buildCaches();
-        return cache;
-    }
-
-    protected Map<CatalogItemId, CatalogItemDo<?,?>> getRegisteredTypeNameCache() {
-        Map<CatalogItemId, CatalogItemDo<?,?>> cache = this.cacheByRegisteredTypeName;
+    protected Map<String, CatalogItemDo<?,?>> getIdCache() {
+        Map<String, CatalogItemDo<?,?>> cache = this.cacheById;
         if (cache==null) cache = buildCaches();
         return cache;
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
-    protected synchronized Map<CatalogItemId, CatalogItemDo<?,?>> buildCaches() {
+    protected synchronized Map<String, CatalogItemDo<?,?>> buildCaches() {
         if (cacheById != null) return cacheById;
         CatalogUtils.logDebugOrTraceIfRebinding(log, "Building cache for {}", this);
         if (!isLoaded()) 
             log.debug("Catalog not fully loaded when loading cache of "+this);
         
-        Map<CatalogItemId, CatalogItemDo<?,?>> cache = new LinkedHashMap<CatalogItemId, CatalogItemDo<?,?>>();
+        Map<String, CatalogItemDo<?,?>> cache = new LinkedHashMap<String, CatalogItemDo<?,?>>();
         
         // build the cache; first from children catalogs, then from local entities
         // so that root and near-root takes precedence over deeper items;
@@ -196,20 +188,14 @@ public class CatalogDo {
             List<CatalogItemDtoAbstract<?,?>> entriesReversed = MutableList.copyOf(dto.getUniqueEntries());
             Collections.reverse(entriesReversed);
             for (CatalogItemDtoAbstract<?,?> entry: entriesReversed)
-                cache.put(new CatalogItemId(entry), new CatalogItemDo(this, entry));
-        }
-        Map<CatalogItemId, CatalogItemDo<?, ?>> typeNameCache = Maps.newHashMap();
-        for (CatalogItemDo<?, ?> entry : cache.values()) {
-            typeNameCache.put(new CatalogItemId(entry.getRegisteredTypeName(), entry.getVersion()), entry);
+                cache.put(entry.getId(), new CatalogItemDo(this, entry));
         }
         this.cacheById = cache;
-        this.cacheByRegisteredTypeName = typeNameCache;
         return cache;
     }
     
     protected synchronized void clearCache(boolean deep) {
         this.cacheById = null;
-        this.cacheByRegisteredTypeName = null;
         if (deep) {
             for (CatalogDo child : childrenCatalogs) {
                 child.clearCache(true);
@@ -225,11 +211,8 @@ public class CatalogDo {
     public synchronized void addEntry(CatalogItemDtoAbstract<?,?> entry) {
         dto.addEntry(entry);
         if (cacheById != null) {
-            CatalogItemId cdoId = new CatalogItemId(entry);
             CatalogItemDo<?, ?> cdo = new CatalogItemDo(this, entry);
-            cacheById.put(cdoId, cdo);
-            CatalogItemId cdoTypeId = new CatalogItemId(entry.getRegisteredTypeName(), cdo.getVersion());
-            cacheByRegisteredTypeName.put(cdoTypeId, cdo);
+            cacheById.put(entry.getId(), cdo);
         }
         if (mgmt != null) {
             mgmt.getRebindManager().getChangeListener().onManaged(entry);
@@ -242,8 +225,7 @@ public class CatalogDo {
     public synchronized void deleteEntry(CatalogItemDtoAbstract<?, ?> entry) {
         dto.removeEntry(entry);
         if (cacheById != null) {
-            cacheById.remove(new CatalogItemId(entry));
-            cacheByRegisteredTypeName.remove(new CatalogItemId(entry.getRegisteredTypeName(), entry.getVersion()));
+            cacheById.remove(entry.getId());
         }
         if (mgmt != null) {
             // TODO: Can the entry be in more than one catalogue? The management context has no notion of

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java
index 8d2513b..722220b 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemBuilder.java
@@ -18,42 +18,47 @@
  */
 package brooklyn.catalog.internal;
 
+import java.util.Collection;
+import java.util.Collections;
+
+import brooklyn.catalog.CatalogItem.CatalogBundle;
+
 import com.google.common.base.Preconditions;
 
 public class CatalogItemBuilder<CatalogItemType extends CatalogItemDtoAbstract<?, ?>> {
     private CatalogItemType dto;
 
-    public static CatalogItemBuilder<CatalogEntityItemDto> newEntity(String registeredTypeName, String version) {
+    public static CatalogItemBuilder<CatalogEntityItemDto> newEntity(String symbolicName, String version) {
         return new CatalogItemBuilder<CatalogEntityItemDto>(new CatalogEntityItemDto())
-                .registeredTypeName(registeredTypeName)
+                .symbolicName(symbolicName)
                 .version(version);
     }
 
-    public static CatalogItemBuilder<CatalogTemplateItemDto> newTemplate(String registeredTypeName, String version) {
+    public static CatalogItemBuilder<CatalogTemplateItemDto> newTemplate(String symbolicName, String version) {
         return new CatalogItemBuilder<CatalogTemplateItemDto>(new CatalogTemplateItemDto())
-                .registeredTypeName(registeredTypeName)
+                .symbolicName(symbolicName)
                 .version(version);
     }
 
-    public static CatalogItemBuilder<CatalogPolicyItemDto> newPolicy(String registeredTypeName, String version) {
+    public static CatalogItemBuilder<CatalogPolicyItemDto> newPolicy(String symbolicName, String version) {
         return new CatalogItemBuilder<CatalogPolicyItemDto>(new CatalogPolicyItemDto())
-                .registeredTypeName(registeredTypeName)
+                .symbolicName(symbolicName)
                 .version(version);
     }
 
     public CatalogItemBuilder(CatalogItemType dto) {
         this.dto = dto;
-        this.dto.libraries = new CatalogLibrariesDto();
+        this.dto.setLibraries(Collections.<CatalogBundle>emptyList());
     }
 
-    public CatalogItemBuilder<CatalogItemType> registeredTypeName(String registeredType) {
-        dto.registeredType = registeredType;
+    public CatalogItemBuilder<CatalogItemType> symbolicName(String symbolicName) {
+        dto.setSymbolicName(symbolicName);
         return this;
     }
 
     @Deprecated
     public CatalogItemBuilder<CatalogItemType> javaType(String javaType) {
-        dto.javaType = javaType;
+        dto.setJavaType(javaType);
         return this;
     }
 
@@ -64,40 +69,41 @@ public class CatalogItemBuilder<CatalogItemType extends CatalogItemDtoAbstract<?
     }
 
     public CatalogItemBuilder<CatalogItemType> displayName(String displayName) {
-        dto.name = displayName;
+        dto.setDisplayName(displayName);
         return this;
     }
 
     public CatalogItemBuilder<CatalogItemType> description(String description) {
-        dto.description = description;
+        dto.setDescription(description);
         return this;
     }
 
     public CatalogItemBuilder<CatalogItemType> iconUrl(String iconUrl) {
-        dto.iconUrl = iconUrl;
+        dto.setIconUrl(iconUrl);
         return this;
     }
 
     public CatalogItemBuilder<CatalogItemType> version(String version) {
-        dto.version = version;
+        dto.setVersion(version);
         return this;
     }
 
-    public CatalogItemBuilder<CatalogItemType> libraries(CatalogLibrariesDto libraries) {
-        dto.libraries = libraries;
+    public CatalogItemBuilder<CatalogItemType> libraries(Collection<CatalogBundle> libraries) {
+        dto.setLibraries(libraries);
         return this;
     }
 
     public CatalogItemBuilder<CatalogItemType> plan(String yaml) {
-        dto.planYaml = yaml;
+        dto.setPlanYaml(yaml);
         return this;
     }
 
     public CatalogItemType build() {
-        Preconditions.checkNotNull(dto.registeredType);
+        Preconditions.checkNotNull(dto.getSymbolicName());
+        Preconditions.checkNotNull(dto.getVersion());
 
-        if (dto.libraries == null) {
-            dto.libraries = new CatalogLibrariesDto();
+        if (dto.getLibraries() == null) {
+            dto.setLibraries(Collections.<CatalogBundle>emptyList());
         }
 
         CatalogItemType ret = dto;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java
index b3ac88d..0b25785 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java
@@ -18,6 +18,8 @@
  */
 package brooklyn.catalog.internal;
 
+import java.util.Collection;
+
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
@@ -65,11 +67,6 @@ public class CatalogItemDo<T,SpecT> implements CatalogItem<T,SpecT> {
     }
 
     @Override
-    public String getRegisteredTypeName() {
-        return itemDto.getRegisteredTypeName();
-    }
-    
-    @Override
     public String getJavaType() {
         return itemDto.getJavaType();
     }
@@ -105,6 +102,11 @@ public class CatalogItemDo<T,SpecT> implements CatalogItem<T,SpecT> {
     public String getIconUrl() {
         return itemDto.getIconUrl();
     }
+    
+    @Override
+    public String getSymbolicName() {
+        return itemDto.getSymbolicName();
+    }
 
     @Override
     public String getVersion() {
@@ -113,7 +115,7 @@ public class CatalogItemDo<T,SpecT> implements CatalogItem<T,SpecT> {
 
     @Nonnull  // but it is still null sometimes, see in CatalogDo.loadJavaClass
     @Override
-    public CatalogItemLibraries getLibraries() {
+    public Collection<CatalogBundle> getLibraries() {
         return itemDto.getLibraries();
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
index 3679373..f834f15 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
@@ -18,6 +18,8 @@
  */
 package brooklyn.catalog.internal;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 
@@ -32,55 +34,55 @@ import brooklyn.catalog.CatalogItem;
 import brooklyn.entity.rebind.BasicCatalogItemRebindSupport;
 import brooklyn.entity.rebind.RebindSupport;
 import brooklyn.mementos.CatalogItemMemento;
+import brooklyn.util.collections.MutableList;
 import brooklyn.util.flags.FlagUtils;
 import brooklyn.util.flags.SetFromFlag;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 
 public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynObject implements CatalogItem<T, SpecT> {
 
-    private static final Logger log = LoggerFactory.getLogger(CatalogItemDtoAbstract.class);
-    
-    // TODO are ID and registeredType the same?
-    @SetFromFlag Set<Object> tags = Sets.newLinkedHashSet();
-    @SetFromFlag String registeredType;
-    @SetFromFlag String javaType;
-    @SetFromFlag String description;
-    @SetFromFlag String iconUrl;
-    @SetFromFlag String version = BasicBrooklynCatalog.NO_VERSION;
-    @SetFromFlag CatalogLibrariesDto libraries;
-    @SetFromFlag String planYaml;
-
-    // Field left named `name' to maintain the name element in existing catalogues.
-    @SetFromFlag("displayName") String name;
-
-    /** @deprecated since 0.7.0.
-     * used for backwards compatibility when deserializing.
-     * when catalogs are converted to new yaml format, this can be removed. */
-    @Deprecated
-    @SetFromFlag
-    String type;
+    private static Logger LOG = LoggerFactory.getLogger(CatalogItemDtoAbstract.class);
+
+    private @SetFromFlag String symbolicName;
+    private @SetFromFlag String version = BasicBrooklynCatalog.NO_VERSION;
+
+    /**@deprecated since 0.7.0, left for deserialization backwards compatibility */
+    private @Deprecated @SetFromFlag String registeredTypeName;
+
+    private @SetFromFlag String displayName;
+    /**@deprecated since 0.7.0, left for deserialization backwards compatibility */
+    private @Deprecated @SetFromFlag String name;
+    private @SetFromFlag String description;
+    private @SetFromFlag String iconUrl;
+
+    private @SetFromFlag String javaType;
+    /**@deprecated since 0.7.0, left for deserialization backwards compatibility */
+    private @Deprecated @SetFromFlag String type;
+    private @SetFromFlag String planYaml;
+
+    private @SetFromFlag Collection<CatalogBundle> bundles;
+    /**@deprecated since 0.7.0, left for deserialization backwards compatibility */
+    private @Deprecated @SetFromFlag CatalogLibrariesDto libraries;
+    private @SetFromFlag Set<Object> tags = Sets.newLinkedHashSet();
 
-    // TODO: Items with neither id nor java type should be rejected at construction.
-    // XStream auto-filling fields makes this a bit awkward.
     @Override
     public String getId() {
-        return super.getId() != null ? super.getId() : getJavaType();
+        return getCatalogItemId();
     }
 
     @Override
-    public String getRegisteredTypeName() {
-        if (registeredType!=null) return registeredType;
-        return getJavaType();
+    public String getCatalogItemId() {
+        return CatalogUtils.getVersionedId(getSymbolicName(), getVersion());
     }
 
     @Override
     public String getJavaType() {
-        if (javaType!=null) return javaType;
-        if (type!=null) return type;
-        return null;
+        if (javaType != null) return javaType;
+        return type;
     }
 
     @Deprecated
@@ -90,6 +92,7 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
 
     @Override
     public String getDisplayName() {
+        if (displayName != null) return displayName;
         return name;
     }
 
@@ -104,9 +107,17 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
     }
 
     @Override
+    public String getSymbolicName() {
+        if (symbolicName != null) return symbolicName;
+        if (registeredTypeName != null) return registeredTypeName;
+        return getJavaType();
+    }
+
+    @Override
     public String getVersion() {
-        //Deserializing doesn't call the constructor
-        //so the property may be null.
+        //xstream doesn't call constructors
+        //the object is used directly (instead of a memento) when
+        //deserializing directly from catalog.xml
         if (version != null) {
             return version;
         } else {
@@ -116,12 +127,15 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
 
     @Nonnull
     @Override
-    public CatalogItemLibraries getLibraries() {
-        return getLibrariesDto();
-    }
-
-    public CatalogLibrariesDto getLibrariesDto() {
-        return libraries;
+    @SuppressWarnings("deprecation")
+    public Collection<CatalogBundle> getLibraries() {
+        if (bundles != null) {
+            return ImmutableList.copyOf(bundles);
+        } else if (libraries != null && libraries.getBundles() != null) {
+            return ImmutableList.copyOf(libraries.getBundles());
+        } else {
+            return Collections.emptyList();
+        }
     }
 
     @Nullable @Override
@@ -131,7 +145,7 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
 
     @Override
     public String toString() {
-        return getClass().getSimpleName()+"["+getId()+":"+getVersion()+"/"+getName()+"]";
+        return getClass().getSimpleName()+"["+getId()+"/"+getDisplayName()+"]";
     }
 
     public abstract Class<SpecT> getSpecType();
@@ -157,7 +171,8 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
 
     @Override
     public void setDisplayName(String newName) {
-        this.name = newName;
+        this.displayName = newName;
+        this.name = null;
     }
 
     @Override
@@ -240,12 +255,98 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
         }
     }
 
-    /** Use of this method is discouraged unless it is assigning the same (final) ID that this object already has. */
     @Override
+    @Deprecated
     public void setCatalogItemId(String id) {
-        if (id!=null && !id.equals(getRegisteredTypeName())) {
-            log.warn("Setting 'catalog-item ID' of catalog item "+getId()+"/"+getRegisteredTypeName()+" to "+id+"; if set, catalog-item ID should match the registered type name.");
+        //no op, should be used by rebind code only
+    }
+
+    protected void setSymbolicName(String symbolicName) {
+        this.symbolicName = symbolicName;
+        this.registeredTypeName = null;
+    }
+
+    protected void setVersion(String version) {
+        this.version = version;
+    }
+
+    protected void setDescription(String description) {
+        this.description = description;
+    }
+
+    protected void setIconUrl(String iconUrl) {
+        this.iconUrl = iconUrl;
+    }
+
+    protected void setJavaType(String javaType) {
+        this.javaType = javaType;
+        this.type = null;
+    }
+
+    protected void setPlanYaml(String planYaml) {
+        this.planYaml = planYaml;
+    }
+
+    protected void setLibraries(Collection<CatalogBundle> libraries) {
+        this.bundles = libraries;
+    }
+
+    protected void setTags(Set<Object> tags) {
+        this.tags = tags;
+    }
+
+    protected void setSerializer(CatalogXmlSerializer serializer) {
+        this.serializer = serializer;
+    }
+
+    /**
+     * Parses an instance of CatalogLibrariesDto from the given List. Expects the list entries
+     * to be either Strings or Maps of String -> String. Will skip items that are not.
+     */
+    public static Collection<CatalogBundle> parseLibraries(Collection<?> possibleLibraries) {
+        Collection<CatalogBundle> dto = MutableList.of();
+        for (Object object : possibleLibraries) {
+            if (object instanceof Map) {
+                Map<?, ?> entry = (Map<?, ?>) object;
+                String name = stringValOrNull(entry, "name");
+                String version = stringValOrNull(entry, "version");
+                String url = stringValOrNull(entry, "url");
+                dto.add(new CatalogBundleDto(name, version, url));
+            } else if (object instanceof String) {
+                String inlineRef = (String) object;
+
+                final String name;
+                final String version;
+                final String url;
+
+                //Infer reference type (heuristically)
+                if (inlineRef.contains("/") || inlineRef.contains("\\")) {
+                    //looks like an url/file path
+                    name = null;
+                    version = null;
+                    url = inlineRef;
+                } else if (inlineRef.indexOf(CatalogUtils.VERSION_DELIMITER) != -1) {
+                    //looks like a name+version ref
+                    name = CatalogUtils.getIdFromVersionedId(inlineRef);
+                    version = CatalogUtils.getVersionFromVersionedId(inlineRef);
+                    url = null;
+                } else {
+                    //assume it to be relative url
+                    name = null;
+                    version = null;
+                    url = inlineRef;
+                }
+
+                dto.add(new CatalogBundleDto(name, version, url));
+            } else {
+                LOG.debug("Unexpected entry in libraries list neither string nor map: " + object);
+            }
         }
-        super.setCatalogItemId(id);
+        return dto;
+    }
+
+    private static String stringValOrNull(Map<?, ?> map, String key) {
+        Object val = map.get(key);
+        return val != null ? String.valueOf(val) : null;
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/catalog/internal/CatalogItemId.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemId.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemId.java
deleted file mode 100644
index 47f7b62..0000000
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemId.java
+++ /dev/null
@@ -1,67 +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 brooklyn.catalog.internal;
-
-import brooklyn.catalog.CatalogItem;
-
-class CatalogItemId {
-    private String id;
-    private String version;
-    
-    public CatalogItemId(String id, String version) {
-        this.id = id;
-        this.version = version;
-    }
-    
-    public CatalogItemId(CatalogItem<?, ?> item) {
-        this(item.getId(), item.getVersion());
-    }
-    
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((id == null) ? 0 : id.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;
-        CatalogItemId other = (CatalogItemId) obj;
-        if (id == null) {
-            if (other.id != null)
-                return false;
-        } else if (!id.equals(other.id))
-            return false;
-        if (version == null) {
-            if (other.version != null)
-                return false;
-        } else if (!version.equals(other.version))
-            return false;
-        return true;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java
index fa5f478..1fda2d7 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java
@@ -25,6 +25,7 @@ import brooklyn.catalog.CatalogItem.CatalogBundle;
 
 import com.google.common.base.Preconditions;
 
+@Deprecated
 public class CatalogLibrariesDo implements CatalogItem.CatalogItemLibraries {
 
     private final CatalogLibrariesDto librariesDto;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java
index 646d69c..8403876 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java
@@ -20,36 +20,18 @@ package brooklyn.catalog.internal;
 
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Map;
 import java.util.concurrent.CopyOnWriteArrayList;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.CatalogItem.CatalogBundle;
 
-import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 
+@Deprecated
 public class CatalogLibrariesDto implements CatalogItem.CatalogItemLibraries {
 
-    private static Logger LOG = LoggerFactory.getLogger(CatalogLibrariesDto.class);
-
     private Collection<CatalogBundle> bundles = new CopyOnWriteArrayList<CatalogBundle>();
 
-    public void addBundle(String name, String version, String url) {
-        Preconditions.checkNotNull(bundles, "Cannot add a bundle to a deserialized DTO");
-        if (name == null && version == null) {
-            Preconditions.checkNotNull(url, "url");
-        } else {
-            Preconditions.checkNotNull(name, "name");
-            Preconditions.checkNotNull(version, "version");
-        }
-        
-        bundles.add(new CatalogBundleDto(name, version, url));
-    }
-
     /**
      * @return An immutable copy of the bundle URLs referenced by this object
      */
@@ -62,54 +44,4 @@ public class CatalogLibrariesDto implements CatalogItem.CatalogItemLibraries {
         return ImmutableList.copyOf(bundles);
     }
 
-    /**
-     * Parses an instance of CatalogLibrariesDto from the given List. Expects the list entries
-     * to be either Strings or Maps of String -> String. Will skip items that are not.
-     */
-    public static CatalogLibrariesDto from(Collection<?> possibleLibraries) {
-        CatalogLibrariesDto dto = new CatalogLibrariesDto();
-        for (Object object : possibleLibraries) {
-            if (object instanceof Map) {
-                Map<?, ?> entry = (Map<?, ?>) object;
-                String name = stringValOrNull(entry, "name");
-                String version = stringValOrNull(entry, "version");
-                String url = stringValOrNull(entry, "url");
-                dto.addBundle(name, version, url);
-            } else if (object instanceof String) {
-                String inlineRef = (String) object;
-
-                final String name;
-                final String version;
-                final String url;
-
-                //Infer reference type (heuristically)
-                if (inlineRef.contains("/") || inlineRef.contains("\\")) {
-                    //looks like an url/file path
-                    name = null;
-                    version = null;
-                    url = inlineRef;
-                } else if (inlineRef.indexOf(CatalogUtils.VERSION_DELIMITER) != -1) {
-                    //looks like a name+version ref
-                    name = CatalogUtils.getIdFromVersionedId(inlineRef);
-                    version = CatalogUtils.getVersionFromVersionedId(inlineRef);
-                    url = null;
-                } else {
-                    //assume it to be relative url
-                    name = null;
-                    version = null;
-                    url = inlineRef;
-                }
-
-                dto.addBundle(name, version, url);
-            } else {
-                LOG.debug("Unexpected entry in libraries list neither string nor map: " + object);
-            }
-        }
-        return dto;
-    }
-
-    private static String stringValOrNull(Map<?, ?> map, String key) {
-        Object val = map.get(key);
-        return val != null ? String.valueOf(val) : null;
-    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
index 312351f..25c8d8d 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
@@ -29,7 +29,6 @@ import brooklyn.basic.BrooklynObject;
 import brooklyn.basic.BrooklynObjectInternal;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.CatalogItem.CatalogBundle;
-import brooklyn.catalog.CatalogItem.CatalogItemLibraries;
 import brooklyn.catalog.internal.BasicBrooklynCatalog.BrooklynLoaderTracker;
 import brooklyn.config.BrooklynLogging;
 import brooklyn.entity.Entity;
@@ -54,23 +53,19 @@ public class CatalogUtils {
     public static final char VERSION_DELIMITER = ':';
 
     public static BrooklynClassLoadingContext newClassLoadingContext(ManagementContext mgmt, CatalogItem<?, ?> item) {
-        CatalogItemLibraries libraries = item.getLibraries();
         // TODO getLibraries() should never be null but sometimes it is still
         // e.g. run CatalogResourceTest without the above check
-        if (libraries == null) {
+        if (item.getLibraries() == null) {
             log.debug("CatalogItemDtoAbstract.getLibraries() is null.", new Exception("Trace for null CatalogItemDtoAbstract.getLibraries()"));
         }
-        return newClassLoadingContext(mgmt, item.getId(), libraries, mgmt.getCatalog().getRootClassLoader());
+        return newClassLoadingContext(mgmt, item.getId(), item.getLibraries(), mgmt.getCatalog().getRootClassLoader());
     }
 
-    public static BrooklynClassLoadingContext newClassLoadingContext(@Nullable ManagementContext mgmt, String catalogItemId, CatalogItemLibraries libraries, ClassLoader classLoader) {
+    public static BrooklynClassLoadingContext newClassLoadingContext(@Nullable ManagementContext mgmt, String catalogItemId, Collection<CatalogBundle> libraries, ClassLoader classLoader) {
         BrooklynClassLoadingContextSequential result = new BrooklynClassLoadingContextSequential(mgmt);
 
-        if (libraries!=null) {
-            Collection<CatalogBundle> bundles = libraries.getBundles();
-            if (bundles!=null && !bundles.isEmpty()) {
-                result.add(new OsgiBrooklynClassLoadingContext(mgmt, catalogItemId, bundles));
-            }
+        if (libraries!=null && !libraries.isEmpty()) {
+            result.add(new OsgiBrooklynClassLoadingContext(mgmt, catalogItemId, libraries));
         }
 
         BrooklynClassLoadingContext loader = BrooklynLoaderTracker.getLoader();
@@ -85,28 +80,27 @@ public class CatalogUtils {
     /**
      * Registers all bundles with the management context's OSGi framework.
      */
-    public static void installLibraries(ManagementContext managementContext, @Nullable CatalogItemLibraries libraries) {
+    public static void installLibraries(ManagementContext managementContext, @Nullable Collection<CatalogBundle> libraries) {
         if (libraries == null) return;
 
         ManagementContextInternal mgmt = (ManagementContextInternal) managementContext;
-        Collection<CatalogBundle> bundles = libraries.getBundles();
-        if (!bundles.isEmpty()) {
+        if (!libraries.isEmpty()) {
             Maybe<OsgiManager> osgi = mgmt.getOsgiManager();
             if (osgi.isAbsent()) {
-                throw new IllegalStateException("Unable to load bundles "+bundles+" because OSGi is not running.");
+                throw new IllegalStateException("Unable to load bundles "+libraries+" because OSGi is not running.");
             }
             if (log.isDebugEnabled()) 
                 logDebugOrTraceIfRebinding(log, 
                     "Loading bundles in {}: {}", 
-                    new Object[] {managementContext, Joiner.on(", ").join(bundles)});
+                    new Object[] {managementContext, Joiner.on(", ").join(libraries)});
             Stopwatch timer = Stopwatch.createStarted();
-            for (CatalogBundle bundleUrl : bundles) {
+            for (CatalogBundle bundleUrl : libraries) {
                 osgi.get().registerBundle(bundleUrl);
             }
             if (log.isDebugEnabled()) 
                 logDebugOrTraceIfRebinding(log, 
                     "Registered {} bundles in {}",
-                    new Object[]{bundles.size(), Time.makeTimeStringRounded(timer)});
+                    new Object[]{libraries.size(), Time.makeTimeStringRounded(timer)});
         }
     }
 
@@ -167,4 +161,8 @@ public class CatalogUtils {
         }
     }
 
+    public static String getVersionedId(String id, String version) {
+        return id + VERSION_DELIMITER + version;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java b/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java
index 0948149..8871edf 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java
@@ -30,6 +30,7 @@ import brooklyn.util.xstream.XmlSerializer;
 
 public class CatalogXmlSerializer extends XmlSerializer<Object> {
 
+    @SuppressWarnings("deprecation")
     public CatalogXmlSerializer() {
         xstream.addDefaultImplementation(ArrayList.class, Collection.class);
         
@@ -49,8 +50,8 @@ public class CatalogXmlSerializer extends XmlSerializer<Object> {
         xstream.aliasType("entity", CatalogEntityItemDto.class);
         xstream.aliasType("policy", CatalogPolicyItemDto.class);
 
-        xstream.useAttributeFor(CatalogItemDtoAbstract.class, "type");
-        xstream.useAttributeFor(CatalogItemDtoAbstract.class, "name");
+        xstream.aliasAttribute(CatalogItemDtoAbstract.class, "javaType", "type");
+        xstream.aliasAttribute(CatalogItemDtoAbstract.class, "displayName", "name");
         xstream.useAttributeFor(CatalogItemDtoAbstract.class, "version");
 
         xstream.useAttributeFor(CatalogClasspathDto.class, "scan");

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java b/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java
index 9ddd4ca..61b9511 100644
--- a/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java
+++ b/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java
@@ -42,14 +42,13 @@ public class BasicCatalogItemRebindSupport extends AbstractBrooklynObjectRebindS
     public void reconstruct(RebindContext rebindContext, CatalogItemMemento memento) {
         super.reconstruct(rebindContext, memento);
         FlagUtils.setFieldsFromFlags(MutableMap.builder()
-                .put("id", memento.getId())
-                .put("registeredType", memento.getRegisteredTypeName())
+                .put("symbolicName", memento.getSymbolicName())
                 .put("javaType", memento.getJavaType())
                 .put("displayName", memento.getDisplayName())
                 .put("description", memento.getDescription())
                 .put("iconUrl", memento.getIconUrl())
                 .put("version", memento.getVersion())
-                .put("libraries", memento.getLibraries())
+                .put("bundles", memento.getBundles())
                 .put("planYaml", memento.getPlanYaml())
                 .build(), instance);
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/entity/rebind/dto/BasicCatalogItemMemento.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/dto/BasicCatalogItemMemento.java b/core/src/main/java/brooklyn/entity/rebind/dto/BasicCatalogItemMemento.java
index 68d1212..b32e841 100644
--- a/core/src/main/java/brooklyn/entity/rebind/dto/BasicCatalogItemMemento.java
+++ b/core/src/main/java/brooklyn/entity/rebind/dto/BasicCatalogItemMemento.java
@@ -19,6 +19,7 @@
 package brooklyn.entity.rebind.dto;
 
 import java.io.Serializable;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
 
@@ -28,6 +29,8 @@ import com.google.common.base.Joiner;
 import com.google.common.base.Objects;
 
 import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.CatalogItem.CatalogBundle;
+import brooklyn.catalog.internal.BasicBrooklynCatalog;
 import brooklyn.mementos.CatalogItemMemento;
 
 @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)
@@ -41,12 +44,12 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
 
     public static class Builder extends AbstractMemento.Builder<Builder> {
         protected String description;
-        protected String registeredTypeName;
+        protected String symbolicName;
         protected String iconUrl;
         protected String javaType;
         protected String version;
         protected String planYaml;
-        protected CatalogItem.CatalogItemLibraries libraries;
+        protected Collection<CatalogItem.CatalogBundle> libraries;
         protected CatalogItem.CatalogItemType catalogItemType;
         protected Class<?> catalogItemJavaType;
         protected Class<?> specType;
@@ -56,8 +59,8 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
             return self();
         }
 
-        public Builder registeredTypeName(String registeredTypeName) {
-            this.registeredTypeName = registeredTypeName;
+        public Builder symbolicName(String symbolicName) {
+            this.symbolicName = symbolicName;
             return self();
         }
 
@@ -81,7 +84,7 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
             return self();
         }
 
-        public Builder libraries(CatalogItem.CatalogItemLibraries libraries) {
+        public Builder libraries(Collection<CatalogItem.CatalogBundle> libraries) {
             this.libraries = libraries;
             return self();
         }
@@ -104,12 +107,12 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
         public Builder from(CatalogItemMemento other) {
             super.from(other);
             description = other.getDescription();
-            registeredTypeName = other.getRegisteredTypeName();
+            symbolicName = other.getSymbolicName();
             iconUrl = other.getIconUrl();
             javaType = other.getJavaType();
             version = other.getVersion();
             planYaml = other.getPlanYaml();
-            libraries = other.getLibraries();
+            libraries = other.getBundles();
             catalogItemType = other.getCatalogItemType();
             catalogItemJavaType = other.getCatalogItemJavaType();
             specType = other.getSpecType();
@@ -122,11 +125,16 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
     }
 
     private String description;
-    private String registeredTypeName;
+    private String symbolicName;
     private String iconUrl;
     private String javaType;
     private String version;
     private String planYaml;
+    //Keep libraries for deserialization compatibility and
+    //introduce bundles to hold the new libraries type from
+    //catalog item
+    private Collection<CatalogItem.CatalogBundle> bundles;
+    @SuppressWarnings("deprecation")
     private CatalogItem.CatalogItemLibraries libraries;
     private CatalogItem.CatalogItemType catalogItemType;
     private Class<?> catalogItemJavaType;
@@ -138,11 +146,12 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
     protected BasicCatalogItemMemento(Builder builder) {
         super(builder);
         this.description = builder.description;
-        this.registeredTypeName = builder.registeredTypeName;
+        this.symbolicName = builder.symbolicName;
         this.iconUrl = builder.iconUrl;
         this.version = builder.version;
         this.planYaml = builder.planYaml;
-        this.libraries = builder.libraries;
+        this.bundles = builder.libraries;
+        this.libraries = null;
         this.catalogItemJavaType = builder.catalogItemJavaType;
         this.catalogItemType = builder.catalogItemType;
         this.specType = builder.specType;
@@ -155,8 +164,8 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
     }
 
     @Override
-    public String getRegisteredTypeName() {
-        return registeredTypeName;
+    public String getSymbolicName() {
+        return symbolicName;
     }
 
     @Override
@@ -166,7 +175,11 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
 
     @Override
     public String getVersion() {
-        return version;
+        if (version != null) {
+            return version;
+        } else {
+            return BasicBrooklynCatalog.NO_VERSION;
+        }
     }
 
     @Override
@@ -180,8 +193,16 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
     }
 
     @Override
-    public CatalogItem.CatalogItemLibraries getLibraries() {
-        return libraries;
+    public Collection<CatalogItem.CatalogBundle> getBundles() {
+        if (bundles != null) {
+            return bundles;
+        } else if (libraries != null) {
+            @SuppressWarnings("deprecation")
+            Collection<CatalogBundle> b = libraries.getBundles();
+            return b;
+        } else {
+            return null;
+        }
     }
 
     @Override
@@ -216,11 +237,11 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
     protected Objects.ToStringHelper newVerboseStringHelper() {
         return super.newVerboseStringHelper()
                 .add("description", getDescription())
-                .add("registeredTypeName", getRegisteredTypeName())
+                .add("symbolicName", getSymbolicName())
                 .add("iconUrl", getIconUrl())
                 .add("version", getVersion())
                 .add("planYaml", getPlanYaml())
-                .add("libraries", getLibraries())
+                .add("bundles", getBundles())
                 .add("catalogItemJavaType", getCatalogItemJavaType())
                 .add("catalogItemType", getCatalogItemType())
                 .add("javaType", getJavaType())

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java b/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java
index d5a5172..94c2b59 100644
--- a/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java
+++ b/core/src/main/java/brooklyn/entity/rebind/dto/MementosGenerators.java
@@ -362,16 +362,15 @@ public class MementosGenerators {
         BasicCatalogItemMemento.Builder builder = BasicCatalogItemMemento.builder();
         populateBrooklynObjectMementoBuilder(catalogItem, builder);
         builder.catalogItemJavaType(catalogItem.getCatalogItemJavaType())
-        .catalogItemType(catalogItem.getCatalogItemType())
-        .description(catalogItem.getDescription())
-        .iconUrl(catalogItem.getIconUrl())
-        .javaType(catalogItem.getJavaType())
-        .libraries(catalogItem.getLibraries())
-        .registeredTypeName(catalogItem.getRegisteredTypeName())
-        .specType(catalogItem.getSpecType())
-        .version(catalogItem.getVersion())
-        .planYaml(catalogItem.getPlanYaml())
-        ;
+            .catalogItemType(catalogItem.getCatalogItemType())
+            .description(catalogItem.getDescription())
+            .iconUrl(catalogItem.getIconUrl())
+            .javaType(catalogItem.getJavaType())
+            .libraries(catalogItem.getLibraries())
+            .symbolicName(catalogItem.getSymbolicName())
+            .specType(catalogItem.getSpecType())
+            .version(catalogItem.getVersion())
+            .planYaml(catalogItem.getPlanYaml());
         return builder.build();
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
index 392a496..ac49879 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
@@ -47,6 +47,7 @@ import brooklyn.mementos.EntityMemento;
 import brooklyn.mementos.LocationMemento;
 import brooklyn.mementos.PolicyMemento;
 import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.io.FileUtil;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
@@ -559,23 +560,27 @@ public class BrooklynMementoPersisterToMultiFile implements BrooklynMementoPersi
     }
 
     private File getFileFor(EntityMemento entity) {
-        return new File(entitiesDir, entity.getId());
+        return getFileFor(entitiesDir, entity.getId());
     }
     
     private File getFileFor(LocationMemento location) {
-        return new File(locationsDir, location.getId());
+        return getFileFor(locationsDir, location.getId());
     }
     
     private File getFileFor(PolicyMemento policy) {
-        return new File(policiesDir, policy.getId());
+        return getFileFor(policiesDir, policy.getId());
     }
     
     private File getFileFor(EnricherMemento enricher) {
-        return new File(enrichersDir, enricher.getId());
+        return getFileFor(enrichersDir, enricher.getId());
     }
 
     private File getFileFor(CatalogItemMemento catalogItem) {
-        return new File(catalogItemsDir, catalogItem.getId());
+        return getFileFor(catalogItemsDir, catalogItem.getId());
+    }
+
+    private File getFileFor(File parent, String id) {
+        return new File(parent, FileUtil.getSafeFileName(id));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
index 0ee9a73..28a2810 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
@@ -31,6 +31,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.regex.Pattern;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -56,6 +57,7 @@ import brooklyn.mementos.Memento;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.CompoundRuntimeException;
 import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.io.FileUtil;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
@@ -657,7 +659,7 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
     }
     
     private String getPath(String subPath, String id) {
-        return subPath+"/"+id;
+        return subPath+"/"+FileUtil.getSafeFileName(id);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
index 84e3fc5..6355c12 100644
--- a/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
+++ b/core/src/test/java/brooklyn/camp/lite/CampYamlLiteTest.java
@@ -154,13 +154,13 @@ public class CampYamlLiteTest {
     public void testYamlServiceForCatalog() {
         CatalogItem<?, ?> realItem = mgmt.getCatalog().addItem(Streams.readFullyString(getClass().getResourceAsStream("test-app-service-blueprint.yaml")));
         Iterable<CatalogItem<Object, Object>> retrievedItems = mgmt.getCatalog()
-                .getCatalogItems(CatalogPredicates.registeredType(Predicates.equalTo("catalog-name")));
+                .getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo("catalog-name")));
         
         Assert.assertEquals(Iterables.size(retrievedItems), 1, "Wrong retrieved items: "+retrievedItems);
         CatalogItem<Object, Object> retrievedItem = Iterables.getOnlyElement(retrievedItems);
         Assert.assertEquals(retrievedItem, realItem);
 
-        Collection<CatalogBundle> bundles = retrievedItem.getLibraries().getBundles();
+        Collection<CatalogBundle> bundles = retrievedItem.getLibraries();
         Assert.assertEquals(bundles.size(), 1);
         CatalogBundle bundle = Iterables.getOnlyElement(bundles);
         Assert.assertEquals(bundle.getUrl(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL);
@@ -175,20 +175,20 @@ public class CampYamlLiteTest {
 
     @Test
     public void testRegisterCustomEntityWithBundleWhereEntityIsFromCoreAndIconFromBundle() throws IOException {
-        String registeredTypeName = "my.catalog.app.id";
+        String symbolicName = "my.catalog.app.id";
         String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
-        String yaml = getSampleMyCatalogAppYaml(registeredTypeName, bundleUrl);
+        String yaml = getSampleMyCatalogAppYaml(symbolicName, bundleUrl);
 
         mgmt.getCatalog().addItem(yaml);
 
-        assertMgmtHasSampleMyCatalogApp(registeredTypeName, bundleUrl);
+        assertMgmtHasSampleMyCatalogApp(symbolicName, bundleUrl);
     }
 
     @Test
     public void testResetXmlWithCustomEntity() throws IOException {
-        String registeredTypeName = "my.catalog.app.id";
+        String symbolicName = "my.catalog.app.id";
         String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
-        String yaml = getSampleMyCatalogAppYaml(registeredTypeName, bundleUrl);
+        String yaml = getSampleMyCatalogAppYaml(symbolicName, bundleUrl);
 
         LocalManagementContext mgmt2 = LocalManagementContextForTests.newInstanceWithOsgi();
         try {
@@ -202,12 +202,12 @@ public class CampYamlLiteTest {
             mgmt2.terminate();
         }
 
-        assertMgmtHasSampleMyCatalogApp(registeredTypeName, bundleUrl);
+        assertMgmtHasSampleMyCatalogApp(symbolicName, bundleUrl);
     }
 
-    private String getSampleMyCatalogAppYaml(String registeredTypeName, String bundleUrl) {
+    private String getSampleMyCatalogAppYaml(String symbolicName, String bundleUrl) {
         return "brooklyn.catalog:\n" +
-                "  id: " + registeredTypeName + "\n" +
+                "  id: " + symbolicName + "\n" +
                 "  name: My Catalog App\n" +
                 "  description: My description\n" +
                 "  icon_url: classpath:/brooklyn/osgi/tests/icon.gif\n" +
@@ -219,20 +219,18 @@ public class CampYamlLiteTest {
                 "- type: io.camp.mock:AppServer\n";
     }
 
-    private void assertMgmtHasSampleMyCatalogApp(String registeredTypeName, String bundleUrl) {
-        CatalogItem<?, ?> item = mgmt.getCatalog().getCatalogItem(registeredTypeName, TEST_VERSION);
-        assertNotNull(item, "failed to load item with id=" + registeredTypeName + " from catalog. Entries were: " +
+    private void assertMgmtHasSampleMyCatalogApp(String symbolicName, String bundleUrl) {
+        CatalogItem<?, ?> item = mgmt.getCatalog().getCatalogItem(symbolicName, TEST_VERSION);
+        assertNotNull(item, "failed to load item with id=" + symbolicName + " from catalog. Entries were: " +
                 Joiner.on(",").join(mgmt.getCatalog().getCatalogItems()));
-        assertEquals(item.getRegisteredTypeName(), registeredTypeName);
+        assertEquals(item.getSymbolicName(), symbolicName);
 
         // stored as yaml, not java
         assertNotNull(item.getPlanYaml());
         Assert.assertTrue(item.getPlanYaml().contains("io.camp.mock:AppServer"));
 
-        assertEquals(item.getId(), registeredTypeName);
-
         // and let's check we have libraries
-        Collection<CatalogBundle> libs = item.getLibraries().getBundles();
+        Collection<CatalogBundle> libs = item.getLibraries();
         assertEquals(libs.size(), 1);
         CatalogBundle bundle = Iterables.getOnlyElement(libs);
         assertEquals(bundle.getUrl(), bundleUrl);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/5661ac49/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java b/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
index e232116..07585b2 100644
--- a/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
+++ b/core/src/test/java/brooklyn/catalog/internal/CatalogDtoTest.java
@@ -21,6 +21,8 @@ package brooklyn.catalog.internal;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 
+import java.util.Arrays;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
@@ -28,6 +30,7 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import brooklyn.catalog.CatalogItem.CatalogBundle;
 import brooklyn.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
 import brooklyn.entity.basic.Entities;
 import brooklyn.management.internal.LocalManagementContext;
@@ -76,7 +79,7 @@ public class CatalogDtoTest {
         CatalogDo loader = new CatalogDo(managementContext, root).load();
         
         // test app comes from jar, by default
-        CatalogItemDo<?,?> worker = loader.getRegisteredTypeNameCache().get(new CatalogItemId(TestApplication.class.getCanonicalName(), BasicBrooklynCatalog.NO_VERSION));
+        CatalogItemDo<?,?> worker = loader.getIdCache().get(CatalogUtils.getVersionedId(TestApplication.class.getCanonicalName(), BasicBrooklynCatalog.NO_VERSION));
         assertNotNull(worker);
         assertEquals(worker.getDisplayName(), "Test App from JAR");
         
@@ -117,9 +120,10 @@ public class CatalogDtoTest {
                 CatalogDto.newNamedInstance("Test Entities from OSGi",
                         "A catalog whose entries define their libraries as a list of OSGi bundles", "test-osgi-defined"));
         osgiCatalog.setClasspathScanForEntities(CatalogScanningModes.NONE);
-        CatalogEntityItemDto osgiEntity = CatalogItemBuilder.newEntity(TestEntity.class.getCanonicalName(), "Test Entity from OSGi").build();
-        // NB: this is not actually an OSGi bundle, but it's okay as we don't instantiate the bundles ahead of time (currently)
-        osgiEntity.libraries.addBundle(null, null, bundleUrl);
+        CatalogEntityItemDto osgiEntity = CatalogItemBuilder.newEntity(TestEntity.class.getCanonicalName(), "Test Entity from OSGi")
+                // NB: this is not actually an OSGi bundle, but it's okay as we don't instantiate the bundles ahead of time (currently)
+                .libraries(Arrays.<CatalogBundle>asList(new CatalogBundleDto(null, null, bundleUrl)))
+                .build();
         testEntitiesJavaCatalog.addEntry(osgiEntity);
         root.addCatalog(osgiCatalog.dto);
 


[12/18] incubator-brooklyn git commit: Catalog versioning - consistently use symbolicName and other minor changes

Posted by he...@apache.org.
Catalog versioning - consistently use symbolicName and other minor changes

Addressing first review comments.


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

Branch: refs/heads/master
Commit: 497496c1563a10608628c566fe17e7be8c68f2bf
Parents: 9c6539f
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Wed Nov 12 12:59:33 2014 +0200
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 11:49:54 2014 +0200

----------------------------------------------------------------------
 api/src/main/java/brooklyn/catalog/CatalogItem.java    |  2 +-
 .../catalog/internal/BasicBrooklynCatalog.java         | 13 ++++++++-----
 .../brooklyn/catalog/internal/CatalogBundleDto.java    | 12 ++++++------
 .../catalog/internal/CatalogItemComparator.java        |  3 +--
 .../persister/BrooklynMementoPersisterToMultiFile.java |  2 +-
 .../BrooklynMementoPersisterToObjectStore.java         |  4 +---
 .../main/java/brooklyn/management/ha/OsgiManager.java  | 10 +++++-----
 core/src/main/java/brooklyn/util/osgi/Osgis.java       |  2 +-
 .../camp/brooklyn/catalog/CatalogYamlEntityTest.java   |  4 ++--
 .../src/main/java/brooklyn/util/io/FileUtil.java       |  8 --------
 10 files changed, 26 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/497496c1/api/src/main/java/brooklyn/catalog/CatalogItem.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/catalog/CatalogItem.java b/api/src/main/java/brooklyn/catalog/CatalogItem.java
index c58dcd3..d06a682 100644
--- a/api/src/main/java/brooklyn/catalog/CatalogItem.java
+++ b/api/src/main/java/brooklyn/catalog/CatalogItem.java
@@ -37,7 +37,7 @@ public interface CatalogItem<T,SpecT> extends BrooklynObject, Rebindable {
     }
     
     public static interface CatalogBundle {
-        public String getName();
+        public String getSymbolicName();
         public String getVersion();
         public String getUrl();
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/497496c1/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
index ed85e76..bc43682 100644
--- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
@@ -193,7 +193,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
 
     private String getDefaultVersion(String symbolicName) {
         Iterable<CatalogItem<Object, Object>> versions = getCatalogItems(CatalogPredicates.symbolicName(Predicates.equalTo(symbolicName)));
-        ImmutableSortedSet<CatalogItem<?, ?>> orderedVersions = ImmutableSortedSet.orderedBy(new CatalogItemComparator()).addAll(versions).build();
+        ImmutableSortedSet<CatalogItem<?, ?>> orderedVersions = ImmutableSortedSet.orderedBy(CatalogItemComparator.INSTANCE).addAll(versions).build();
         if (!orderedVersions.isEmpty()) {
             return orderedVersions.iterator().next().getVersion();
         } else {
@@ -469,11 +469,14 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
         if (possibleVersion.isAbsent() && Strings.isBlank(version)) {
             throw new IllegalArgumentException("'version' attribute missing in 'brooklyn.catalog' section.");
         } else if (possibleVersion.isPresent()) {
-            if (Strings.isNonBlank(version)) {
-                throw new IllegalArgumentException("Can't use both attribute 'version' and versioned id");
-            }
             //could be coalesced to a number - can be one of Integer, Double, String
-            version = possibleVersion.get().toString();
+            String versionProperty = possibleVersion.get().toString();
+
+            if (!Strings.isBlank(version) && !versionProperty.equals(version)) {
+                throw new IllegalArgumentException("Discrepency between version set in id/name " + version + " and version property " + versionProperty);
+            }
+
+            version = versionProperty;
         }
 
         CatalogUtils.installLibraries(mgmt, libraries);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/497496c1/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
index 9f65f80..35353c4 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogBundleDto.java
@@ -24,7 +24,7 @@ import com.google.common.base.Preconditions;
 import brooklyn.catalog.CatalogItem.CatalogBundle;
 
 public class CatalogBundleDto implements CatalogBundle {
-    private String name;
+    private String symbolicName;
     private String version;
     private String url;
 
@@ -38,19 +38,19 @@ public class CatalogBundleDto implements CatalogBundle {
             Preconditions.checkNotNull(version, "version");
         }
 
-        this.name = name;
+        this.symbolicName = name;
         this.version = version;
         this.url = url;
     }
 
     @Override
     public boolean isNamed() {
-        return name != null && version != null;
+        return symbolicName != null && version != null;
     }
 
     @Override
-    public String getName() {
-        return name;
+    public String getSymbolicName() {
+        return symbolicName;
     }
 
     @Override
@@ -66,7 +66,7 @@ public class CatalogBundleDto implements CatalogBundle {
     @Override
     public String toString() {
         return Objects.toStringHelper(this)
-                .add("name", name)
+                .add("symbolicName", symbolicName)
                 .add("version", version)
                 .add("url", url)
                 .toString();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/497496c1/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
index 4ac03f9..a202b92 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemComparator.java
@@ -25,7 +25,6 @@ import brooklyn.util.text.NaturalOrderComparator;
 
 public class CatalogItemComparator implements Comparator<CatalogItem<?, ?>> {
     private static final String SNAPSHOT = "SNAPSHOT";
-    private static final Comparator<String> COMPARATOR = new NaturalOrderComparator();
 
     public static final CatalogItemComparator INSTANCE = new CatalogItemComparator();
 
@@ -40,7 +39,7 @@ public class CatalogItemComparator implements Comparator<CatalogItem<?, ?>> {
             boolean isV1Snapshot = v1.contains(SNAPSHOT);
             boolean isV2Snapshot = v2.contains(SNAPSHOT);
             if (isV1Snapshot == isV2Snapshot) {
-                return -COMPARATOR.compare(v1, v2);
+                return -NaturalOrderComparator.INSTANCE.compare(v1, v2);
             } else if (isV1Snapshot) {
                 return 1;
             } else {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/497496c1/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
index ac49879..74817c5 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
@@ -580,7 +580,7 @@ public class BrooklynMementoPersisterToMultiFile implements BrooklynMementoPersi
     }
 
     private File getFileFor(File parent, String id) {
-        return new File(parent, FileUtil.getSafeFileName(id));
+        return new File(parent, Strings.makeValidFilename(id));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/497496c1/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
index 28a2810..38cc12d 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
@@ -31,7 +31,6 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.regex.Pattern;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -57,7 +56,6 @@ import brooklyn.mementos.Memento;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.CompoundRuntimeException;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.io.FileUtil;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
@@ -659,7 +657,7 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
     }
     
     private String getPath(String subPath, String id) {
-        return subPath+"/"+FileUtil.getSafeFileName(id);
+        return subPath+"/"+Strings.makeValidFilename(id);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/497496c1/core/src/main/java/brooklyn/management/ha/OsgiManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ha/OsgiManager.java b/core/src/main/java/brooklyn/management/ha/OsgiManager.java
index c3a2faa..1042d1f 100644
--- a/core/src/main/java/brooklyn/management/ha/OsgiManager.java
+++ b/core/src/main/java/brooklyn/management/ha/OsgiManager.java
@@ -112,10 +112,10 @@ public class OsgiManager {
         String nv = b.getSymbolicName()+":"+b.getVersion().toString();
 
         if (bundle.isNamed() &&
-                (!bundle.getName().equals(b.getSymbolicName()) ||
+                (!bundle.getSymbolicName().equals(b.getSymbolicName()) ||
                 !bundle.getVersion().equals(b.getVersion().toString()))) {
             log.warn("Bundle at " + bundle.getUrl() + " installed as " + nv +
-                    " but user explicitly requested " + bundle.getName() + ":" + bundle.getVersion());
+                    " but user explicitly requested " + bundle.getSymbolicName() + ":" + bundle.getVersion());
         }
 
         List<Bundle> matches = Osgis.bundleFinder(framework)
@@ -139,7 +139,7 @@ public class OsgiManager {
         if (bundleUrl != null) {
             VersionedName nv = urlToBundleIdentifier.get(bundleUrl);
             if (nv!=null) {
-                if (bundle.isNamed() && !nv.equals(bundle.getName(), bundle.getVersion())) {
+                if (bundle.isNamed() && !nv.equals(bundle.getSymbolicName(), bundle.getVersion())) {
                     throw new IllegalStateException("Bundle from "+bundleUrl+" already installed as "+nv+" but user explicitly requested "+bundle);
                 }
                 Maybe<Bundle> installedBundle = Osgis.bundleFinder(framework).requiringFromUrl(bundleUrl).find();
@@ -157,7 +157,7 @@ public class OsgiManager {
                 }
             }
         } else {
-            Maybe<Bundle> installedBundle = Osgis.bundleFinder(framework).symbolicName(bundle.getName()).version(bundle.getVersion()).find();
+            Maybe<Bundle> installedBundle = Osgis.bundleFinder(framework).symbolicName(bundle.getSymbolicName()).version(bundle.getVersion()).find();
             if (installedBundle.isPresent()) {
                 log.trace("Bundle "+bundle+" installed from "+installedBundle.get().getLocation());
             } else {
@@ -231,7 +231,7 @@ public class OsgiManager {
         if (catalogBundle.getUrl() != null) {
             bundleFinder.requiringFromUrl(catalogBundle.getUrl());
         } else {
-            bundleFinder.symbolicName(catalogBundle.getName()).version(catalogBundle.getVersion());
+            bundleFinder.symbolicName(catalogBundle.getSymbolicName()).version(catalogBundle.getVersion());
         }
         return bundleFinder.find();
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/497496c1/core/src/main/java/brooklyn/util/osgi/Osgis.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/osgi/Osgis.java b/core/src/main/java/brooklyn/util/osgi/Osgis.java
index dfcea83..c6c9d9a 100644
--- a/core/src/main/java/brooklyn/util/osgi/Osgis.java
+++ b/core/src/main/java/brooklyn/util/osgi/Osgis.java
@@ -163,7 +163,7 @@ public class Osgis {
 
         public BundleFinder bundle(CatalogBundle bundle) {
             if (bundle.isNamed()) {
-                symbolicName(bundle.getName());
+                symbolicName(bundle.getSymbolicName());
                 version(bundle.getVersion());
             }
             if (bundle.getUrl() != null) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/497496c1/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
index c0083de..4ce917b 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
@@ -211,7 +211,7 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
                 "- type: " + SIMPLE_ENTITY_TYPE);
             fail();
         } catch (IllegalStateException e) {
-            Assert.assertEquals(e.getMessage(), "Bundle CatalogBundleDto{name=" + nonExistentId + ", version=" + nonExistentVersion + ", url=null} not previously registered, but URL is empty.");
+            Assert.assertEquals(e.getMessage(), "Bundle CatalogBundleDto{symbolicName=" + nonExistentId + ", version=" + nonExistentVersion + ", url=null} not previously registered, but URL is empty.");
         }
     }
 
@@ -300,7 +300,7 @@ public class CatalogYamlEntityTest extends AbstractYamlTest {
                 "- type: " + SIMPLE_ENTITY_TYPE);
             fail();
         } catch (IllegalStateException e) {
-            assertEquals(e.getMessage(), "Bundle CatalogBundleDto{name=" + nonExistentId + ", version=" + nonExistentVersion + ", url=null} " +
+            assertEquals(e.getMessage(), "Bundle CatalogBundleDto{symbolicName=" + nonExistentId + ", version=" + nonExistentVersion + ", url=null} " +
                     "not previously registered, but URL is empty.");
         }
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/497496c1/utils/common/src/main/java/brooklyn/util/io/FileUtil.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/io/FileUtil.java b/utils/common/src/main/java/brooklyn/util/io/FileUtil.java
index aed8450..66e7fc1 100644
--- a/utils/common/src/main/java/brooklyn/util/io/FileUtil.java
+++ b/utils/common/src/main/java/brooklyn/util/io/FileUtil.java
@@ -45,11 +45,6 @@ public class FileUtil {
 
     private static final Logger LOG = LoggerFactory.getLogger(FileUtil.class);
 
-    //Linux allows any characters except /
-    //Windows reserves the following set: < > : " / \ | ? *
-    //Object stores: ???, better be conservative
-    private static final Pattern FILE_NAME_BLACKLIST_CHARACTERS = Pattern.compile("[^\\w\\d \\-_.()\\[\\]$!]");
-
     private static boolean loggedSetFilePermissionsWarning = false;
     
     // When we move to java 7, we can use Files.setPosixFilePermissions
@@ -204,7 +199,4 @@ public class FileUtil {
         }
     }
 
-    public static String getSafeFileName(String str) {
-        return FILE_NAME_BLACKLIST_CHARACTERS.matcher(str).replaceAll("_");
-    }
 }


[13/18] incubator-brooklyn git commit: Catalog versioning - streamline catalog field initialization

Posted by he...@apache.org.
Catalog versioning - streamline catalog field initialization

Implement new requirements for symbolicName, displayName initialization and make the selection logic between fields consistent.


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

Branch: refs/heads/master
Commit: 65995c689262042bade9941b766f5341d4bd1067
Parents: 497496c
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Wed Nov 12 15:31:26 2014 +0200
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 11:49:54 2014 +0200

----------------------------------------------------------------------
 .../catalog/internal/BasicBrooklynCatalog.java  | 131 ++++++++++++-------
 .../brooklyn/catalog/internal/CatalogUtils.java |   2 +-
 2 files changed, 82 insertions(+), 51 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/65995c68/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
index bc43682..3a21114 100644
--- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java
@@ -422,8 +422,6 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     private CatalogItemDtoAbstract<?,?> getAbstractCatalogItem(String yaml) {
         DeploymentPlan plan = makePlanFromYaml(yaml);
 
-        Collection<CatalogBundle> libraries = Collections.emptyList();
-
         @SuppressWarnings("rawtypes")
         Maybe<Map> possibleCatalog = plan.getCustomAttribute("brooklyn.catalog", Map.class, true);
         MutableMap<String, Object> catalog = MutableMap.of();
@@ -433,6 +431,7 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
             catalog.putAll(catalog2);
         }
 
+        Collection<CatalogBundle> libraries = Collections.emptyList();
         Maybe<Object> possibleLibraries = catalog.getMaybe("libraries");
         if (possibleLibraries.isAbsent()) possibleLibraries = catalog.getMaybe("brooklyn.libraries");
         if (possibleLibraries.isPresentAndNonNull()) {
@@ -441,69 +440,101 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
             libraries = CatalogItemDtoAbstract.parseLibraries((Collection<?>) possibleLibraries.get());
         }
 
-        String symbolicName;
-        String version = null;
+        final String id = (String) catalog.getMaybe("id").orNull();
+        final String version = Strings.toString(catalog.getMaybe("version").orNull());
+        final String symbolicName = (String) catalog.getMaybe("symbolicName").orNull();
+        final String name = (String) catalog.getMaybe("name").orNull();
+        final String displayName = (String) catalog.getMaybe("displayName").orNull();
+        final String description = (String) catalog.getMaybe("description").orNull();
+        final String iconUrl = (String) catalog.getMaybe("iconUrl").orNull();
+        final String iconUrlUnderscore = (String) catalog.getMaybe("icon_url").orNull();
+
+        if ((Strings.isNonBlank(id) || Strings.isNonBlank(symbolicName)) && 
+                Strings.isNonBlank(displayName) &&
+                Strings.isNonBlank(name) && !name.equals(displayName)) {
+            log.warn("Name property will be ignored due to the existence of displayName and at least one of id, symbolicName");
+        }
 
-        symbolicName = (String) catalog.getMaybe("symbolicName").orNull();
-        if (Strings.isBlank(symbolicName)) {
-            symbolicName = (String) catalog.getMaybe("id").orNull();
-            if (Strings.isNonBlank(symbolicName) && CatalogUtils.looksLikeVersionedId(symbolicName)) {
-                symbolicName = CatalogUtils.getIdFromVersionedId(symbolicName);
-                version = CatalogUtils.getVersionFromVersionedId(symbolicName);
+        final String catalogSymbolicName;
+        if (Strings.isNonBlank(symbolicName)) {
+            catalogSymbolicName = symbolicName;
+        } else if (Strings.isNonBlank(id)) {
+            if (Strings.isNonBlank(id) && CatalogUtils.looksLikeVersionedId(id)) {
+                catalogSymbolicName = CatalogUtils.getIdFromVersionedId(id);
+            } else {
+                catalogSymbolicName = id;
+            }
+        } else if (Strings.isNonBlank(name)) {
+            catalogSymbolicName = name;
+        } else if (Strings.isNonBlank(plan.getName())) {
+            catalogSymbolicName = plan.getName();
+        } else if (plan.getServices().size()==1) {
+            Service svc = Iterables.getOnlyElement(plan.getServices());
+            if (Strings.isBlank(svc.getServiceType())) {
+                throw new IllegalStateException("CAMP service type not expected to be missing for " + svc);
             }
+            catalogSymbolicName = svc.getServiceType();
+        } else {
+            log.error("Can't infer catalog item symbolicName from the following plan:\n" + yaml);
+            throw new IllegalStateException("Can't infer catalog item symbolicName from catalog item description");
         }
-        if (Strings.isBlank(symbolicName))
-            symbolicName = (String) catalog.getMaybe("name").orNull();
-        // take name from plan if not specified in brooklyn.catalog section not supplied
-        if (Strings.isBlank(symbolicName)) {
-            symbolicName = plan.getName();
-            if (Strings.isBlank(symbolicName)) {
-                if (plan.getServices().size()==1) {
-                    Service svc = Iterables.getOnlyElement(plan.getServices());
-                    symbolicName = svc.getServiceType();
-                }
+
+        final String catalogVersion;
+        if (CatalogUtils.looksLikeVersionedId(id)) {
+            catalogVersion = CatalogUtils.getVersionFromVersionedId(id);
+            if (version != null  && !catalogVersion.equals(version)) {
+                throw new IllegalArgumentException("Discrepency between version set in id " + catalogVersion + " and version property " + version);
             }
+        } else if (Strings.isNonBlank(version)) {
+            catalogVersion = version;
+        } else {
+            log.warn("No version specified for catalog item " + catalogSymbolicName + ". Using default value.");
+            catalogVersion = null;
         }
 
-        Maybe<Object> possibleVersion = catalog.getMaybe("version");
-        if (possibleVersion.isAbsent() && Strings.isBlank(version)) {
-            throw new IllegalArgumentException("'version' attribute missing in 'brooklyn.catalog' section.");
-        } else if (possibleVersion.isPresent()) {
-            //could be coalesced to a number - can be one of Integer, Double, String
-            String versionProperty = possibleVersion.get().toString();
+        final String catalogDisplayName;
+        if (Strings.isNonBlank(displayName)) {
+            catalogDisplayName = displayName;
+        } else if (Strings.isNonBlank(name)) {
+            catalogDisplayName = name;
+        } else if (Strings.isNonBlank(plan.getName())) {
+            catalogDisplayName = plan.getName();
+        } else {
+            catalogDisplayName = null;
+        }
 
-            if (!Strings.isBlank(version) && !versionProperty.equals(version)) {
-                throw new IllegalArgumentException("Discrepency between version set in id/name " + version + " and version property " + versionProperty);
-            }
+        final String catalogDescription;
+        if (Strings.isNonBlank(description)) {
+            catalogDescription = description;
+        } else if (Strings.isNonBlank(plan.getDescription())) {
+            catalogDescription = plan.getDescription();
+        } else {
+            catalogDescription = null;
+        }
 
-            version = versionProperty;
+        final String catalogIconUrl;
+        if (Strings.isNonBlank(iconUrl)) {
+            catalogIconUrl = iconUrl;
+        } else if (Strings.isNonBlank(iconUrlUnderscore)) {
+            catalogIconUrl = iconUrlUnderscore;
+        } else {
+            catalogIconUrl = null;
         }
 
         CatalogUtils.installLibraries(mgmt, libraries);
 
-        AbstractBrooklynObjectSpec<?, ?> spec = createSpec(plan, CatalogUtils.newClassLoadingContext(mgmt, CatalogUtils.getVersionedId(symbolicName, version), libraries, getRootClassLoader()));
+        String versionedId = CatalogUtils.getVersionedId(catalogSymbolicName, catalogVersion);
+        BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt, versionedId, libraries, getRootClassLoader());
+        AbstractBrooklynObjectSpec<?, ?> spec = createSpec(plan, loader);
 
-        CatalogItemBuilder<?> builder = createItemBuilder(spec, symbolicName, version)
+        CatalogItemDtoAbstract<?, ?> dto = createItemBuilder(spec, catalogSymbolicName, catalogVersion)
             .libraries(libraries)
-            .displayName(plan.getName())
-            .description(plan.getDescription())
-            .plan(yaml);
-
-        // and populate other fields
-        Maybe<Object> name = catalog.getMaybe("displayName");
-        if (name.isAbsent()) name = catalog.getMaybe("name");
-        if (name.isAbsent()) name = Maybe.<Object>fromNullable(plan.getName());
-        if (name.isPresent()) builder.displayName((String) name.get());
-
-        Maybe<Object> description = catalog.getMaybe("description");
-        if (description.isPresent()) builder.description((String)description.get());
-
-        Maybe<Object> iconUrl = catalog.getMaybe("iconUrl");
-        if (iconUrl.isAbsent()) iconUrl = catalog.getMaybe("icon_url");
-        if (iconUrl.isPresent()) builder.iconUrl((String)iconUrl.get());
+            .displayName(catalogDisplayName)
+            .description(catalogDescription)
+            .iconUrl(catalogIconUrl)
+            .plan(yaml)
+            .build();
 
-        CatalogItemDtoAbstract<?, ?> dto = builder.build();
-        // Overwrite generated ID
         dto.setManagementContext((ManagementContextInternal) mgmt);
         return dto;
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/65995c68/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
index 24bfe47..cb98dfe 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogUtils.java
@@ -143,7 +143,7 @@ public class CatalogUtils {
     }
 
     public static boolean looksLikeVersionedId(String versionedId) {
-        return versionedId.indexOf(VERSION_DELIMITER) != -1;
+        return versionedId != null && versionedId.indexOf(VERSION_DELIMITER) != -1;
     }
 
     public static String getIdFromVersionedId(String versionedId) {


[14/18] incubator-brooklyn git commit: Catalog versioning - backwards compatibility from rebind and catalog.xml

Posted by he...@apache.org.
Catalog versioning - backwards compatibility from rebind and catalog.xml


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

Branch: refs/heads/master
Commit: 40eee57a1a705caa11e500cf8e24a1aba927d40d
Parents: 65995c6
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Wed Nov 12 19:54:03 2014 +0200
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 11:49:55 2014 +0200

----------------------------------------------------------------------
 .../main/java/brooklyn/catalog/CatalogItem.java |  6 +-
 .../brooklyn/mementos/CatalogItemMemento.java   |  2 +-
 .../main/java/brooklyn/basic/BrooklynTypes.java |  2 +-
 .../internal/CatalogBundleConverter.java        | 59 ++++++++++++++++++
 .../catalog/internal/CatalogItemDo.java         |  6 ++
 .../internal/CatalogItemDtoAbstract.java        | 29 +++------
 .../catalog/internal/CatalogLibrariesDo.java    |  3 +-
 .../catalog/internal/CatalogLibrariesDto.java   | 14 +++--
 .../catalog/internal/CatalogXmlSerializer.java  |  8 +--
 .../rebind/BasicCatalogItemRebindSupport.java   |  2 +-
 .../rebind/dto/BasicCatalogItemMemento.java     | 39 +++++-------
 .../BrooklynMementoPersisterToObjectStore.java  |  5 +-
 .../CatalogItemLibrariesConverter.java          | 64 ++++++++++++++++++++
 .../rebind/persister/XmlMementoSerializer.java  |  6 ++
 .../persister/XmlMementoSerializerTest.java     |  5 ++
 .../brooklyn/catalog/CatalogYamlEntityTest.java |  2 +-
 16 files changed, 192 insertions(+), 60 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/api/src/main/java/brooklyn/catalog/CatalogItem.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/catalog/CatalogItem.java b/api/src/main/java/brooklyn/catalog/CatalogItem.java
index d06a682..d3824fd 100644
--- a/api/src/main/java/brooklyn/catalog/CatalogItem.java
+++ b/api/src/main/java/brooklyn/catalog/CatalogItem.java
@@ -47,7 +47,7 @@ public interface CatalogItem<T,SpecT> extends BrooklynObject, Rebindable {
 
     @Deprecated
     public static interface CatalogItemLibraries {
-        Collection<CatalogBundle> getBundles();
+        Collection<String> getBundles();
     }
 
     public CatalogItemType getCatalogItemType();
@@ -65,6 +65,10 @@ public interface CatalogItem<T,SpecT> extends BrooklynObject, Rebindable {
     @Deprecated
     public String getName();
 
+    /** @deprecated since 0.7.0. Use {@link #getSymbolicName} */
+    @Deprecated
+    public String getRegisteredTypeName();
+
     @Nullable public String getDescription();
 
     @Nullable public String getIconUrl();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/api/src/main/java/brooklyn/mementos/CatalogItemMemento.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/mementos/CatalogItemMemento.java b/api/src/main/java/brooklyn/mementos/CatalogItemMemento.java
index d3163e5..36d497d 100644
--- a/api/src/main/java/brooklyn/mementos/CatalogItemMemento.java
+++ b/api/src/main/java/brooklyn/mementos/CatalogItemMemento.java
@@ -36,7 +36,7 @@ public interface CatalogItemMemento extends Memento {
 
     String getJavaType();
 
-    Collection<CatalogItem.CatalogBundle> getBundles();
+    Collection<CatalogItem.CatalogBundle> getLibraries();
 
     CatalogItem.CatalogItemType getCatalogItemType();
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/core/src/main/java/brooklyn/basic/BrooklynTypes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/basic/BrooklynTypes.java b/core/src/main/java/brooklyn/basic/BrooklynTypes.java
index 1a0049c..483cc2b 100644
--- a/core/src/main/java/brooklyn/basic/BrooklynTypes.java
+++ b/core/src/main/java/brooklyn/basic/BrooklynTypes.java
@@ -90,7 +90,7 @@ public class BrooklynTypes {
         if (Entity.class.isAssignableFrom(brooklynClass)) {
             type = new ImmutableEntityType((Class<? extends Entity>)brooklynClass);
         } else if (Location.class.isAssignableFrom(brooklynClass)) {
-            type = new ImmutableEntityType((Class)brooklynClass);
+            type = new ImmutableEntityType((Class<? extends Entity>)brooklynClass);
         } else if (Policy.class.isAssignableFrom(brooklynClass)) {
             type = new PolicyDynamicType((Class<? extends Policy>)brooklynClass); // TODO immutable?
         } else if (Enricher.class.isAssignableFrom(brooklynClass)) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/core/src/main/java/brooklyn/catalog/internal/CatalogBundleConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogBundleConverter.java b/core/src/main/java/brooklyn/catalog/internal/CatalogBundleConverter.java
new file mode 100644
index 0000000..fe77d02
--- /dev/null
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogBundleConverter.java
@@ -0,0 +1,59 @@
+/*
+ * 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 brooklyn.catalog.internal;
+
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
+import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import com.thoughtworks.xstream.mapper.Mapper;
+
+
+@Deprecated
+public class CatalogBundleConverter implements Converter {
+
+    private ReflectionConverter delegateConverter;
+
+    public CatalogBundleConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
+        this.delegateConverter = new ReflectionConverter(mapper, reflectionProvider);
+    }
+
+    @Override
+    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
+        return type == CatalogBundleDto.class;
+    }
+
+    @Override
+    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
+        context.convertAnother(source, delegateConverter);
+    }
+
+    @Override
+    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+        if (reader.hasMoreChildren()) {
+            return context.convertAnother(context.currentObject(), CatalogBundleDto.class, delegateConverter);
+        } else {
+            return new CatalogBundleDto(null, null, reader.getValue());
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java
index 0b25785..f40a49b 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDo.java
@@ -77,6 +77,12 @@ public class CatalogItemDo<T,SpecT> implements CatalogItem<T,SpecT> {
         return getDisplayName();
     }
 
+    @Deprecated
+    @Override
+    public String getRegisteredTypeName() {
+        return getSymbolicName();
+    }
+
     @Override
     public String getDisplayName() {
         return itemDto.getDisplayName();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
index f10aee1..9b434cc 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
@@ -50,12 +50,7 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
     private @SetFromFlag String symbolicName;
     private @SetFromFlag String version = BasicBrooklynCatalog.NO_VERSION;
 
-    /**@deprecated since 0.7.0, left for deserialization backwards compatibility */
-    private @Deprecated @SetFromFlag String registeredTypeName;
-
     private @SetFromFlag String displayName;
-    /**@deprecated since 0.7.0, left for deserialization backwards compatibility */
-    private @Deprecated @SetFromFlag String name;
     private @SetFromFlag String description;
     private @SetFromFlag String iconUrl;
 
@@ -64,9 +59,7 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
     private @Deprecated @SetFromFlag String type;
     private @SetFromFlag String planYaml;
 
-    private @SetFromFlag Collection<CatalogBundle> bundles;
-    /**@deprecated since 0.7.0, left for deserialization backwards compatibility */
-    private @Deprecated @SetFromFlag CatalogLibrariesDto libraries;
+    private @SetFromFlag Collection<CatalogBundle> libraries;
     private @SetFromFlag Set<Object> tags = Sets.newLinkedHashSet();
 
     @Override
@@ -90,10 +83,14 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
         return getDisplayName();
     }
 
+    @Deprecated
+    public String getRegisteredTypeName() {
+        return getSymbolicName();
+    }
+
     @Override
     public String getDisplayName() {
-        if (displayName != null) return displayName;
-        return name;
+        return displayName;
     }
 
     @Override
@@ -109,7 +106,6 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
     @Override
     public String getSymbolicName() {
         if (symbolicName != null) return symbolicName;
-        if (registeredTypeName != null) return registeredTypeName;
         return getJavaType();
     }
 
@@ -127,12 +123,9 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
 
     @Nonnull
     @Override
-    @SuppressWarnings("deprecation")
     public Collection<CatalogBundle> getLibraries() {
-        if (bundles != null) {
-            return ImmutableList.copyOf(bundles);
-        } else if (libraries != null && libraries.getBundles() != null) {
-            return ImmutableList.copyOf(libraries.getBundles());
+        if (libraries != null) {
+            return ImmutableList.copyOf(libraries);
         } else {
             return Collections.emptyList();
         }
@@ -172,7 +165,6 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
     @Override
     public void setDisplayName(String newName) {
         this.displayName = newName;
-        this.name = null;
     }
 
     @Override
@@ -263,7 +255,6 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
 
     protected void setSymbolicName(String symbolicName) {
         this.symbolicName = symbolicName;
-        this.registeredTypeName = null;
     }
 
     protected void setVersion(String version) {
@@ -288,7 +279,7 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
     }
 
     protected void setLibraries(Collection<CatalogBundle> libraries) {
-        this.bundles = libraries;
+        this.libraries = libraries;
     }
 
     protected void setTags(Set<Object> tags) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java
index 1fda2d7..2589cb2 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDo.java
@@ -21,7 +21,6 @@ package brooklyn.catalog.internal;
 import java.util.Collection;
 
 import brooklyn.catalog.CatalogItem;
-import brooklyn.catalog.CatalogItem.CatalogBundle;
 
 import com.google.common.base.Preconditions;
 
@@ -36,7 +35,7 @@ public class CatalogLibrariesDo implements CatalogItem.CatalogItemLibraries {
     }
 
     @Override
-    public Collection<CatalogBundle> getBundles() {
+    public Collection<String> getBundles() {
         return librariesDto.getBundles();
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java
index 8403876..dea135b 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogLibrariesDto.java
@@ -20,23 +20,29 @@ package brooklyn.catalog.internal;
 
 import java.util.Collection;
 import java.util.Collections;
-import java.util.concurrent.CopyOnWriteArrayList;
 
 import brooklyn.catalog.CatalogItem;
-import brooklyn.catalog.CatalogItem.CatalogBundle;
 
 import com.google.common.collect.ImmutableList;
 
 @Deprecated
 public class CatalogLibrariesDto implements CatalogItem.CatalogItemLibraries {
 
-    private Collection<CatalogBundle> bundles = new CopyOnWriteArrayList<CatalogBundle>();
+    private final Collection<String> bundles;
+
+    public CatalogLibrariesDto() {
+        this.bundles = Collections.emptyList();
+    }
+
+    public CatalogLibrariesDto(Collection<String> bundles) {
+        this.bundles = bundles;
+    }
 
     /**
      * @return An immutable copy of the bundle URLs referenced by this object
      */
     @Override
-    public Collection<CatalogBundle> getBundles() {
+    public Collection<String> getBundles() {
         if (bundles == null) {
             // can be null on deserialization
             return Collections.emptyList();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java b/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java
index 8871edf..46c014f 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogXmlSerializer.java
@@ -50,17 +50,17 @@ public class CatalogXmlSerializer extends XmlSerializer<Object> {
         xstream.aliasType("entity", CatalogEntityItemDto.class);
         xstream.aliasType("policy", CatalogPolicyItemDto.class);
 
-        xstream.aliasAttribute(CatalogItemDtoAbstract.class, "javaType", "type");
+        xstream.aliasField("registeredType", CatalogItemDtoAbstract.class, "symbolicName");
         xstream.aliasAttribute(CatalogItemDtoAbstract.class, "displayName", "name");
+        xstream.useAttributeFor(CatalogItemDtoAbstract.class, "type");
         xstream.useAttributeFor(CatalogItemDtoAbstract.class, "version");
+        xstream.aliasType("bundle", CatalogBundleDto.class);
+        xstream.registerConverter(new CatalogBundleConverter(xstream.getMapper(), xstream.getReflectionProvider()));
 
         xstream.useAttributeFor(CatalogClasspathDto.class, "scan");
         xstream.addImplicitCollection(CatalogClasspathDto.class, "entries", "entry", String.class);
         xstream.registerConverter(new EnumCaseForgivingSingleValueConverter(CatalogScanningModes.class));
 
-        xstream.aliasType("libraries", CatalogLibrariesDto.class);
-        xstream.addImplicitCollection(CatalogLibrariesDto.class, "bundles", "bundle", CatalogBundleDto.class);
-
         // Note: the management context is being omitted because it is unnecessary for
         // representations of catalogues generated with this serializer.
         xstream.omitField(AbstractBrooklynObject.class, "managementContext");

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java b/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java
index 61b9511..8ef2931 100644
--- a/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java
+++ b/core/src/main/java/brooklyn/entity/rebind/BasicCatalogItemRebindSupport.java
@@ -48,7 +48,7 @@ public class BasicCatalogItemRebindSupport extends AbstractBrooklynObjectRebindS
                 .put("description", memento.getDescription())
                 .put("iconUrl", memento.getIconUrl())
                 .put("version", memento.getVersion())
-                .put("bundles", memento.getBundles())
+                .put("libraries", memento.getLibraries())
                 .put("planYaml", memento.getPlanYaml())
                 .build(), instance);
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/core/src/main/java/brooklyn/entity/rebind/dto/BasicCatalogItemMemento.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/dto/BasicCatalogItemMemento.java b/core/src/main/java/brooklyn/entity/rebind/dto/BasicCatalogItemMemento.java
index b32e841..5c07c65 100644
--- a/core/src/main/java/brooklyn/entity/rebind/dto/BasicCatalogItemMemento.java
+++ b/core/src/main/java/brooklyn/entity/rebind/dto/BasicCatalogItemMemento.java
@@ -25,14 +25,14 @@ import java.util.Map;
 
 import org.codehaus.jackson.annotate.JsonAutoDetect;
 
-import com.google.common.base.Joiner;
-import com.google.common.base.Objects;
-
 import brooklyn.catalog.CatalogItem;
-import brooklyn.catalog.CatalogItem.CatalogBundle;
 import brooklyn.catalog.internal.BasicBrooklynCatalog;
+import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.mementos.CatalogItemMemento;
 
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+
 @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)
 public class BasicCatalogItemMemento extends AbstractMemento implements CatalogItemMemento, Serializable {
 
@@ -112,7 +112,7 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
             javaType = other.getJavaType();
             version = other.getVersion();
             planYaml = other.getPlanYaml();
-            libraries = other.getBundles();
+            libraries = other.getLibraries();
             catalogItemType = other.getCatalogItemType();
             catalogItemJavaType = other.getCatalogItemJavaType();
             specType = other.getSpecType();
@@ -130,12 +130,7 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
     private String javaType;
     private String version;
     private String planYaml;
-    //Keep libraries for deserialization compatibility and
-    //introduce bundles to hold the new libraries type from
-    //catalog item
-    private Collection<CatalogItem.CatalogBundle> bundles;
-    @SuppressWarnings("deprecation")
-    private CatalogItem.CatalogItemLibraries libraries;
+    private Collection<CatalogItem.CatalogBundle> libraries;
     private CatalogItem.CatalogItemType catalogItemType;
     private Class<?> catalogItemJavaType;
     private Class<?> specType;
@@ -150,8 +145,7 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
         this.iconUrl = builder.iconUrl;
         this.version = builder.version;
         this.planYaml = builder.planYaml;
-        this.bundles = builder.libraries;
-        this.libraries = null;
+        this.libraries = builder.libraries;
         this.catalogItemJavaType = builder.catalogItemJavaType;
         this.catalogItemType = builder.catalogItemType;
         this.specType = builder.specType;
@@ -159,6 +153,11 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
     }
 
     @Override
+    public String getId() {
+        return CatalogUtils.getVersionedId(getSymbolicName(), getVersion());
+    }
+
+    @Override
     public String getDescription() {
         return description;
     }
@@ -193,16 +192,8 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
     }
 
     @Override
-    public Collection<CatalogItem.CatalogBundle> getBundles() {
-        if (bundles != null) {
-            return bundles;
-        } else if (libraries != null) {
-            @SuppressWarnings("deprecation")
-            Collection<CatalogBundle> b = libraries.getBundles();
-            return b;
-        } else {
-            return null;
-        }
+    public Collection<CatalogItem.CatalogBundle> getLibraries() {
+        return libraries;
     }
 
     @Override
@@ -241,7 +232,7 @@ public class BasicCatalogItemMemento extends AbstractMemento implements CatalogI
                 .add("iconUrl", getIconUrl())
                 .add("version", getVersion())
                 .add("planYaml", getPlanYaml())
-                .add("bundles", getBundles())
+                .add("libraries", getLibraries())
                 .add("catalogItemJavaType", getCatalogItemJavaType())
                 .add("catalogItemType", getCatalogItemType())
                 .add("javaType", getJavaType())

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
index 38cc12d..de691e0 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
@@ -248,8 +248,9 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
                 }
                 
                 String xmlId = (String) XmlUtil.xpath(contents, "/"+type.toCamelCase()+"/id");
-                if (!Objects.equal(id, xmlId))
-                    LOG.warn("ID mismatch on "+type.toCamelCase()+", "+id+" from path, "+xmlId+" from xml");
+                String safeXmlId = Strings.makeValidFilename(xmlId);
+                if (!Objects.equal(id, safeXmlId))
+                    LOG.warn("ID mismatch on "+type.toCamelCase()+", "+id+" from path, "+safeXmlId+" from xml");
                 
                 builder.put(type, xmlId, contents);
             }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/core/src/main/java/brooklyn/entity/rebind/persister/CatalogItemLibrariesConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/CatalogItemLibrariesConverter.java b/core/src/main/java/brooklyn/entity/rebind/persister/CatalogItemLibrariesConverter.java
new file mode 100644
index 0000000..ea0399b
--- /dev/null
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/CatalogItemLibrariesConverter.java
@@ -0,0 +1,64 @@
+/*
+ * 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 brooklyn.entity.rebind.persister;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import brooklyn.catalog.CatalogItem.CatalogBundle;
+import brooklyn.catalog.CatalogItem.CatalogItemLibraries;
+import brooklyn.catalog.internal.CatalogBundleDto;
+
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+
+@Deprecated
+public class CatalogItemLibrariesConverter implements Converter {
+
+    @Override
+    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
+        return CatalogItemLibraries.class.isAssignableFrom(type) ||
+                Collection.class.isAssignableFrom(type);
+    }
+
+    @Override
+    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
+        context.convertAnother(source);
+    }
+
+    @Override
+    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+        Object obj = context.convertAnother(context.currentObject(), context.getRequiredType());
+        if (CatalogItemLibraries.class.isAssignableFrom(context.getRequiredType())) {
+            CatalogItemLibraries libs = (CatalogItemLibraries)obj;
+            Collection<String> bundles = libs.getBundles();
+            Collection<CatalogBundle> libraries = new ArrayList<CatalogBundle>(bundles.size());
+            for (String url : bundles) {
+                libraries.add(new CatalogBundleDto(null, null, url));
+            }
+            return libraries;
+        } else {
+            return obj;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java b/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
index a179479..533cfd2 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java
@@ -28,6 +28,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.catalog.CatalogItem;
+import brooklyn.catalog.internal.CatalogBundleDto;
 import brooklyn.entity.Effector;
 import brooklyn.entity.Entity;
 import brooklyn.entity.Feed;
@@ -90,6 +91,7 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento
         xstream.alias("enricher", BasicEnricherMemento.class);
         xstream.alias("configKey", BasicConfigKey.class);
         xstream.alias("catalogItem", BasicCatalogItemMemento.class);
+        xstream.alias("bundle", CatalogBundleDto.class);
         xstream.alias("attributeSensor", BasicAttributeSensor.class);
 
         xstream.alias("effector", Effector.class);
@@ -112,6 +114,10 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento
         xstream.registerConverter(new ManagementContextConverter());
         
         xstream.registerConverter(new TaskConverter(xstream.getMapper()));
+    
+        //For compatibility with existing persistence stores content.
+        xstream.aliasField("registeredTypeName", BasicCatalogItemMemento.class, "symbolicName");
+        xstream.registerLocalConverter(BasicCatalogItemMemento.class, "libraries", new CatalogItemLibrariesConverter());
     }
     
     // Warning: this is called in the super-class constuctor, so before this constructor!

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java b/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
index 7ae59f6..38a0456 100644
--- a/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/persister/XmlMementoSerializerTest.java
@@ -20,6 +20,10 @@ package brooklyn.entity.rebind.persister;
 
 import static org.testng.Assert.assertEquals;
 
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.nio.charset.Charset;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
@@ -51,6 +55,7 @@ import brooklyn.test.entity.TestEntity;
 import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
+import brooklyn.util.stream.Streams;
 
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/40eee57a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
index 4ce917b..a980fc4 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/catalog/CatalogYamlEntityTest.java
@@ -25,7 +25,7 @@ import io.brooklyn.camp.brooklyn.AbstractYamlTest;
 
 import java.util.Collection;
 
-import org.junit.Assert;
+import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import brooklyn.catalog.BrooklynCatalog;


[03/18] incubator-brooklyn git commit: Catalog versioning - JSGUI

Posted by he...@apache.org.
Catalog versioning - JSGUI


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

Branch: refs/heads/master
Commit: 1e8878354f00671e2b74b85040f6710733197201
Parents: 91f1611
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Fri Jul 18 19:32:32 2014 +0300
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Thu Nov 13 11:49:48 2014 +0200

----------------------------------------------------------------------
 .../internal/CatalogItemDtoAbstract.java        | 10 +++-
 usage/jsgui/src/main/webapp/assets/css/base.css |  9 ++-
 .../src/main/webapp/assets/js/model/entity.js   | 15 +++++
 .../src/main/webapp/assets/js/view/catalog.js   | 62 +++++++++++++++-----
 .../assets/tpl/catalog/details-entity.html      |  8 ++-
 .../webapp/assets/tpl/catalog/nav-entry.html    |  2 +-
 .../rest/domain/CatalogEntitySummary.java       |  5 +-
 .../rest/domain/CatalogItemSummary.java         | 28 ++++++---
 .../rest/domain/CatalogPolicySummary.java       |  5 +-
 .../rest/transform/CatalogTransformer.java      |  6 +-
 .../rest/resources/CatalogResourceTest.java     |  8 ++-
 11 files changed, 120 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1e887835/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
index e78721a..3679373 100644
--- a/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
+++ b/core/src/main/java/brooklyn/catalog/internal/CatalogItemDtoAbstract.java
@@ -105,7 +105,13 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
 
     @Override
     public String getVersion() {
-        return version;
+        //Deserializing doesn't call the constructor
+        //so the property may be null.
+        if (version != null) {
+            return version;
+        } else {
+            return BasicBrooklynCatalog.NO_VERSION;
+        }
     }
 
     @Nonnull
@@ -125,7 +131,7 @@ public abstract class CatalogItemDtoAbstract<T, SpecT> extends AbstractBrooklynO
 
     @Override
     public String toString() {
-        return getClass().getSimpleName()+"["+getId()+"/"+getName()+"]";
+        return getClass().getSimpleName()+"["+getId()+":"+getVersion()+"/"+getName()+"]";
     }
 
     public abstract Class<SpecT> getSpecType();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1e887835/usage/jsgui/src/main/webapp/assets/css/base.css
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/css/base.css b/usage/jsgui/src/main/webapp/assets/css/base.css
index c567ecb..cb988b6 100644
--- a/usage/jsgui/src/main/webapp/assets/css/base.css
+++ b/usage/jsgui/src/main/webapp/assets/css/base.css
@@ -1119,6 +1119,13 @@ tr.app-add-wizard-config-entry {
 .accordion-nav-row.active {
     font-weight: bold;
 }
+.accordion-nav-child {
+    padding-left: 15px;
+    color: lightgray;
+}
+.accordion-nav-child.active {
+    color: #505050;
+}
 .catalog-details {
     padding-left: 16px;
     padding-right: 25px;
@@ -1435,4 +1442,4 @@ textarea.param-value {
 
 #catalog-details-accordion {
     margin-top: 12px;
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1e887835/usage/jsgui/src/main/webapp/assets/js/model/entity.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/model/entity.js b/usage/jsgui/src/main/webapp/assets/js/model/entity.js
index 2d10bb9..547e329 100644
--- a/usage/jsgui/src/main/webapp/assets/js/model/entity.js
+++ b/usage/jsgui/src/main/webapp/assets/js/model/entity.js
@@ -28,6 +28,21 @@ define(["underscore", "backbone"], function (_, Backbone) {
                 config:{}
             }
         },
+        getVersionedAttr: function(name) {
+            var attr = this.get(name);
+            var version = this.get('version');
+            if (version && version != '0.0.0') {
+                return attr + ':' + version;
+            } else {
+                return attr;
+            }
+        },
+        url: function() {
+            var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError();
+            if (this.isNew()) return base;
+            return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + 
+                encodeURIComponent(this.get("symbolicName")) + '/' + encodeURIComponent(this.get("version"));
+        },
         getConfigByName:function (key) {
             if (key) return this.get("config")[key]
         },

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1e887835/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/catalog.js b/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
index 9875f73..cb83b3b 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/catalog.js
@@ -283,7 +283,7 @@ define([
             // Returns template applied to function arguments. Alter if collection altered.
             // Will be run in the context of the AccordionItemView.
             this.entryTemplateArgs = this.options.entryTemplateArgs || function(model, index) {
-                return {type: model.get("type"), id: model.get("id")};
+                return {type: model.getVersionedAttr("type"), id: model.get("id")};
             };
 
             // undefined argument is used for existing model items
@@ -313,19 +313,24 @@ define([
             return this;
         },
 
+        singleItemTempalter: function(name, active, isChild, model, index) {
+            var args = _.extend({
+                    cid: model.cid,
+                    isChild: isChild,
+                    extraClasses: (activeDetailsView == name && model.cid == active) ? "active" : ""
+                }, this.entryTemplateArgs(model));
+            return this.template(args);
+        },
+
         renderEntries: function() {
-            var name = this.name, active = this.activeCid;
-            var templater = function(model, index) {
-                var args = _.extend({
-                        cid: model.cid,
-                        extraClasses: (activeDetailsView == name && model.cid == active) ? "active" : ""
-                    }, this.entryTemplateArgs(model));
-                return this.template(args);
-            };
-            var elements = this.collection.map(templater, this);
+            var elements = this.collection.map(_.partial(this.singleItemTemplater, this.name, this.activeCid, false), this);
+            this.updateContent(elements.join(''));
+        },
+
+        updateContent: function(markup) {
             this.$(".accordion-body")
                 .empty()
-                .append(elements.join(''));
+                .append(markup);
         },
 
         refresh: function() {
@@ -362,6 +367,35 @@ define([
             }
         }
     });
+    
+    var AccordionEntityView = AccordionItemView.extend({
+        renderEntries: function() {
+            var symbolicNameFn = function(model) {return model.get("symbolicName")};
+            var groups = this.collection.groupBy(symbolicNameFn);
+            var orderedIds = _.uniq(this.collection.map(symbolicNameFn), true);
+
+            function getLatestStableVersion(items) {
+                //TODO implement more sophisticated "default" version selection
+                //Could let the server choose it
+                return items[items.length-1];
+            }
+
+            var catalogTree = orderedIds.map(function(symbolicName) {
+                var group = groups[symbolicName];
+                var root = getLatestStableVersion(group);
+                var children = _.reject(group, function(model) {return root.id == model.id;});
+                return {root: root, children: children};
+            });
+
+            var templater = function(memo, item, index) {
+                memo.push(this.singleItemTemplater(false, item.root));
+                return memo.concat(_.map(item.children, _.partial(this.singleItemTemplater, true), this));
+            };
+
+            var elements = _.reduce(catalogTree, templater, [], this);
+            this.updateContent(elements.join(''));
+        }
+    });
 
     // Controls whole page. Parent of accordion items and details view.
     var CatalogResourceView = Backbone.View.extend({
@@ -381,21 +415,21 @@ define([
             // `this' will not be set correctly for the onItemSelected callbacks.
             _.bindAll(this);
             this.accordion = this.options.accordion || {
-                "applications": new AccordionItemView({
+                "applications": new AccordionEntityView({
                     name: "applications",
                     singular: "application",
                     onItemSelected: _.partial(this.showCatalogItem, DetailsEntityHtml),
                     model: Entity.Model,
                     autoOpen: !this.options.kind || this.options.kind == "applications"
                 }),
-                "entities": new AccordionItemView({
+                "entities": new AccordionEntityView({
                     name: "entities",
                     singular: "entity",
                     onItemSelected: _.partial(this.showCatalogItem, DetailsEntityHtml),
                     model: Entity.Model,
                     autoOpen: this.options.kind == "entities"
                 }),
-                "policies": new AccordionItemView({
+                "policies": new AccordionEntityView({
                     onItemSelected: _.partial(this.showCatalogItem, DetailsEntityHtml),
                     name: "policies",
                     singular: "policy",

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1e887835/usage/jsgui/src/main/webapp/assets/tpl/catalog/details-entity.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/catalog/details-entity.html b/usage/jsgui/src/main/webapp/assets/tpl/catalog/details-entity.html
index b8bc86f..f123913 100644
--- a/usage/jsgui/src/main/webapp/assets/tpl/catalog/details-entity.html
+++ b/usage/jsgui/src/main/webapp/assets/tpl/catalog/details-entity.html
@@ -18,9 +18,11 @@ under the License.
 -->
 <div class="catalog-details">
 
-    <h2><%- model.get("name") %></h2>
-    <% if (model.get("registeredType") && (model.get("name") != model.get("registeredType"))) { %>
-        <p><%- model.get("registeredType") %></p>
+    <% if (model.get("name") != model.get("symbolicName")) { %>
+        <h2><%- model.get("name") %></h2>
+        <p><%- model.getVersionedAttr("symbolicName") %></p>
+    <% } else { %>
+        <h2><%- model.getVersionedAttr("symbolicName") %></h2>
     <% } %>
     <% if (model.get("description")) { %>
         <p><%- model.get("description") %></p>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1e887835/usage/jsgui/src/main/webapp/assets/tpl/catalog/nav-entry.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/catalog/nav-entry.html b/usage/jsgui/src/main/webapp/assets/tpl/catalog/nav-entry.html
index 36a8588..e739704 100644
--- a/usage/jsgui/src/main/webapp/assets/tpl/catalog/nav-entry.html
+++ b/usage/jsgui/src/main/webapp/assets/tpl/catalog/nav-entry.html
@@ -16,4 +16,4 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 -->
-<div data-cid="<%= cid %>" class="accordion-nav-row <%= extraClasses %>"><%- type %></div>
+<div data-cid="<%= cid %>" class="accordion-nav-row <%= extraClasses %> <%= isChild ? 'accordion-nav-child' : '' %>"><%- type %></div>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1e887835/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogEntitySummary.java
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogEntitySummary.java b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogEntitySummary.java
index 928530c..a0e6017 100644
--- a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogEntitySummary.java
+++ b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogEntitySummary.java
@@ -31,7 +31,8 @@ public class CatalogEntitySummary extends CatalogItemSummary {
     private final Set<EffectorSummary> effectors;
 
     public CatalogEntitySummary(
-            @JsonProperty("id") String id,
+            @JsonProperty("symbolicName") String symbolicName,
+            @JsonProperty("version") String version,
             @JsonProperty("name") String name,
             @JsonProperty("registeredType") String registeredType,
             @JsonProperty("javaType") String javaType,
@@ -44,7 +45,7 @@ public class CatalogEntitySummary extends CatalogItemSummary {
             @JsonProperty("effectors") Set<EffectorSummary> effectors,
             @JsonProperty("links") Map<String, URI> links
         ) {
-        super(id, name, registeredType, javaType, type, planYaml, description, iconUrl, links);
+        super(symbolicName, version, name, registeredType, javaType, type, planYaml, description, iconUrl, links);
         this.config = config;
         this.sensors = sensors;
         this.effectors = effectors;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1e887835/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogItemSummary.java
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogItemSummary.java b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogItemSummary.java
index 61e54a8..042e659 100644
--- a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogItemSummary.java
+++ b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogItemSummary.java
@@ -33,7 +33,8 @@ import com.google.common.collect.ImmutableMap;
  * see also, subclasses */
 public class CatalogItemSummary implements HasId, HasName {
 
-    private final String id;
+    private final String symbolicName;
+    private final String version;
     
     // TODO too many types, see in CatalogItem
     private final String type;
@@ -50,7 +51,8 @@ public class CatalogItemSummary implements HasId, HasName {
     private final Map<String, URI> links;
 
     public CatalogItemSummary(
-            @JsonProperty("id") String id,
+            @JsonProperty("symbolicName") String symbolicName,
+            @JsonProperty("version") String version,
             @JsonProperty("name") String name,
             @JsonProperty("registeredType") String registeredType,
             @JsonProperty("javaType") String javaType,
@@ -60,7 +62,8 @@ public class CatalogItemSummary implements HasId, HasName {
             @JsonProperty("iconUrl") String iconUrl,
             @JsonProperty("links") Map<String, URI> links
         ) {
-        this.id = id;
+        this.symbolicName = symbolicName;
+        this.version = version;
         this.name = name;
         this.javaType = javaType;
         this.registeredType = registeredType;
@@ -70,10 +73,18 @@ public class CatalogItemSummary implements HasId, HasName {
         this.iconUrl = iconUrl;
         this.links = (links == null) ? ImmutableMap.<String, URI>of() : ImmutableMap.copyOf(links);
     }
-    
+
     @Override
     public String getId() {
-        return id;
+        return symbolicName + ":" + version;
+    }
+
+    public String getSymbolicName() {
+        return symbolicName;
+    }
+
+    public String getVersion() {
+        return version;
     }
 
     public String getType() {
@@ -111,12 +122,15 @@ public class CatalogItemSummary implements HasId, HasName {
 
     @Override
     public String toString() {
-        return Objects.toStringHelper(this).add("id", id).toString();
+        return Objects.toStringHelper(this)
+                .add("id", symbolicName)
+                .add("version", version)
+                .toString();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(id, name, type);
+        return Objects.hashCode(symbolicName, version, name, type);
     }
     
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1e887835/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java
index 1625095..513cfa0 100644
--- a/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java
+++ b/usage/rest-api/src/main/java/brooklyn/rest/domain/CatalogPolicySummary.java
@@ -31,7 +31,8 @@ public class CatalogPolicySummary extends CatalogItemSummary {
     private final Set<PolicyConfigSummary> config;
 
     public CatalogPolicySummary(
-            @JsonProperty("id") String id,
+            @JsonProperty("symbolicName") String symbolicName,
+            @JsonProperty("version") String version,
             @JsonProperty("name") String name,
             @JsonProperty("type") String type,
             @JsonProperty("planYaml") String planYaml,
@@ -40,7 +41,7 @@ public class CatalogPolicySummary extends CatalogItemSummary {
             @JsonProperty("config") Set<PolicyConfigSummary> config,
             @JsonProperty("links") Map<String, URI> links
         ) {
-        super(id, name, type, type, type, planYaml, description, iconUrl, links);
+        super(symbolicName, version, name, type, type, type, planYaml, description, iconUrl, links);
         // TODO expose config from policies
         this.config = (config == null) ? ImmutableSet.<PolicyConfigSummary>of() : config;
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1e887835/usage/rest-server/src/main/java/brooklyn/rest/transform/CatalogTransformer.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/transform/CatalogTransformer.java b/usage/rest-server/src/main/java/brooklyn/rest/transform/CatalogTransformer.java
index 9f232d9..3634868 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/transform/CatalogTransformer.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/transform/CatalogTransformer.java
@@ -70,7 +70,7 @@ public class CatalogTransformer {
         for (Effector<?> x: type.getEffectors())
             effectors.add(EffectorTransformer.effectorSummaryForCatalog(x));
 
-        return new CatalogEntitySummary(item.getId(), item.getName(),
+        return new CatalogEntitySummary(item.getId(), item.getVersion(), item.getDisplayName(),
             item.getRegisteredTypeName(), item.getJavaType(), 
             item.getRegisteredTypeName(),
             item.getPlanYaml(),
@@ -80,7 +80,7 @@ public class CatalogTransformer {
     }
 
     public static CatalogItemSummary catalogItemSummary(BrooklynRestResourceUtils b, CatalogItem<?,?> item) {
-        return new CatalogItemSummary(item.getId(), item.getName(), 
+        return new CatalogItemSummary(item.getId(), item.getVersion(), item.getDisplayName(), 
                 item.getRegisteredTypeName(), item.getJavaType(), 
                 item.getRegisteredTypeName(),
                 item.getPlanYaml(),
@@ -89,7 +89,7 @@ public class CatalogTransformer {
 
     public static CatalogPolicySummary catalogPolicySummary(BrooklynRestResourceUtils b, CatalogItem<? extends Policy,PolicySpec<?>> item) {
         Set<PolicyConfigSummary> config = ImmutableSet.of();
-        return new CatalogPolicySummary(item.getId(), item.getName(), item.getRegisteredTypeName(),
+        return new CatalogPolicySummary(item.getId(), item.getVersion(), item.getDisplayName(), item.getRegisteredTypeName(),
                 item.getPlanYaml(),
                 item.getDescription(), tidyIconLink(b, item, item.getIconUrl()), config,
                 makeLinks(item));

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/1e887835/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
index 993a7bd..771f8ff 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/resources/CatalogResourceTest.java
@@ -99,7 +99,8 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
     Assert.assertNotNull(entityItem.getPlanYaml());
     Assert.assertTrue(entityItem.getPlanYaml().contains("brooklyn.test.entity.TestEntity"));
     
-    assertEquals(entityItem.getId(), registeredTypeName);
+    assertEquals(entityItem.getSymbolicName(), registeredTypeName);
+    assertEquals(entityItem.getVersion(), TEST_VERSION);
     
     // and internally let's check we have libraries
     CatalogItem<?, ?> item = getManagementContext().getCatalog().getCatalogItem(registeredTypeName, TEST_VERSION);
@@ -141,7 +142,8 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
     assertEquals(entityItem.getRegisteredType(), registeredTypeName);
     Assert.assertNotNull(entityItem.getPlanYaml());
     Assert.assertTrue(entityItem.getPlanYaml().contains(policyType));
-    assertEquals(entityItem.getId(), registeredTypeName);
+    assertEquals(entityItem.getSymbolicName(), registeredTypeName);
+    assertEquals(entityItem.getVersion(), TEST_VERSION);
   }
 
   @Test
@@ -184,7 +186,7 @@ public class CatalogResourceTest extends BrooklynRestResourceTest {
               URI.create("/v1/catalog/entities/brooklyn.entity.nosql.redis.RedisStore"))
               .get(CatalogEntitySummary.class);
       assertTrue(details.toString().contains("redis.port"), "expected more config, only got: "+details);
-      String iconUrl = "/v1/catalog/icon/" + details.getId();
+      String iconUrl = "/v1/catalog/icon/" + details.getSymbolicName();
       assertTrue(details.getIconUrl().contains(iconUrl), "expected brooklyn URL for icon image, but got: "+details.getIconUrl());
   }