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
