Add checksum to managed bundle so we can tell if it changes and fail accordingly

This permits us to allow the same BOM YAML to be added multiple times (whether 
or not bundles are declared), except where it's a non-snapshot BOM that doesn't 
declare a bundle, because the containing bundle will be different there.
This also fixes a bug where containingBundle wasn't persisted; not a big 
problem as we re-read bundle items, but cleaner to have it working.


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

Branch: refs/heads/master
Commit: e7dc875af19138261f95994c5da08a2187ee64c7
Parents: 2165778
Author: Alex Heneveld <[email protected]>
Authored: Tue Jun 27 15:38:15 2017 +0100
Committer: Alex Heneveld <[email protected]>
Committed: Tue Jun 27 15:39:28 2017 +0100

----------------------------------------------------------------------
 .../rebind/mementos/CatalogItemMemento.java     |  2 ++
 .../brooklyn/api/typereg/ManagedBundle.java     |  6 +++-
 .../CatalogOsgiVersionMoreEntityRebindTest.java | 32 +++++++++++++----
 .../catalog/CatalogOsgiYamlEntityTest.java      | 13 ++++---
 .../catalog/internal/BasicBrooklynCatalog.java  |  6 ++--
 .../core/mgmt/ha/OsgiArchiveInstaller.java      |  8 +++++
 .../mgmt/ha/OsgiBundleInstallationResult.java   |  1 -
 .../brooklyn/core/mgmt/ha/OsgiManager.java      |  2 ++
 .../rebind/BasicCatalogItemRebindSupport.java   |  1 +
 .../rebind/dto/BasicCatalogItemMemento.java     | 14 ++++++++
 .../mgmt/rebind/dto/MementosGenerators.java     |  2 ++
 .../core/typereg/BasicManagedBundle.java        | 16 ++++++++-
 .../main/resources/brooklyn/empty.catalog.bom   |  4 ++-
 .../apache/brooklyn/util/stream/Streams.java    | 21 ++++++++++++
 .../brooklyn/util/stream/StreamsTest.java       | 36 ++++++++++++++++++++
 .../TestResourceUnavailableException.java       |  6 ++--
 16 files changed, 149 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/CatalogItemMemento.java
----------------------------------------------------------------------
diff --git 
a/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/CatalogItemMemento.java
 
b/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/CatalogItemMemento.java
index 0ab7bc8..11b301a 100644
--- 
a/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/CatalogItemMemento.java
+++ 
b/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/mementos/CatalogItemMemento.java
@@ -30,6 +30,8 @@ public interface CatalogItemMemento extends Memento {
 
     String getSymbolicName();
 
+    String getContainingBundle();
+    
     String getIconUrl();
 
     String getVersion();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/api/src/main/java/org/apache/brooklyn/api/typereg/ManagedBundle.java
----------------------------------------------------------------------
diff --git 
a/api/src/main/java/org/apache/brooklyn/api/typereg/ManagedBundle.java 
b/api/src/main/java/org/apache/brooklyn/api/typereg/ManagedBundle.java
index 66ca592..49f5d7f 100644
--- a/api/src/main/java/org/apache/brooklyn/api/typereg/ManagedBundle.java
+++ b/api/src/main/java/org/apache/brooklyn/api/typereg/ManagedBundle.java
@@ -27,9 +27,13 @@ public interface ManagedBundle extends BrooklynObject, 
Rebindable, OsgiBundleWit
 
     /** A URL-like thing that we can register with the OSGi framework
      * to uniquely identify this bundle-instance.
-     * This typically includes the unique {@link #getId()} of this item. */
+     * This typically includes the unique {@link #getId()} of this item. 
+     * This will not normally be a URL that can be loaded. */
     String getOsgiUniqueUrl();
 
     VersionedName getVersionedName();
 
+    /** MD5 checksum of the contents of bundle as installed to OSGi */
+    String getChecksum();
+    
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java
----------------------------------------------------------------------
diff --git 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java
 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java
index b881ec7..54c1efa 100644
--- 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java
+++ 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiVersionMoreEntityRebindTest.java
@@ -50,6 +50,7 @@ import org.apache.brooklyn.util.core.ClassLoaderUtils;
 import org.apache.brooklyn.util.core.ResourceUtils;
 import org.apache.brooklyn.util.javalang.Reflections;
 import org.apache.brooklyn.util.osgi.OsgiTestResources;
+import org.apache.brooklyn.util.osgi.VersionedName;
 import org.apache.brooklyn.util.text.Strings;
 import org.osgi.framework.Bundle;
 import org.slf4j.Logger;
@@ -72,24 +73,41 @@ public class CatalogOsgiVersionMoreEntityRebindTest extends 
AbstractYamlRebindTe
     protected boolean useOsgi() {
         return true;
     }
-
+    
     @Test
-    public void testRebindAppIncludingBundle() throws Exception {
-        
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), 
OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_PATH);
+    public void testRebindAppIncludingBundleAllWorksAndPreservesChecksum() 
throws Exception {
+        
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), 
OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
         ((ManagementContextInternal)mgmt()).getOsgiManager().get().install( 
             new 
ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V1_URL)
 );
         
