You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by sv...@apache.org on 2016/07/11 12:45:51 UTC
[5/8] brooklyn-server git commit: Change classloading order in
ClassLoaderUtils
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 <al...@gmail.com>
Authored: Thu Jul 7 14:34:04 2016 +0100
Committer: Aled Sage <al...@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();