BrooklynVersion.getFeatures

Reports a summary of feature information extracted from all manifests
on the classpath and in catalogue bundles.


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

Branch: refs/heads/master
Commit: b087c1c8b215360d958247e2ec0375925186b04a
Parents: 8ff666e
Author: Sam Corbett <[email protected]>
Authored: Tue May 26 22:10:31 2015 +0100
Committer: Sam Corbett <[email protected]>
Committed: Tue Jun 2 18:07:37 2015 +0100

----------------------------------------------------------------------
 .../src/main/java/brooklyn/BrooklynVersion.java | 142 ++++++++++++++++++-
 .../OsgiBrooklynClassLoadingContext.java        |  18 ++-
 .../internal/AbstractManagementContext.java     |   2 +-
 .../src/main/java/brooklyn/util/osgi/Osgis.java |  11 +-
 .../src/test/dependencies/osgi/entities/pom.xml |   2 +
 .../test/java/brooklyn/BrooklynVersionTest.java |  50 ++++++-
 .../osgi/brooklyn-test-osgi-entities.jar        | Bin 13060 -> 13666 bytes
 7 files changed, 212 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b087c1c8/core/src/main/java/brooklyn/BrooklynVersion.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/BrooklynVersion.java 
b/core/src/main/java/brooklyn/BrooklynVersion.java
index 9920b17..aac75e4 100644
--- a/core/src/main/java/brooklyn/BrooklynVersion.java
+++ b/core/src/main/java/brooklyn/BrooklynVersion.java
@@ -24,15 +24,27 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
 import java.util.Enumeration;
+import java.util.Map;
 import java.util.Properties;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.jar.Attributes;
-
 import javax.annotation.Nullable;
 
+import org.osgi.framework.Constants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+
+import brooklyn.catalog.CatalogItem;
+import brooklyn.management.ManagementContext;
+import brooklyn.management.classloading.OsgiBrooklynClassLoadingContext;
+import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.osgi.Osgis;
 import brooklyn.util.osgi.Osgis.ManifestHelper;