+        RegisteredType item = 
mgmt().getTypeRegistry().get(BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY);
+        Assert.assertNotNull(item);
+        Assert.assertEquals(item.getContainingBundle(), 
OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL+":"+"0.1.0");
+        
+        ManagedBundle mb = 
((ManagementContextInternal)mgmt()).getOsgiManager().get().getManagedBundle(VersionedName.fromString(item.getContainingBundle()));
+        Assert.assertNotNull(mb);
+        String c1 = mb.getChecksum();
+        Assert.assertTrue(Strings.isNonBlank(c1), "Missing checksum for 
bundle");
+
+        Map<String, ManagedBundle> bundles1 = 
((ManagementContextInternal)mgmt()).getOsgiManager().get().getManagedBundles();
+        
         createAndStartApplication("services: [ { type: 
"+BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY+" } ]");
         
         StartableApplication newApp = rebind();
 
         // bundles installed
         Map<String, ManagedBundle> bundles = 
((ManagementContextInternal)mgmt()).getOsgiManager().get().getManagedBundles();
-        Asserts.assertSize(bundles.keySet(), 1);
+        Asserts.assertEquals(bundles, bundles1);
+        
+        //item installed
+        item = 
mgmt().getTypeRegistry().get(BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY);
+        Assert.assertNotNull(item);
+        Assert.assertEquals(item.getContainingBundle(), 
OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL+":"+"0.1.0");
         
