brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From s...@apache.org
Subject [5/8] brooklyn-server git commit: Change classloading order in ClassLoaderUtils
Date Mon, 11 Jul 2016 12:45:51 GMT
Change classloading order in ClassLoaderUtils


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

Branch: refs/heads/master
Commit: 2654305d4f4bc5f9d1a45bffb363996c4eb6e6fc
Parents: a555bea
Author: Aled Sage <aled.sage@gmail.com>
Authored: Thu Jul 7 14:34:04 2016 +0100
Committer: Aled Sage <aled.sage@gmail.com>
Committed: Sat Jul 9 10:58:36 2016 +0100

----------------------------------------------------------------------
 .../brooklyn/camp/brooklyn/RebindOsgiTest.java  | 63 ++++++++++++++++++++
 .../brooklyn/util/core/ClassLoaderUtils.java    | 63 +++++++++++++++++---
 .../mgmt/persist/XmlMementoSerializerTest.java  |  8 +++
 .../util/core/ClassLoaderUtilsTest.java         | 20 +++++++
 4 files changed, 145 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2654305d/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/RebindOsgiTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/RebindOsgiTest.java
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/RebindOsgiTest.java
index 752b066..46387da 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/RebindOsgiTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/RebindOsgiTest.java
@@ -37,6 +37,7 @@ import org.apache.brooklyn.core.mgmt.ha.OsgiManager;
 import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.mgmt.osgi.OsgiStandaloneTest;
+import org.apache.brooklyn.core.mgmt.osgi.OsgiVersionMoreEntityTest;
 import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.core.test.entity.TestEntity;
 import org.apache.brooklyn.util.core.osgi.Osgis;
@@ -321,6 +322,62 @@ public class RebindOsgiTest extends AbstractYamlRebindTest {
         assertEquals(entity2.getCatalogItemId(), appSymbolicName+":"+appVersion);
     }
     
+    /**
+     * Installs a newer version of the bundle than that used in the catalog. Then creates
an
+     * app with that catalog item, and rebinds. Confirms that we use our explicit version,
rather
+     * that the newer version available in the OSGi container.
+     */
+    @Test
+    public void testUsesCatalogBundleVersion() throws Exception {
+        String bundleV1Url = OsgiVersionMoreEntityTest.BROOKLYN_TEST_MORE_ENTITIES_V1_URL;
+        String bundleV2Url = OsgiVersionMoreEntityTest.BROOKLYN_TEST_MORE_ENTITIES_V2_URL;
+        String bundleEntityType = "org.apache.brooklyn.test.osgi.entities.more.MoreEntity";
+        String bundleObjectType = "org.apache.brooklyn.test.osgi.entities.more.MoreObject";
+        String v1Version = "0.1.0";
+
+        installBundle(mgmt(), bundleV2Url);
+        bundleUrlsToInstallOnRebind.add(bundleV2Url);
+        
+        String appSymbolicName = "my.catalog.app.id.load";
+        String appVersion = "0.1.0";
+        String appCatalogFormat = Joiner.on("\n").join(
+                "brooklyn.catalog:",
+                "  id: " + appSymbolicName,
+                "  version: " + appVersion,
+                "  itemType: entity",
+                "  libraries:",
+                "  - " + bundleV1Url,
+                "  item:",
+                "    type: " + bundleEntityType,
+                "    brooklyn.config:",
+                "      my.conf:",
+                "        $brooklyn:object:",
+                "          type: " + bundleObjectType,
+                "          object.fields:",
+                "            val: myEntityVal");
+        
+        Iterables.getOnlyElement(addCatalogItems(String.format(appCatalogFormat, appVersion)));
+        
+        String appBlueprintYaml = Joiner.on("\n").join(
+                "location: localhost\n",
+                "services:",
+                "- type: " + CatalogUtils.getVersionedId(appSymbolicName, appVersion));
+        origApp = (StartableApplication) createAndStartApplication(appBlueprintYaml);
+        Entity origEntity = Iterables.getOnlyElement(origApp.getChildren());
+        Object configVal = origEntity.config().get(ConfigKeys.newConfigKey(Object.class,
"my.conf"));
+        assertBundleVersionOf(Entities.deproxy(origEntity), v1Version);
+        assertBundleVersionOf(configVal, v1Version);
+        
+        // Rebind
+        rebind();
+
+        // Ensure entity/config is loaded from the explicit catalog version
+        Entity newEntity = Iterables.getOnlyElement(newApp.getChildren());
+        Object newConfigVal = newEntity.config().get(ConfigKeys.newConfigKey(Object.class,
"my.conf"));
+        assertBundleVersionOf(Entities.deproxy(newEntity), v1Version);
+        assertBundleVersionOf(newConfigVal, v1Version);
+    }
+    
     // TODO Does not do rebind; the config isn't there after rebind.
     // Need to reproduce that in a simpler use-case.
     @Test