@@ -55,15 +67,18 @@ public class BrooklynVersion {
     private static final String MVN_VERSION_PROPERTY_NAME = "version";
     private static final String OSGI_VERSION_PROPERTY_NAME = 
Attributes.Name.IMPLEMENTATION_VERSION.toString();
     private static final String OSGI_SHA1_PROPERTY_NAME = 
"Implementation-SHA-1";
+    private static final String OSGI_BRANCH_PROPERTY_NAME = 
"Implementation-Branch";
 
     private final static String VERSION_FROM_STATIC = "0.7.0-SNAPSHOT"; // 
BROOKLYN_VERSION
     private static final AtomicReference<Boolean> IS_DEV_ENV = new 
AtomicReference<Boolean>();
 
+    private static final String BROOKLYN_FEATURE_PREFIX = "Brooklyn-Feature-";
+
     public static final BrooklynVersion INSTANCE = new BrooklynVersion();
 
     private final Properties versionProperties = new Properties();
 
-    public BrooklynVersion() {
+    private BrooklynVersion() {
         // we read the maven pom metadata and osgi metadata and make sure it's 
sensible
         // everything is put into a single map for now (good enough, but 
should be cleaned up)
         
readPropertiesFromMavenResource(BrooklynVersion.class.getClassLoader());
@@ -249,4 +264,127 @@ public class BrooklynVersion {
         return INSTANCE.getVersion();
     }
 
+    /**
+     * @param mgmt The context to search for features.
+     * @return An iterable containing all features found in the management 
context's classpath and catalogue.
+     */
+    public static Iterable<BrooklynFeature> getFeatures(ManagementContext 
mgmt) {
+        Iterable<URL> manifests = 
ResourceUtils.create(mgmt).getResources(MANIFEST_PATH);
+
+        for (CatalogItem<?, ?> catalogItem : 
mgmt.getCatalog().getCatalogItems()) {
+            OsgiBrooklynClassLoadingContext osgiContext = new 
OsgiBrooklynClassLoadingContext(
+                    mgmt, catalogItem.getCatalogItemId(), 
catalogItem.getLibraries());
+            manifests = Iterables.concat(manifests, 
osgiContext.getResources(MANIFEST_PATH));
+        }
+
+        // Set over list in case a bundle is reported more than once (e.g. 
from classpath and from OSGi).
+        // Not sure of validity of this approach over just reporting 
duplicates.
+        ImmutableSet.Builder<BrooklynFeature> features = 
ImmutableSet.builder();
+        for (URL manifest : manifests) {
+            ManifestHelper mh = null;
+            try {
+                mh = Osgis.ManifestHelper.forManifest(manifest);
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                log.debug("Error reading OSGi manifest from " + manifest + " 
when determining version properties: " + e, e);
+            }
+            if (mh == null) continue;
+            Attributes attrs = mh.getManifest().getMainAttributes();
+            Optional<BrooklynFeature> fs = BrooklynFeature.newFeature(attrs);
+            if (fs.isPresent()) {
+                features.add(fs.get());
+            }
+        }
+        return features.build();
+    }
+
+    public static class BrooklynFeature {
+        private final String name;
+        private final String symbolicName;
+        private final String version;
+        private final String lastModified;
+        private final Map<String, String> additionalData;
+
+        BrooklynFeature(String name, String symbolicName, String version, 
String lastModified, Map<String, String> additionalData) {
+            this.symbolicName = checkNotNull(symbolicName, "symbolicName");
+            this.name = name;
+            this.version = version;
+            this.lastModified = lastModified;
+            this.additionalData = ImmutableMap.copyOf(additionalData);
+        }
+
+        /** @return Present if any attribute name begins with {@link 
#BROOKLYN_FEATURE_PREFIX}, absent otherwise. */
+        private static Optional<BrooklynFeature> newFeature(Attributes 
attributes) {
+            Map<String, String> additionalData = Maps.newHashMap();
+            for (Object key : attributes.keySet()) {
+                if (key instanceof Attributes.Name && 
key.toString().startsWith(BROOKLYN_FEATURE_PREFIX)) {
+                    Attributes.Name name = Attributes.Name.class.cast(key);
+                    String value = attributes.getValue(name);
+                    if (!Strings.isBlank(value)) {
+                        additionalData.put(name.toString(), value);
+                    }
+                }
+            }
+            if (additionalData.isEmpty()) {
+                return Optional.absent();
+            }
+
+            // Name is special cased as it a useful way to indicate a feature 
without
+            String nameKey = BROOKLYN_FEATURE_PREFIX + "Name";
+            String name = Optional.fromNullable(additionalData.remove(nameKey))
+                    .or(Optional.fromNullable(Constants.BUNDLE_NAME))
+                    .or(attributes.getValue(Constants.BUNDLE_SYMBOLICNAME));
+
+            return Optional.of(new BrooklynFeature(
+                    name,
+                    attributes.getValue(Constants.BUNDLE_SYMBOLICNAME),
+                    attributes.getValue(Constants.BUNDLE_VERSION),
+                    attributes.getValue("Bnd-LastModified"),
+                    additionalData));
+        }
+
+        public String getLastModified() {
+            return lastModified;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getSymbolicName() {
+            return symbolicName;
+        }
+
+        public String getVersion() {
+            return version;
+        }
+
+        /** @return an unmodifiable map */
+        public Map<String, String> getAdditionalData() {
+            return additionalData;
+        }
+
+        @Override
+        public String toString() {
+            return getClass().getSimpleName() + "{" + symbolicName + (version 
!= null ? ":" + version : "") + "}";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(symbolicName, version);
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (this == other) return true;
+            if (other == null || getClass() != other.getClass()) return false;
+            BrooklynFeature that = (BrooklynFeature) other;
+            if (!symbolicName.equals(that.symbolicName)) {
+                return false;
+            } else if (version != null ? !version.equals(that.version) : 
that.version != null) {
+                return false;
+            }
+            return true;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b087c1c8/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 91e692f..efee3b5 100644
--- 
a/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java
+++ 
b/core/src/main/java/brooklyn/management/classloading/OsgiBrooklynClassLoadingContext.java
@@ -26,6 +26,7 @@ import brooklyn.catalog.CatalogItem;
 import brooklyn.catalog.CatalogItem.CatalogBundle;
 import brooklyn.catalog.internal.CatalogUtils;
 import brooklyn.management.ManagementContext;
+import brooklyn.management.entitlement.EntitlementClass;
 import brooklyn.management.entitlement.Entitlements;
 import brooklyn.management.ha.OsgiManager;
 import brooklyn.management.internal.ManagementContextInternal;
@@ -56,6 +57,7 @@ public class OsgiBrooklynClassLoadingContext extends 
AbstractBrooklynClassLoadin
         return _bundles;
     }
     
+    @Override
     @SuppressWarnings({ "unchecked", "rawtypes" })
     public Maybe<Class<?>> tryLoadClass(String className) {
         Maybe<Class<Object>> clazz = null;
@@ -107,8 +109,8 @@ public class OsgiBrooklynClassLoadingContext extends 
AbstractBrooklynClassLoadin
 
     @Override
     public URL getResource(String name) {
-        if (mgmt!=null) {
-            Maybe<OsgiManager> osgi = 
((ManagementContextInternal)mgmt).getOsgiManager();
+        if (mgmt != null && isEntitledToSeeCatalogItem()) {
+            Maybe<OsgiManager> osgi = ((ManagementContextInternal) 
mgmt).getOsgiManager();
             if (osgi.isPresent() && hasBundles) {
                 return osgi.get().getResource(name, getBundles());
             }
@@ -118,7 +120,7 @@ public class OsgiBrooklynClassLoadingContext extends 
AbstractBrooklynClassLoadin
 
     @Override
     public Iterable<URL> getResources(String name) {
-        if (mgmt != null) {
+        if (mgmt != null && isEntitledToSeeCatalogItem()) {
             Maybe<OsgiManager> osgi = ((ManagementContextInternal) 
mgmt).getOsgiManager();
             if (osgi.isPresent() && hasBundles) {
                 return osgi.get().getResources(name, getBundles());
@@ -131,4 +133,14 @@ public class OsgiBrooklynClassLoadingContext extends 
AbstractBrooklynClassLoadin
         return catalogItemId;
     }
 
+    /**
+     * @return true if the current entitlement context may {@link 
Entitlements#SEE_CATALOG_ITEM see}
+     * {@link #getCatalogItemId}.
+     */
+    private boolean isEntitledToSeeCatalogItem() {
+        return Entitlements.isEntitled(mgmt.getEntitlementManager(),
+                Entitlements.SEE_CATALOG_ITEM,
+                catalogItemId);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b087c1c8/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
 
b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
index 81dd26b..905a903 100644
--- 
a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
+++ 
b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
@@ -401,7 +401,7 @@ public abstract class AbstractManagementContext implements 
ManagementContextInte
     
     /** Optional mechanism for setting the classpath which should be scanned 
by the catalog, if the catalog
      * is scanning the default classpath.  Usually it infers the right thing, 
but some classloaders
-     * (e.g. surefire) do funny things which the underlying 
org.reflections.Reflectiosn library can't see in to. 
+     * (e.g. surefire) do funny things which the underlying 
org.reflections.Reflections library can't see in to.
      * <p>
      * This should normally be invoked early in the server startup.  Setting 
it after the catalog is loaded will not
      * take effect without an explicit internal call to do so.  Once set, it 
can be changed prior to catalog loading

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b087c1c8/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 de49b2d..15f85a8 100644
--- a/core/src/main/java/brooklyn/util/osgi/Osgis.java
+++ b/core/src/main/java/brooklyn/util/osgi/Osgis.java
@@ -665,10 +665,13 @@ public class Osgis {
         }
         
         public static ManifestHelper forManifest(URL url) throws IOException, 
BundleException {
-            InputStream in = url.openStream();
-            ManifestHelper helper = forManifest(in);
-            in.close();
-            return helper;
+            InputStream in = null;
+            try {
+                in = url.openStream();
+                return forManifest(in);
+            } finally {
+                if (in != null) in.close();
+            }
         }
         
         public static ManifestHelper forManifest(InputStream in) throws 
IOException, BundleException {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b087c1c8/core/src/test/dependencies/osgi/entities/pom.xml
----------------------------------------------------------------------
diff --git a/core/src/test/dependencies/osgi/entities/pom.xml 
b/core/src/test/dependencies/osgi/entities/pom.xml
index d143ea2..6fa1314 100644
--- a/core/src/test/dependencies/osgi/entities/pom.xml
+++ b/core/src/test/dependencies/osgi/entities/pom.xml
@@ -74,6 +74,8 @@
                 <configuration>
                     <instructions>
                         <Bundle-Version>${project.version}</Bundle-Version>
+                        
<Brooklyn-Feature-BuildId>${buildNumber}</Brooklyn-Feature-BuildId>
+                        
<Brooklyn-Feature-BuildBranch>${scmBranch}</Brooklyn-Feature-BuildBranch>
                     </instructions>
                 </configuration>
             </plugin>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b087c1c8/core/src/test/java/brooklyn/BrooklynVersionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/BrooklynVersionTest.java 
b/core/src/test/java/brooklyn/BrooklynVersionTest.java
index bfd32ce..ff65215 100644
--- a/core/src/test/java/brooklyn/BrooklynVersionTest.java
+++ b/core/src/test/java/brooklyn/BrooklynVersionTest.java
@@ -19,14 +19,28 @@
 package brooklyn;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
 
 import java.net.URL;
+import java.util.Iterator;
+import java.util.List;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import brooklyn.catalog.internal.CatalogEntityItemDto;
+import brooklyn.catalog.internal.CatalogItemBuilder;
+import brooklyn.catalog.internal.CatalogItemDtoAbstract;
+import brooklyn.management.internal.LocalManagementContext;
+import brooklyn.management.osgi.OsgiTestResources;
+import brooklyn.test.TestResourceUnavailableException;
+import brooklyn.test.entity.LocalManagementContextForTests;
 import brooklyn.util.text.Strings;
 
 public class BrooklynVersionTest {
@@ -42,19 +56,19 @@ public class BrooklynVersionTest {
     public void testGetHardcodedClasspathVersion() {
         @SuppressWarnings("deprecation")
         String v = BrooklynVersion.INSTANCE.getVersionFromClasspath();
-        Assert.assertTrue(BrooklynVersion.get().equals(v) || 
"0.0.0-SNAPSHOT".equals(v), v);
+        assertTrue(BrooklynVersion.get().equals(v) || 
"0.0.0-SNAPSHOT".equals(v), v);
     }
 
     @Test
     public void testGetFromMaven() {
         String v = BrooklynVersion.INSTANCE.getVersionFromMavenProperties();
-        Assert.assertTrue(v == null || BrooklynVersion.get().equals(v), v);
+        assertTrue(v == null || BrooklynVersion.get().equals(v), v);
     }
 
     @Test
     public void testGetFromOsgi() {
         String v = BrooklynVersion.INSTANCE.getVersionFromOsgiManifest();
-        Assert.assertTrue(v == null || BrooklynVersion.get().equals(v), v);
+        assertTrue(v == null || BrooklynVersion.get().equals(v), v);
     }
 
     @Test
@@ -79,4 +93,34 @@ public class BrooklynVersionTest {
                 "Dev env? " + BrooklynVersion.isDevelopmentEnvironment() + "; 
but resource path: " + sp);
     }
 
+    @Test
+    public void testGetFeatures() throws Exception {
+        
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), 
OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+        LocalManagementContext mgmt = 
LocalManagementContextForTests.builder(true)
+                .disableOsgi(false)
+                .build();
+        String symName = 
"org.apache.brooklyn.test.resources.osgi.brooklyn-test-osgi-entities";
+        String version = "0.1.0";
+        String type = "brooklyn.osgi.tests.SimpleEntity";
+        List<String> libraries = Lists.newArrayList("classpath:" + 
OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
+
+        CatalogEntityItemDto c1 = CatalogItemBuilder.newEntity(symName, 
version)
+                .javaType(type)
+                .libraries(CatalogItemDtoAbstract.parseLibraries(libraries))
+                .build();
+        mgmt.getCatalog().addItem(c1);
+
+        Iterable<BrooklynVersion.BrooklynFeature> features = 
BrooklynVersion.getFeatures(mgmt);
+        assertTrue(features.iterator().hasNext());
+        boolean found = false;
+        Iterator<BrooklynVersion.BrooklynFeature> iterator = 
features.iterator();
+        while (!found && iterator.hasNext()) {
+            BrooklynVersion.BrooklynFeature feature = iterator.next();
+            if (feature.getSymbolicName().equals(symName) && 
feature.getVersion().equals(version)) {
+                found = true;
+            }
+        }
+        assertTrue(found, "Expected to find " + symName + ":" + version + " 
in: " + Iterables.toString(features));
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b087c1c8/core/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar
----------------------------------------------------------------------
diff --git 
a/core/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar 
b/core/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar
index 5a3b052..6e2e80d 100644
Binary files 
a/core/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar and 
b/core/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar differ

Reply via email to