-        // types installed
-        RegisteredType t = 
mgmt().getTypeRegistry().get("org.apache.brooklyn.test.osgi.entities.more.MoreEntity");
-        Assert.assertNotNull(t);
+        // containing bundle set, matches, and checksum matches
+        mb = 
((ManagementContextInternal)mgmt()).getOsgiManager().get().getManagedBundle(VersionedName.fromString(item.getContainingBundle()));
+        Assert.assertEquals(mb, bundles.get(mb.getId()));
+        Assert.assertEquals(mb.getChecksum(), c1, "checksums should be the 
same after rebinding");
         
         Assert.assertNotNull(newApp);
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlEntityTest.java
----------------------------------------------------------------------
diff --git 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlEntityTest.java
 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlEntityTest.java
index a1bd800..6d7df19 100644
--- 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlEntityTest.java
+++ 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogOsgiYamlEntityTest.java
@@ -236,7 +236,7 @@ public class CatalogOsgiYamlEntityTest extends 
AbstractYamlTest {
         try {
             addCatalogItems(
                 "brooklyn.catalog:",
-                "  id: my.catalog.app.id.non_existing.ref",
+                "  id: my.catalog.app.id.non_existing.ref.1",
                 "  version: " + TEST_VERSION,
                 "  itemType: entity",
                 "  libraries:",
@@ -250,7 +250,7 @@ public class CatalogOsgiYamlEntityTest extends 
AbstractYamlTest {
         try {
             addCatalogItems(
                 "brooklyn.catalog:",
-                "  id: my.catalog.app.id.non_existing.ref",
+                "  id: my.catalog.app.id.non_existing.ref.2",
                 "  version: " + TEST_VERSION,
                 "  itemType: entity",
                 "  libraries:",
@@ -323,13 +323,18 @@ public class CatalogOsgiYamlEntityTest extends 
AbstractYamlTest {
         addCatalogOSGiEntity(id);
     }
     
-    @Test(expectedExceptions = IllegalStateException.class)
+    @Test
     public void testUpdatingItemFailsIfDifferent() {
         
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), 
OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
 
         String id = "my.catalog.app.id.duplicate";
         addCatalogOSGiEntity(id);
-        addCatalogOSGiEntity(id, SIMPLE_ENTITY_TYPE, true);
+        try {
+            addCatalogOSGiEntity(id, SIMPLE_ENTITY_TYPE, true);
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Exception e) {
+            Asserts.expectedFailureContainsIgnoreCase(e, id, "already 
installed", "cannot install a different bundle at a same non-snapshot version");
+        }
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
 
b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
index c0e2e07..9d40e2f 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
@@ -1173,7 +1173,7 @@ public class BasicBrooklynCatalog implements 
BrooklynCatalog {
     public List<? extends CatalogItem<?,?>> addItems(String yaml, boolean 
forceUpdate) {
         Maybe<OsgiManager> osgiManager = 
((ManagementContextInternal)mgmt).getOsgiManager();
         if (osgiManager.isPresent() && AUTO_WRAP_CATALOG_YAML_AS_BUNDLE) {
-            // TODO wrap in a bundle to be managed; need to get bundle and 
version from yaml
+            // wrap in a bundle to be managed; need to get bundle and version 
from yaml
             Map<?, ?> cm = BasicBrooklynCatalog.getCatalogMetadata(yaml);
             VersionedName vn = BasicBrooklynCatalog.getVersionedName( cm, 
false );
             if (vn==null) {
@@ -1202,9 +1202,7 @@ public class BasicBrooklynCatalog implements 
BrooklynCatalog {
                 throw Exceptions.propagate(e);
             }
             bf.delete();
-            if (result.getCode().isError() || 
result.getCode()==ResultCode.IGNORING_BUNDLE_AREADY_INSTALLED) {
-                // if we're wrapping YAML then we don't allow equivalent YAML 
to be pasted
-                // TODO remove this once we have better bundle equivalence 
checks
+            if (result.getCode().isError()) {
                 throw new IllegalStateException(result.getMessage());
             }
             return toItems(result.getCatalogItemsInstalled());

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiArchiveInstaller.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiArchiveInstaller.java 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiArchiveInstaller.java
index 9ef04c8..d15a036 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiArchiveInstaller.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiArchiveInstaller.java
@@ -271,6 +271,8 @@ class OsgiArchiveInstaller {
             updateManifestFromAllSourceInformation();
             if (result.code!=null) return 
ReferenceWithError.newInstanceWithoutError(result);
             assert inferredMetadata.isNameResolved() : "Should have resolved 
"+inferredMetadata;
+            assert inferredMetadata instanceof BasicManagedBundle : "Only 
BasicManagedBundles supported";
+            
((BasicManagedBundle)inferredMetadata).setChecksum(Streams.getMd5Checksum(new 
FileInputStream(zipFile)));
 
             final boolean updating;
             result.metadata = 
osgiManager.getManagedBundle(inferredMetadata.getVersionedName());
@@ -296,6 +298,12 @@ class OsgiArchiveInstaller {
                     }
                     updating = true;
                 } else {
+                    if (result.getMetadata().getChecksum()==null || 
inferredMetadata.getChecksum()==null) {
+                        log.warn("Missing bundle checksum data for "+result+"; 
assuming bundle replacement is permitted");
+                    } else if 
(!Objects.equal(result.getMetadata().getChecksum(), 
inferredMetadata.getChecksum())) {
+                        throw new IllegalArgumentException("Bundle 
"+result.getMetadata().getVersionedName()+" already installed; "
+                            + "cannot install a different bundle at a same 
non-snapshot version");                        
+                    }
                     result.setIgnoringAlreadyInstalled();
                     return ReferenceWithError.newInstanceWithoutError(result);
                 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiBundleInstallationResult.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiBundleInstallationResult.java
 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiBundleInstallationResult.java
index 9e845ad..e0e7456 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiBundleInstallationResult.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiBundleInstallationResult.java
@@ -39,7 +39,6 @@ public class OsgiBundleInstallationResult {
     public enum ResultCode { 
         INSTALLED_NEW_BUNDLE(false),
         UPDATED_EXISTING_BUNDLE(false),
-        // TODO if bundle installed is different to bundle supplied we should 
flag an error
         IGNORING_BUNDLE_AREADY_INSTALLED(false), 
         ERROR_PREPARING_BUNDLE(true),
         ERROR_INSTALLING_BUNDLE(true);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java
index dcabe00..34a5d0b 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java
@@ -251,10 +251,12 @@ public class OsgiManager {
         framework = null;
     }
 
+    /** Map of bundles by UID */
     public Map<String, ManagedBundle> getManagedBundles() {
         return managedBundlesRecord.getManagedBundles();
     }
 
+    /** Gets UID given a name, or null */
     public String getManagedBundleId(VersionedName vn) {
         return managedBundlesRecord.getManagedBundleId(vn);
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicCatalogItemRebindSupport.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicCatalogItemRebindSupport.java
 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicCatalogItemRebindSupport.java
index 614b085..8cea7b2 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicCatalogItemRebindSupport.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicCatalogItemRebindSupport.java
@@ -43,6 +43,7 @@ public class BasicCatalogItemRebindSupport extends 
AbstractBrooklynObjectRebindS
         super.reconstruct(rebindContext, memento);
         FlagUtils.setFieldsFromFlags(MutableMap.builder()
                 .put("symbolicName", memento.getSymbolicName())
+                .put("containingBundle", memento.getContainingBundle())
                 .put("javaType", memento.getJavaType())
                 .put("displayName", memento.getDisplayName())
                 .put("description", memento.getDescription())

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BasicCatalogItemMemento.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BasicCatalogItemMemento.java
 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BasicCatalogItemMemento.java
index 33406a4..72447a7 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BasicCatalogItemMemento.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/BasicCatalogItemMemento.java
@@ -47,6 +47,7 @@ public class BasicCatalogItemMemento extends AbstractMemento 
implements CatalogI
     public static class Builder extends AbstractMemento.Builder<Builder> {
         protected String description;
         protected String symbolicName;
+        protected String containingBundle;
         protected String iconUrl;
         protected String javaType;
         protected String version;
@@ -69,6 +70,11 @@ public class BasicCatalogItemMemento extends AbstractMemento 
implements CatalogI
             return self();
         }
 
+        public Builder containingBundle(String containingBundle) {
+            this.containingBundle = containingBundle;
+            return self();
+        }
+
         public Builder iconUrl(String iconUrl) {
             this.iconUrl = iconUrl;
             return self();
@@ -128,6 +134,7 @@ public class BasicCatalogItemMemento extends 
AbstractMemento implements CatalogI
             super.from(other);
             description = other.getDescription();
             symbolicName = other.getSymbolicName();
+            containingBundle = other.getContainingBundle();
             iconUrl = other.getIconUrl();
             javaType = other.getJavaType();
             version = other.getVersion();
@@ -149,6 +156,7 @@ public class BasicCatalogItemMemento extends 
AbstractMemento implements CatalogI
 
     private String description;
     private String symbolicName;
+    private String containingBundle;
     private String iconUrl;
     private String javaType;
     private String version;
@@ -168,6 +176,7 @@ public class BasicCatalogItemMemento extends 
AbstractMemento implements CatalogI
         super(builder);
         this.description = builder.description;
         this.symbolicName = builder.symbolicName;
+        this.containingBundle = builder.containingBundle;
         this.iconUrl = builder.iconUrl;
         this.version = builder.version;
         this.planYaml = builder.planYaml;
@@ -197,6 +206,11 @@ public class BasicCatalogItemMemento extends 
AbstractMemento implements CatalogI
     }
 
     @Override
+    public String getContainingBundle() {
+        return containingBundle;
+    }
+    
+    @Override
     public String getIconUrl() {
         return iconUrl;
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java
 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java
index 20179bc..e3969cb 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java
@@ -323,6 +323,7 @@ public class MementosGenerators {
         return builder.build();
     }
     
+    @SuppressWarnings("deprecation")
     private static CatalogItemMemento newCatalogItemMemento(CatalogItem<?, ?> 
catalogItem) {
         if (catalogItem instanceof CatalogItemDo<?,?>) {
             catalogItem = ((CatalogItemDo<?,?>)catalogItem).getDto();
@@ -331,6 +332,7 @@ public class MementosGenerators {
         populateBrooklynObjectMementoBuilder(catalogItem, builder);
         builder.catalogItemJavaType(catalogItem.getCatalogItemJavaType())
             .catalogItemType(catalogItem.getCatalogItemType())
+            .containingBundle(catalogItem.getContainingBundle())
             .description(catalogItem.getDescription())
             .iconUrl(catalogItem.getIconUrl())
             .javaType(catalogItem.getJavaType())

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicManagedBundle.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicManagedBundle.java 
b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicManagedBundle.java
index d480ed5..1cf4e84 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/typereg/BasicManagedBundle.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/typereg/BasicManagedBundle.java
@@ -31,7 +31,6 @@ import org.apache.brooklyn.core.objs.AbstractBrooklynObject;
 import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
 import org.apache.brooklyn.util.osgi.VersionedName;
 import org.apache.brooklyn.util.text.BrooklynVersionSyntax;
-import org.osgi.framework.Version;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.MoreObjects;
@@ -42,6 +41,7 @@ public class BasicManagedBundle extends 
AbstractBrooklynObject implements Manage
 
     private String symbolicName;
     private String version;
+    private String checksum;
     private String url;
     private transient File localFileWhenJustUploaded;
 
@@ -129,6 +129,7 @@ public class BasicManagedBundle extends 
AbstractBrooklynObject implements Manage
 
     @Override
     public int hashCode() {
+        // checksum deliberately omitted here to match with OsgiBundleWithUrl
         return Objects.hashCode(symbolicName, version, url);
     }
 
@@ -141,6 +142,11 @@ public class BasicManagedBundle extends 
AbstractBrooklynObject implements Manage
         if (!Objects.equal(symbolicName, other.getSymbolicName())) return 
false;
         if (!Objects.equal(getOsgiVersionString(), 
other.getOsgiVersionString())) return false;
         if (!Objects.equal(url, other.getUrl())) return false;
+        if (other instanceof BasicManagedBundle) {
+            // make equality symetricm, OsgiBunde equals this iff this equals 
OsgiBundle;
+            // checksum compared if available, but not required
+            if (!Objects.equal(checksum, 
((BasicManagedBundle)other).getChecksum())) return false;
+        }
         return true;
     }
 
@@ -180,6 +186,14 @@ public class BasicManagedBundle extends 
AbstractBrooklynObject implements Manage
     public void setDisplayName(String newName) {
         throw new UnsupportedOperationException();
     }
+    
+    @Override
+    public String getChecksum() {
+        return checksum;
+    }
+    public void setChecksum(String md5Checksum) {
+        this.checksum = md5Checksum;
+    }
 
     @Override
     protected BrooklynObjectInternal configure(Map<?, ?> flags) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/core/src/main/resources/brooklyn/empty.catalog.bom
----------------------------------------------------------------------
diff --git a/core/src/main/resources/brooklyn/empty.catalog.bom 
b/core/src/main/resources/brooklyn/empty.catalog.bom
index 58cfb1b..a256754 100644
--- a/core/src/main/resources/brooklyn/empty.catalog.bom
+++ b/core/src/main/resources/brooklyn/empty.catalog.bom
@@ -15,4 +15,6 @@
 # specific language governing permissions and limitations
 # under the License.
 #
-brooklyn.catalog: {}
\ No newline at end of file
+brooklyn.catalog:
+  bundle: brooklyn-empty-catalog
+  version: 0.0.0-SNAPSHOT

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/utils/common/src/main/java/org/apache/brooklyn/util/stream/Streams.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/main/java/org/apache/brooklyn/util/stream/Streams.java 
b/utils/common/src/main/java/org/apache/brooklyn/util/stream/Streams.java
index 0f34357..a703dba 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/stream/Streams.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/stream/Streams.java
@@ -30,6 +30,9 @@ import java.io.Reader;
 import java.io.StringReader;
 import java.io.Writer;
 import java.nio.charset.Charset;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.slf4j.Logger;
@@ -39,6 +42,7 @@ import com.google.common.annotations.Beta;
 import com.google.common.base.Charsets;
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
 import com.google.common.base.Supplier;
 import com.google.common.io.ByteStreams;
 import com.google.common.io.CharStreams;
@@ -215,4 +219,21 @@ public class Streams {
         return false;
     }
 
+    public static String getMd5Checksum(InputStream in) {
+        MessageDigest md;
+        try {
+            md = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            throw Exceptions.propagate(e);
+        }
+        DigestInputStream dis = new DigestInputStream(in, md);
+        readFullyAndClose(dis);
+        byte[] digest = md.digest();
+        StringBuilder result = new StringBuilder();
+        for (byte b: digest) {
+            result.append(Strings.padStart(Integer.toHexString((256+b)%256), 
2, '0'));
+        }
+        return result.toString().toUpperCase();
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/utils/common/src/test/java/org/apache/brooklyn/util/stream/StreamsTest.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/test/java/org/apache/brooklyn/util/stream/StreamsTest.java 
b/utils/common/src/test/java/org/apache/brooklyn/util/stream/StreamsTest.java
new file mode 100644
index 0000000..37340f4
--- /dev/null
+++ 
b/utils/common/src/test/java/org/apache/brooklyn/util/stream/StreamsTest.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.util.stream;
+
+import java.io.ByteArrayInputStream;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class StreamsTest {
+    
+    @Test
+    public void testChecksum() {
+        Assert.assertEquals(Streams.getMd5Checksum(new 
ByteArrayInputStream("hello world".getBytes())),
+            // generated from 3rd party tool
+            "5EB63BBBE01EEED093CB22BB8F5ACDC3"
+            );
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e7dc875a/utils/test-support/src/main/java/org/apache/brooklyn/test/support/TestResourceUnavailableException.java
----------------------------------------------------------------------
diff --git 
a/utils/test-support/src/main/java/org/apache/brooklyn/test/support/TestResourceUnavailableException.java
 
b/utils/test-support/src/main/java/org/apache/brooklyn/test/support/TestResourceUnavailableException.java
index cb4205f..34c3de3 100644
--- 
a/utils/test-support/src/main/java/org/apache/brooklyn/test/support/TestResourceUnavailableException.java
+++ 
b/utils/test-support/src/main/java/org/apache/brooklyn/test/support/TestResourceUnavailableException.java
@@ -53,7 +53,7 @@ public class TestResourceUnavailableException extends 
SkipException {
      *
      * Note that this will use the same classloader that was used to load this 
class.
      *
-     * @param resourceName the classpath resource name, e.g.
+     * @param resourceName the classpath resource name, as a path, e.g.
      *                     
<code>/brooklyn/osgi/brooklyn-test-osgi-entities.jar</code>
      */
     public static void throwIfResourceUnavailable(Class<?> relativeToClass, 
String resourceName) {
@@ -61,8 +61,10 @@ public class TestResourceUnavailableException extends 
SkipException {
         checkNotNull(resourceName, "resourceName");
         checkArgument(!resourceName.isEmpty(), "resourceName must not be 
empty");
         InputStream resource = 
relativeToClass.getResourceAsStream(resourceName);
-        if (resource == null)
+        if (resource == null) {
+            // would be nice to support URLs but ResourceUtils isn't available 
here
             throw new TestResourceUnavailableException(resourceName);
+        }
 
         // just make sure we clean up the resource
         try {

Reply via email to