@@ -395,4 +452,10 @@ public class RebindOsgiTest extends AbstractYamlRebindTest {
         Framework framework = osgiManager.getFramework();
         return Osgis.install(framework, bundleUrl);
     }
+    
+    protected void assertBundleVersionOf(Object obj, String expectedVersion) {
+        assertNotNull(obj);
+        Class<?> clazz = (obj instanceof Class) ? (Class<?>)obj : obj.getClass();
+        assertEquals(Osgis.getBundleOf(clazz).get().getVersion().toString(), expectedVersion);
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2654305d/core/src/main/java/org/apache/brooklyn/util/core/ClassLoaderUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/ClassLoaderUtils.java b/core/src/main/java/org/apache/brooklyn/util/core/ClassLoaderUtils.java
index bc46b60..b46f68d 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/ClassLoaderUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/ClassLoaderUtils.java
@@ -101,12 +101,44 @@ public class ClassLoaderUtils {
         this.mgmt = checkNotNull(mgmt, "mgmt");
     }
 
+    /**
+     * Loads the given class, handle OSGi bundles. The class could be in one of the following
formats:
+     * <ul>
+     *   <li>{@code <classname>}, such as {@code com.google.common.net.HostAndPort}
+     *   <li>{@code <bunde-symbolicName>:<classname>}, such as {@code com.google.guava:com.google.common.net.HostAndPort}
+     *   <li>{@code <bunde-symbolicName>:<bundle-version>:<classname>},
such as {@code com.google.guava:16.0.1:com.google.common.net.HostAndPort}
+     * </ul>
+     * 
+     * The classloading order is as follows:
+     * <ol>
+     *   <li>If the class explicitly states the bundle name and version, then load
from that.
+     *   <li>Otherwise try to load from the catalog's classloader. This is so we respect
any 
+     *       {@code libraries} supplied in the catalog metadata, and can thus handle updating

+     *       catalog versions. It also means we can try our best to handle a catalog that
+     *       uses a different bundle version from something that ships with Brooklyn.
+     *   <li>The white-listed bundles (i.e. those that ship with Brooklyn). We prefer
the 
+     *       version of the bundle that Brooklyn depends on, rather than taking the highest
+     *       version installed (e.g. Karaf has Guava 16.0.1 and 18.0; we want the former,
which
+     *       Brooklyn uses).
+     *   <li>The classloader passed in. Normally this is a boring unhelpful classloader
(e.g.
+     *       obtained from {@code callingClass.getClassLoader()}), so won't work. But it's
up
+     *       to the caller if they pass in something more useful.
+     *   <li>The {@link ManagementContext#getCatalogClassLoader()}. Again, this is
normally not helpful. 
+     *       We instead would prefer the specific catalog item's classloader (which we tried
earlier).
+     *   <li>If we were given a bundle name without a version, then finally try just
using the
+     *       most recent version of the bundle that is available in the OSGi container.
+     * </ol>
+     * 
+     * The list of "white-listed bundles" are controlled using the system property named
+     * {@link #WHITE_LIST_KEY}, defaulting to all {@code org.apache.brooklyn.*} bundles.
+     */
     public Class<?> loadClass(String name) throws ClassNotFoundException {
+        String symbolicName;
+        String version;
+        String className;
+
         if (looksLikeBundledClassName(name)) {
             String[] arr = name.split(CLASS_NAME_DELIMITER);
-            String symbolicName;
-            String version;
-            String className;
             if (arr.length > 3) {
                 throw new IllegalStateException("'" + name + "' doesn't look like a class
name and contains too many colons to be parsed as bundle:version:class triple.");
             } else if (arr.length == 3) {
@@ -120,9 +152,17 @@ public class ClassLoaderUtils {
             } else {
                 throw new IllegalStateException("'" + name + "' contains a bundle:version:class
delimiter, but only one of those specified");
             }
-            return loadClass(symbolicName, version, className);
+        } else {
+            symbolicName = null;
+            version = null;
+            className = name;
         }
 
+        if (symbolicName != null && version != null) {
+            // Very explicit; do as we're told!
+            return loadClass(symbolicName, version, className);
+        }
+        
         if (entity != null && mgmt != null) {
             String catalogItemId = entity.getCatalogItemId();
             if (catalogItemId != null) {
@@ -130,7 +170,7 @@ public class ClassLoaderUtils {
                 if (item != null) {
                     BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(mgmt,
item);
                     try {
-                        return loader.loadClass(name);
+                        return loader.loadClass(className);
                     } catch (IllegalStateException e) {
                         ClassNotFoundException cnfe = Exceptions.getFirstThrowableOfType(e,
ClassNotFoundException.class);
                         NoClassDefFoundError ncdfe = Exceptions.getFirstThrowableOfType(e,
NoClassDefFoundError.class);
@@ -146,11 +186,16 @@ public class ClassLoaderUtils {
             }
         }
 
+        Class<?> cls = tryLoadFromBundleWhiteList(name);
+        if (cls != null) {
+            return cls;
+        }
+        
         try {
             // Used instead of callingClass.getClassLoader().loadClass(...) as it could be
null (only for bootstrap classes)
             // Note that Class.forName(name, false, classLoader) doesn't seem to like us
returning a 
             // class with a different name from that intended (e.g. stripping off an OSGi
prefix).
-            return classLoader.loadClass(name);
+            return classLoader.loadClass(className);
         } catch (ClassNotFoundException e) {
         }
 
@@ -161,9 +206,9 @@ public class ClassLoaderUtils {
             }
         }
 
-        Class<?> cls = tryLoadFromBundleWhiteList(name);
-        if (cls != null) {
-            return cls;
+        if (symbolicName != null) {
+            // Finally fall back to loading from any version of the bundle
+            return loadClass(symbolicName, version, className);
         } else {
             throw new ClassNotFoundException("Class " + name + " not found on the application
class path, nor in the bundle white list.");
         }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2654305d/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerTest.java
b/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerTest.java
index 9251354..e26983b 100644
--- a/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerTest.java
@@ -43,11 +43,13 @@ import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoPersister.Loo
 import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.objs.BrooklynObjectType;
 import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.api.sensor.Enricher;
 import org.apache.brooklyn.api.sensor.Feed;
 import org.apache.brooklyn.api.typereg.RegisteredType;
 import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder;
 import org.apache.brooklyn.core.catalog.internal.CatalogItemDtoAbstract;
+import org.apache.brooklyn.core.entity.Attributes;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.location.SimulatedLocation;
 import org.apache.brooklyn.core.mgmt.ha.OsgiManager;
@@ -263,6 +265,12 @@ public class XmlMementoSerializerTest {
     }
 
     @Test
+    public void testSensorWithTypeToken() throws Exception {
+        AttributeSensor<Map<String, Object>> obj = Attributes.SERVICE_NOT_UP_DIAGNOSTICS;
+        assertSerializeAndDeserialize(obj);
+    }
+
+    @Test
     public void testClass() throws Exception {
         Class<?> t = XmlMementoSerializer.class;
         assertSerializeAndDeserialize(t);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2654305d/core/src/test/java/org/apache/brooklyn/util/core/ClassLoaderUtilsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/util/core/ClassLoaderUtilsTest.java b/core/src/test/java/org/apache/brooklyn/util/core/ClassLoaderUtilsTest.java
index 3f2a8e1..98a93ef 100644
--- a/core/src/test/java/org/apache/brooklyn/util/core/ClassLoaderUtilsTest.java
+++ b/core/src/test/java/org/apache/brooklyn/util/core/ClassLoaderUtilsTest.java
@@ -41,6 +41,8 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableList;
+
 public class ClassLoaderUtilsTest {
 
     private LocalManagementContext mgmt;
@@ -144,6 +146,24 @@ public class ClassLoaderUtilsTest {
         assertFalse(clu.isBundleWhiteListed(getBundle(mgmt, "com.google.guava")));
     }
     
+    /**
+     * When two guava versions installed, want us to load from the *brooklyn* version rather
than 
+     * a newer version that happens to be in Karaf.
+     */
+    @Test(groups={"Integration"})
+    public void testLoadsFromRightGuavaVersion() throws Exception {
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
+        ClassLoaderUtils clu = new ClassLoaderUtils(getClass(), mgmt);
+        
+        String bundleUrl = "http://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar";
+        Bundle bundle = installBundle(mgmt, bundleUrl);
+        String bundleName = bundle.getSymbolicName();
+        
+        String classname = bundleName + ":" + ImmutableList.class.getName();
+        Class<?> clazz = clu.loadClass(classname);
+        assertEquals(clazz, ImmutableList.class);
+    }
+    
     private Bundle installBundle(ManagementContext mgmt, String bundleUrl) throws Exception
{
         OsgiManager osgiManager = ((ManagementContextInternal)mgmt).getOsgiManager().get();
         Framework framework = osgiManager.getFramework();


Mime
View raw message