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:47 UTC

[1/8] brooklyn-server git commit: OSGi classloading: incorporate review comments

Repository: brooklyn-server
Updated Branches:
  refs/heads/master c3dc6e847 -> 12c645ecf


OSGi classloading: incorporate review comments


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

Branch: refs/heads/master
Commit: cde747f39a2af547fd0d4c7da02ffdf168de3e1f
Parents: 7735787
Author: Aled Sage <al...@gmail.com>
Authored: Thu Jul 7 23:40:28 2016 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Sat Jul 9 10:58:36 2016 +0100

----------------------------------------------------------------------
 .../brooklyn/camp/brooklyn/RebindOsgiTest.java  | 20 ++++++++++++++++++--
 .../brooklyn/util/core/ClassLoaderUtils.java    | 11 +++++++----
 .../mgmt/persist/XmlMementoSerializerTest.java  |  2 +-
 .../util/core/ClassLoaderUtilsTest.java         |  7 +++++++
 4 files changed, 33 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/cde747f3/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 46387da..2f2f462 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
@@ -40,6 +40,7 @@ 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.test.support.TestResourceUnavailableException;
 import org.apache.brooklyn.util.core.osgi.Osgis;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
@@ -62,8 +63,10 @@ import com.google.common.collect.Lists;
 
 public class RebindOsgiTest extends AbstractYamlRebindTest {
 
+    @SuppressWarnings("unused")
     private static final Logger LOG = LoggerFactory.getLogger(RebindOsgiTest.class);
 
+    private static final String OSGI_BUNDLE_PATH = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH;
     private static final String OSGI_BUNDLE_URL = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
     private static final String OSGI_BUNDLE_SYMBOLIC_NAME = "org.apache.brooklyn.test.resources.osgi.brooklyn-test-osgi-entities";
     private static final String OSGI_ENTITY_TYPE = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY;
@@ -155,6 +158,8 @@ public class RebindOsgiTest extends AbstractYamlRebindTest {
 
     @Test
     public void testValInEntityFromOtherBundle() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OSGI_BUNDLE_PATH);
+
         installBundle(mgmt(), OSGI_BUNDLE_URL);
         bundleUrlsToInstallOnRebind.add(OSGI_BUNDLE_URL);
         
@@ -180,6 +185,8 @@ public class RebindOsgiTest extends AbstractYamlRebindTest {
     
     @Test
     public void testEntityAndPolicyFromCatalogOsgi() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OSGI_BUNDLE_PATH);
+        
         String appSymbolicName = "my.catalog.app.id.load";
         String appVersion = "0.1.0";
         String appCatalogFormat = Joiner.on("\n").join(
@@ -228,6 +235,8 @@ public class RebindOsgiTest extends AbstractYamlRebindTest {
 
     @Test
     public void testJavaPojoFromCatalogOsgi() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OSGI_BUNDLE_PATH);
+        
         String appSymbolicName = "my.catalog.app.id.load";
         String appVersion = "0.1.0";
         String appCatalogFormat = Joiner.on("\n").join(
@@ -270,6 +279,8 @@ public class RebindOsgiTest extends AbstractYamlRebindTest {
     
     @Test
     public void testBrooklynObjectDslFromCatalogOsgi() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OSGI_BUNDLE_PATH);
+        
         String appSymbolicName = "my.catalog.app.id.load";
         String appVersion = "0.1.0";
         String appCatalogFormat = Joiner.on("\n").join(
@@ -329,6 +340,9 @@ public class RebindOsgiTest extends AbstractYamlRebindTest {
      */
     @Test
     public void testUsesCatalogBundleVersion() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiVersionMoreEntityTest.BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiVersionMoreEntityTest.BROOKLYN_TEST_MORE_ENTITIES_V2_PATH);
+        
         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";
@@ -382,6 +396,8 @@ public class RebindOsgiTest extends AbstractYamlRebindTest {
     // Need to reproduce that in a simpler use-case.
     @Test
     public void testBrooklynObjectDslFromCatalogOsgiInPolicy() throws Exception {
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OSGI_BUNDLE_PATH);
+        
         String appSymbolicName = "my.catalog.app.id.load";
         String appVersion = "0.1.0";
         String appCatalogFormat = Joiner.on("\n").join(
@@ -429,7 +445,7 @@ public class RebindOsgiTest extends AbstractYamlRebindTest {
     
     private Object newOsgiSimpleObject(String val) throws Exception {
         Class<?> osgiObjectClazz = getBundle(mgmt(), OSGI_BUNDLE_SYMBOLIC_NAME).loadClass(OSGI_OBJECT_TYPE);
-        return Reflections.invokeConstructorWithArgs(osgiObjectClazz, val).get();
+        return Reflections.invokeConstructorFromArgs(osgiObjectClazz, val).get();
     }
     
     private void assertOsgiSimpleObjectsEqual(Object val1, Object val2) throws Exception {
@@ -444,7 +460,7 @@ public class RebindOsgiTest extends AbstractYamlRebindTest {
 
     private String getOsgiSimpleObjectsVal(Object val) throws Exception {
         assertNotNull(val);
-        return (String) Reflections.invokeMethodWithArgs(val, "getVal", ImmutableList.of()).get();
+        return (String) Reflections.invokeMethodFromArgs(val, "getVal", ImmutableList.of()).get();
     }
     
     private Bundle installBundle(ManagementContext mgmt, String bundleUrl) throws Exception {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/cde747f3/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 b6d39e9..179cc4f 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
@@ -72,7 +72,8 @@ public class ClassLoaderUtils {
     }
 
     public ClassLoaderUtils(Class<?> callingClass) {
-        this.classLoader = checkNotNull(callingClass, "callingClass").getClassLoader();
+        checkNotNull(callingClass, "callingClass");
+        this.classLoader = (callingClass.getClassLoader() != null) ? callingClass.getClassLoader() : getClass().getClassLoader();
         this.entity = null;
         this.mgmt = null;
     }
@@ -90,13 +91,15 @@ public class ClassLoaderUtils {
     }
 
     public ClassLoaderUtils(Class<?> callingClass, Entity entity) {
-        this.classLoader = checkNotNull(callingClass, "callingClass").getClassLoader();
+        checkNotNull(callingClass, "callingClass");
+        this.classLoader = (callingClass.getClassLoader() != null) ? callingClass.getClassLoader() : getClass().getClassLoader();
         this.entity = checkNotNull(entity, "entity");
         this.mgmt = ((EntityInternal)entity).getManagementContext();
     }
 
     public ClassLoaderUtils(Class<?> callingClass, @Nullable ManagementContext mgmt) {
-        this.classLoader = checkNotNull(callingClass, "callingClass").getClassLoader();
+        checkNotNull(callingClass, "callingClass");
+        this.classLoader = (callingClass.getClassLoader() != null) ? callingClass.getClassLoader() : getClass().getClassLoader();
         this.entity = null;
         this.mgmt = checkNotNull(mgmt, "mgmt");
     }
@@ -180,7 +183,7 @@ public class ClassLoaderUtils {
             }
         }
 
-        Class<?> cls = tryLoadFromBundleWhiteList(name);
+        Class<?> cls = tryLoadFromBundleWhiteList(className);
         if (cls != null) {
             return cls;
         }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/cde747f3/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 e26983b..069416e 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
@@ -411,7 +411,7 @@ public class XmlMementoSerializerTest {
         
         String classname = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_OBJECT;
         Class<?> osgiObjectClazz = bundle.loadClass(classname);
-        Object obj = Reflections.invokeConstructorWithArgs(osgiObjectClazz, "myval").get();
+        Object obj = Reflections.invokeConstructorFromArgs(osgiObjectClazz, "myval").get();
 
         assertSerializeAndDeserialize(obj);
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/cde747f3/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 98a93ef..b5bfbc2 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
@@ -31,6 +31,7 @@ import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.mgmt.osgi.OsgiStandaloneTest;
 import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.test.support.TestResourceUnavailableException;
 import org.apache.brooklyn.util.core.osgi.Osgis;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.osgi.OsgiTestResources;
@@ -77,9 +78,12 @@ public class ClassLoaderUtilsTest {
     
     @Test
     public void testLoadClassInOsgi() throws Exception {
+        String bundlePath = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH;
         String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
         String classname = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY;
         
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), bundlePath);
+
         mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
         Bundle bundle = installBundle(mgmt, bundleUrl);
         
@@ -92,9 +96,12 @@ public class ClassLoaderUtilsTest {
     
     @Test
     public void testLoadClassInOsgiWhiteList() throws Exception {
+        String bundlePath = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH;
         String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
         String classname = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY;
         
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), bundlePath);
+
         mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
         Bundle bundle = installBundle(mgmt, bundleUrl);
         


[6/8] brooklyn-server git commit: Persistence hacks

Posted by sv...@apache.org.
Persistence hacks


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

Branch: refs/heads/master
Commit: a4930f02734c80846ae6c7a7469ead9a145ccd5b
Parents: c3dc6e8
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Wed Jul 6 17:37:19 2016 +0300
Committer: Aled Sage <al...@gmail.com>
Committed: Sat Jul 9 10:58:36 2016 +0100

----------------------------------------------------------------------
 .../core/mgmt/persist/XmlMementoSerializer.java | 16 +++++++++++-
 .../core/mgmt/rebind/RebindIteration.java       | 26 +++++++++++++++++---
 .../brooklyn/util/core/ClassLoaderUtils.java    | 20 +++++++++------
 .../src/main/resources/etc/default.catalog.bom  |  2 +-
 4 files changed, 52 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a4930f02/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java
index e9085dc..58df79f 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java
@@ -60,6 +60,7 @@ import org.apache.brooklyn.core.mgmt.rebind.dto.BasicLocationMemento;
 import org.apache.brooklyn.core.mgmt.rebind.dto.BasicPolicyMemento;
 import org.apache.brooklyn.core.mgmt.rebind.dto.MutableBrooklynMemento;
 import org.apache.brooklyn.core.sensor.BasicAttributeSensor;
+import org.apache.brooklyn.util.core.ClassLoaderUtils;
 import org.apache.brooklyn.util.core.xstream.XmlSerializer;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.text.Strings;
@@ -94,10 +95,23 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento
         this(classLoader, DeserializingClassRenamesProvider.loadDeserializingClassRenames());
     }
     
+    private static class CustomClassLoader extends ClassLoader {
+        private ClassLoaderUtils loader;
+        private CustomClassLoader(ClassLoader cl) {
+            loader = new ClassLoaderUtils(cl);
+        }
+
+        @Override
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+            return loader.loadClass(name);
+        }
+        
+    }
+    
     public XmlMementoSerializer(ClassLoader classLoader, Map<String, String> deserializingClassRenames) {
         super(deserializingClassRenames);
         this.classLoader = checkNotNull(classLoader, "classLoader");
-        xstream.setClassLoader(this.classLoader);
+        xstream.setClassLoader(new CustomClassLoader(this.classLoader));
         
         // old (deprecated in 070? or earlier) single-file persistence uses this keyword; TODO remove soon in 080 ?
         xstream.alias("brooklyn", MutableBrooklynMemento.class);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a4930f02/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
index 6c0bd9d..1f1ac2e 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
@@ -91,10 +91,12 @@ import org.apache.brooklyn.core.objs.proxy.InternalPolicyFactory;
 import org.apache.brooklyn.core.policy.AbstractPolicy;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.ClassLoaderUtils;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.javalang.Reflections;
+import org.apache.brooklyn.util.javalang.Reflections.ReflectionNotFoundException;
 import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.time.Duration;
 import org.apache.brooklyn.util.time.Time;
@@ -956,7 +958,7 @@ public abstract class RebindIteration {
             }
             
             try {
-                return new LoadedClass<T>((Class<T>)reflections.loadClass(jType), catalogItemId);
+                return new LoadedClass<T>((Class<T>)loadClass(jType), catalogItemId);
             } catch (Exception e) {
                 Exceptions.propagateIfFatal(e);
                 LOG.warn("Unable to load "+jType+" using reflections; will try standard context");
@@ -980,11 +982,29 @@ public abstract class RebindIteration {
             }
         }
 
+        protected Class<?> loadClass(String jType) throws ClassNotFoundException {
+            try {
+            return reflections.loadClass(jType);
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+            }
+            return new ClassLoaderUtils(reflections.getClassLoader()).loadClass(jType);
+        }
+
+        @SuppressWarnings("unchecked")
+        public <T> Class<? extends T> loadClass(String classname, Class<T> superType) {
+            try {
+                return (Class<? extends T>) loadClass(classname);
+            } catch (ClassNotFoundException e) {
+                throw Exceptions.propagate(e);
+            }
+        }
+
         /**
          * Constructs a new location, passing to its constructor the location id and all of memento.getFlags().
          */
         protected Location newLocation(String locationId, String locationType) {
-            Class<? extends Location> locationClazz = reflections.loadClass(locationType, Location.class);
+            Class<? extends Location> locationClazz = loadClass(locationType, Location.class);
 
             if (InternalFactory.isNewStyle(locationClazz)) {
                 // Not using loationManager.createLocation(LocationSpec) because don't want init() to be called
@@ -1110,7 +1130,7 @@ public abstract class RebindIteration {
             String id = memento.getId();
             // catalog item subtypes are internal to brooklyn, not in osgi
             String itemType = checkNotNull(memento.getType(), "catalog item type of %s must not be null in memento", id);
-            Class<? extends CatalogItem> clazz = reflections.loadClass(itemType, CatalogItem.class);
+            Class<? extends CatalogItem> clazz = loadClass(itemType, CatalogItem.class);
             return invokeConstructor(reflections, clazz, new Object[]{});
         }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a4930f02/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 644f07a..00fd05f 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
@@ -57,7 +57,7 @@ public class ClassLoaderUtils {
 
     // Class.forName gets the class loader from the calling class.
     // We don't have access to the same reflection API so need to pass it explicitly.
-    private final Class<?> callingClass;
+    private final ClassLoader classLoader;
     private final Entity entity;
     private final ManagementContext mgmt;
 
@@ -69,19 +69,25 @@ public class ClassLoaderUtils {
     }
 
     public ClassLoaderUtils(Class<?> callingClass) {
-        this.callingClass = callingClass;
+        this.classLoader = callingClass.getClassLoader();
+        this.entity = null;
+        this.mgmt = null;
+    }
+
+    public ClassLoaderUtils(ClassLoader cl) {
+        this.classLoader = cl;
         this.entity = null;
         this.mgmt = null;
     }
 
     public ClassLoaderUtils(Class<?> callingClass, Entity entity) {
-        this.callingClass = callingClass;
+        this.classLoader = callingClass.getClassLoader();
         this.entity = entity;
         this.mgmt = ((EntityInternal)entity).getManagementContext();
     }
 
     public ClassLoaderUtils(Class<?> callingClass, @Nullable ManagementContext mgmt) {
-        this.callingClass = callingClass;
+        this.classLoader = callingClass.getClassLoader();
         this.entity = null;
         this.mgmt = mgmt;
     }
@@ -132,8 +138,8 @@ public class ClassLoaderUtils {
         }
 
         try {
-            // Used instead of callingClass.getClassLoader() as it could be null (only for bootstrap classes)
-            return Class.forName(name, true, callingClass.getClassLoader());
+            // Used instead of callingClass.getClassLoader().loadClass(...) as it could be null (only for bootstrap classes)
+            return Class.forName(name, true, classLoader);
         } catch (ClassNotFoundException e) {
         }
 
@@ -171,7 +177,7 @@ public class ClassLoaderUtils {
             }
             return SystemFrameworkLoader.get().loadClassFromBundle(className, bundle.get());
         } else {
-            return Class.forName(className);
+            return Class.forName(className, true, classLoader);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a4930f02/karaf/apache-brooklyn/src/main/resources/etc/default.catalog.bom
----------------------------------------------------------------------
diff --git a/karaf/apache-brooklyn/src/main/resources/etc/default.catalog.bom b/karaf/apache-brooklyn/src/main/resources/etc/default.catalog.bom
index 8184c6e..e1cfe99 100644
--- a/karaf/apache-brooklyn/src/main/resources/etc/default.catalog.bom
+++ b/karaf/apache-brooklyn/src/main/resources/etc/default.catalog.bom
@@ -7,7 +7,7 @@ brooklyn.catalog:
   items:
   - brooklyn.libraries:
     - name: org.apache.brooklyn.karaf-init
-      version: 0.10.0.SNAPSHOT # BROOKLYN_VERSION
+      version: "0.10.0.SNAPSHOT" # BROOKLYN_VERSION
     include: classpath://catalog-classes.bom
 
   - id: server


[4/8] brooklyn-server git commit: Handle wrapped ClassNotFoundException

Posted by sv...@apache.org.
Handle wrapped ClassNotFoundException

And try not to wrap it!


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

Branch: refs/heads/master
Commit: 77357873a1feec996cd6b3f8e98f01e6999c70ea
Parents: 2654305
Author: Aled Sage <al...@gmail.com>
Authored: Thu Jul 7 23:05:38 2016 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Sat Jul 9 10:58:36 2016 +0100

----------------------------------------------------------------------
 ...ssLoaderFromBrooklynClassLoadingContext.java | 11 ++--
 .../brooklyn/util/core/ClassLoaderUtils.java    | 23 ++++---
 ...aderFromBrooklynClassLoadingContextTest.java | 65 ++++++++++++++++++++
 3 files changed, 87 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/77357873/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/ClassLoaderFromBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/ClassLoaderFromBrooklynClassLoadingContext.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/ClassLoaderFromBrooklynClassLoadingContext.java
index f36e2ac..35d3b05 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/ClassLoaderFromBrooklynClassLoadingContext.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/ClassLoaderFromBrooklynClassLoadingContext.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.core.mgmt.classloading;
 import java.net.URL;
 
 import org.apache.brooklyn.api.mgmt.classloading.BrooklynClassLoadingContext;
+import org.apache.brooklyn.util.guava.Maybe;
 
 public class ClassLoaderFromBrooklynClassLoadingContext extends ClassLoader {
 
@@ -36,15 +37,15 @@ public class ClassLoaderFromBrooklynClassLoadingContext extends ClassLoader {
     }
     
     @Override
-    public Class<?> findClass(String className) throws ClassNotFoundException {
-        Class<?> result = clc.loadClass(className);
-        if (result!=null) return result;
+    protected Class<?> findClass(String className) throws ClassNotFoundException {
+        Maybe<Class<?>> result = clc.tryLoadClass(className);
+        if (result.isPresent()) return result.get();
         
         // last resort. see comment in XStream CompositeClassLoader
         ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
         if (contextClassLoader != null) {
-            result = contextClassLoader.loadClass(className);
-            if (result!=null) return result;
+            Class<?> result2 = contextClassLoader.loadClass(className);
+            if (result2 != null) return result2;
         }
         return null;
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/77357873/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 b46f68d..b6d39e9 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
@@ -172,13 +172,7 @@ public class ClassLoaderUtils {
                     try {
                         return loader.loadClass(className);
                     } catch (IllegalStateException e) {
-                        ClassNotFoundException cnfe = Exceptions.getFirstThrowableOfType(e, ClassNotFoundException.class);
-                        NoClassDefFoundError ncdfe = Exceptions.getFirstThrowableOfType(e, NoClassDefFoundError.class);
-                        if (cnfe == null && ncdfe == null) {
-                            throw e;
-                        } else {
-                            // ignore, fall back to Class.forName(...)
-                        }
+                        propagateIfCauseNotClassNotFound(e);
                     }
                 } else {
                     log.warn("Entity " + entity + " refers to non-existent catalog item " + catalogItemId + ". Trying to load class " + name);
@@ -196,12 +190,16 @@ public class ClassLoaderUtils {
             // 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(className);
+        } catch (IllegalStateException e) {
+            propagateIfCauseNotClassNotFound(e);
         } catch (ClassNotFoundException e) {
         }
 
         if (mgmt != null) {
             try {
                 return mgmt.getCatalogClassLoader().loadClass(name);
+            } catch (IllegalStateException e) {
+                propagateIfCauseNotClassNotFound(e);
             } catch (ClassNotFoundException e) {
             }
         }
@@ -214,6 +212,17 @@ public class ClassLoaderUtils {
         }
     }
 
+    protected void propagateIfCauseNotClassNotFound(IllegalStateException e) {
+        // TODO loadClass() should not throw IllegalStateException; should throw ClassNotFoundException without wrapping.
+        ClassNotFoundException cnfe = Exceptions.getFirstThrowableOfType(e, ClassNotFoundException.class);
+        NoClassDefFoundError ncdfe = Exceptions.getFirstThrowableOfType(e, NoClassDefFoundError.class);
+        if (cnfe == null && ncdfe == null) {
+            throw e;
+        } else {
+            // ignore, try next way of loading
+        }
+    }
+    
     public Class<?> loadClass(String symbolicName, @Nullable String version, String className) throws ClassNotFoundException {
         Framework framework = getFramework();
         if (framework != null) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/77357873/core/src/test/java/org/apache/brooklyn/core/mgmt/classloading/ClassLoaderFromBrooklynClassLoadingContextTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/classloading/ClassLoaderFromBrooklynClassLoadingContextTest.java b/core/src/test/java/org/apache/brooklyn/core/mgmt/classloading/ClassLoaderFromBrooklynClassLoadingContextTest.java
new file mode 100644
index 0000000..997ff5c
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/classloading/ClassLoaderFromBrooklynClassLoadingContextTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016 The Apache Software Foundation.
+ *
+ * Licensed 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.core.mgmt.classloading;
+
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.mgmt.classloading.BrooklynClassLoadingContext;
+import org.apache.brooklyn.api.typereg.OsgiBundleWithUrl;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.entity.stock.BasicApplication;
+import org.apache.brooklyn.test.Asserts;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class ClassLoaderFromBrooklynClassLoadingContextTest {
+
+    private LocalManagementContext mgmt;
+    private CatalogItem<?, ?> item;
+    private ClassLoaderFromBrooklynClassLoadingContext loader;
+    
+    @BeforeMethod(alwaysRun=true)
+    @SuppressWarnings("deprecation")
+    public void setUp() throws Exception {
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
+        item = mgmt.getCatalog().addItem(BasicApplication.class);
+        
+        BrooklynClassLoadingContext clc = new OsgiBrooklynClassLoadingContext(mgmt, item.getCatalogItemId(), ImmutableList.<OsgiBundleWithUrl>of());
+        loader = new ClassLoaderFromBrooklynClassLoadingContext(clc);
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (mgmt != null) {
+            Entities.destroyAll(mgmt);
+            mgmt = null;
+        }
+    }
+
+    @Test
+    public void testLoadNonExistantClassThrowsClassNotFound() throws Exception {
+        try {
+            loader.loadClass("my.clazz.does.not.Exist");
+            Asserts.shouldHaveFailedPreviously();
+        } catch (ClassNotFoundException e) {
+            // success;
+        }
+    }
+}


[5/8] brooklyn-server git commit: Change classloading order in ClassLoaderUtils

Posted by sv...@apache.org.
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();


[8/8] brooklyn-server git commit: Closes #237

Posted by sv...@apache.org.
Closes #237

Misc osgi classloading

For review only at this point.

Please first merge https://github.com/apache/brooklyn-server/pull/236, then I'll rebase this.


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

Branch: refs/heads/master
Commit: 12c645ecfa30a5a37e7bacdd87e5be0ce62f2e30
Parents: c3dc6e8 3bb4788
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Mon Jul 11 15:45:37 2016 +0300
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Mon Jul 11 15:45:37 2016 +0300

----------------------------------------------------------------------
 .../spi/dsl/methods/BrooklynDslCommon.java      |  63 ++-
 .../brooklyn/camp/brooklyn/RebindOsgiTest.java  | 477 +++++++++++++++++++
 ...ssLoaderFromBrooklynClassLoadingContext.java |  11 +-
 .../brooklyn/core/mgmt/ha/OsgiManager.java      |   1 -
 .../core/mgmt/persist/XmlMementoSerializer.java | 204 +++++---
 .../core/mgmt/rebind/RebindIteration.java       |  30 +-
 .../brooklyn/util/core/ClassLoaderUtils.java    | 136 ++++--
 .../apache/brooklyn/util/core/osgi/Osgis.java   |   6 +
 ...aderFromBrooklynClassLoadingContextTest.java |  65 +++
 ...entoSerializerDelegatingClassLoaderTest.java | 140 ++++++
 .../mgmt/persist/XmlMementoSerializerTest.java  | 123 ++++-
 .../util/core/ClassLoaderUtilsTest.java         |  39 ++
 .../src/main/resources/etc/default.catalog.bom  |   2 +-
 .../test/osgi/entities/SimpleApplication.java   |   1 -
 .../osgi/entities/SimpleApplicationImpl.java    |   1 -
 .../test/osgi/entities/SimpleEntity.java        |   9 +-
 .../test/osgi/entities/SimpleEntityImpl.java    |   1 -
 .../test/osgi/entities/SimpleObject.java        |  56 +++
 .../test/osgi/entities/SimplePolicy.java        |   6 +-
 .../test/osgi/entities/more/MoreObject.java     |  56 +++
 .../test/osgi/entities/more/MoreObject.java     |  56 +++
 .../brooklyn/util/osgi/VersionedName.java       |  11 +-
 .../brooklyn/util/osgi/OsgiTestResources.java   |   3 +
 .../osgi/brooklyn-test-osgi-entities.jar        | Bin 17775 -> 19168 bytes
 .../brooklyn-test-osgi-more-entities_0.1.0.jar  | Bin 14964 -> 16004 bytes
 .../brooklyn-test-osgi-more-entities_0.2.0.jar  | Bin 15646 -> 16906 bytes
 26 files changed, 1356 insertions(+), 141 deletions(-)
----------------------------------------------------------------------



[2/8] brooklyn-server git commit: OSGi classloading for persistence

Posted by sv...@apache.org.
OSGi classloading for persistence

* classnames are written out with the OSGi bundle\u2019s symbolic name
  as a prefix in persisted state (unless from a white-listed brooklyn
  bundle)
* stream custom classloading to handle the bundle-name prefix
* adds SimpleObject to osgi jar (and adds config keys to entity)
* adds tests for rebind with OSGi


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

Branch: refs/heads/master
Commit: a555beac618c931d0a1a682f34f1158cb23b3df2
Parents: 25605ca
Author: Aled Sage <al...@gmail.com>
Authored: Thu Jul 7 00:12:30 2016 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Sat Jul 9 10:58:36 2016 +0100

----------------------------------------------------------------------
 .../brooklyn/camp/brooklyn/RebindOsgiTest.java  | 398 +++++++++++++++++++
 .../brooklyn/core/mgmt/ha/OsgiManager.java      |   1 -
 .../core/mgmt/persist/XmlMementoSerializer.java | 216 ++++++----
 .../core/mgmt/rebind/RebindIteration.java       |   4 +-
 .../brooklyn/util/core/ClassLoaderUtils.java    |  39 +-
 .../apache/brooklyn/util/core/osgi/Osgis.java   |   6 +
 ...entoSerializerDelegatingClassLoaderTest.java | 140 +++++++
 .../mgmt/persist/XmlMementoSerializerTest.java  | 115 +++++-
 .../util/core/ClassLoaderUtilsTest.java         |  12 +
 .../test/osgi/entities/SimpleApplication.java   |   1 -
 .../osgi/entities/SimpleApplicationImpl.java    |   1 -
 .../test/osgi/entities/SimpleEntity.java        |   9 +-
 .../test/osgi/entities/SimpleEntityImpl.java    |   1 -
 .../test/osgi/entities/SimpleObject.java        |  56 +++
 .../test/osgi/entities/SimplePolicy.java        |   6 +-
 .../brooklyn/util/osgi/VersionedName.java       |  11 +-
 .../brooklyn/util/osgi/OsgiTestResources.java   |   3 +
 17 files changed, 909 insertions(+), 110 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/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
new file mode 100644
index 0000000..752b066
--- /dev/null
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/RebindOsgiTest.java
@@ -0,0 +1,398 @@
+/*
+ * 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.camp.brooklyn;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import java.io.File;
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.StartableApplication;
+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.sensor.Sensors;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.util.core.osgi.Osgis;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.javalang.Reflections;
+import org.apache.brooklyn.util.osgi.OsgiTestResources;
+import org.jclouds.compute.domain.OsFamily;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.launch.Framework;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+public class RebindOsgiTest extends AbstractYamlRebindTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RebindOsgiTest.class);
+
+    private static final String OSGI_BUNDLE_URL = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
+    private static final String OSGI_BUNDLE_SYMBOLIC_NAME = "org.apache.brooklyn.test.resources.osgi.brooklyn-test-osgi-entities";
+    private static final String OSGI_ENTITY_TYPE = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY;
+    private static final String OSGI_POLICY_TYPE = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_POLICY;
+    private static final String OSGI_OBJECT_TYPE = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_OBJECT;
+    private static final String OSGI_ENTITY_CONFIG_NAME = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY_CONFIG_NAME;
+    private static final String OSGI_ENTITY_SENSOR_NAME = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY_SENSOR_NAME;
+
+    private List<String> bundleUrlsToInstallOnRebind;
+    
+    @BeforeMethod(alwaysRun = true)
+    @Override
+    public void setUp() throws Exception {
+        bundleUrlsToInstallOnRebind = Lists.newArrayList();
+        super.setUp();
+    }
+
+    @Override
+    protected boolean useOsgi() {
+        return true;
+    }
+    
+    @Override
+    protected LocalManagementContext createNewManagementContext(File mementoDir) {
+        LocalManagementContext result = super.createNewManagementContext(mementoDir);
+        for (String bundleUrl : bundleUrlsToInstallOnRebind) {
+            try {
+                installBundle(result, bundleUrl);
+            } catch (Exception e) {
+                throw Exceptions.propagate(e);
+            }
+        }
+        return result;
+    }
+    
+    @DataProvider(name = "valInEntityDataProvider")
+    public Object[][] valInEntityDataProvider() {
+        return new Object[][] {
+            {Predicates.alwaysTrue(), false},
+            {Predicates.alwaysTrue(), true},
+            {OsFamily.CENTOS, false},
+            {OsFamily.CENTOS, true},
+        };
+    }
+ 
+    @Test(dataProvider = "valInEntityDataProvider")
+    public void testValInEntity(Object val, boolean useOsgi) throws Exception {
+        String appSymbolicName = "my.catalog.app.id.load";
+        String appVersion = "0.1.0";
+        String appCatalogFormat;
+        if (useOsgi) {
+            appCatalogFormat = Joiner.on("\n").join(
+                    "brooklyn.catalog:",
+                    "  id: " + appSymbolicName,
+                    "  version: " + appVersion,
+                    "  itemType: entity",
+                    "  libraries:",
+                    "  - " + OSGI_BUNDLE_URL,
+                    "  item:",
+                    "    type: " + OSGI_ENTITY_TYPE);
+        } else {
+            appCatalogFormat = Joiner.on("\n").join(
+                    "brooklyn.catalog:",
+                    "  id: " + appSymbolicName,
+                    "  version: " + appVersion,
+                    "  itemType: entity",
+                    "  item:",
+                    "    type: " + TestEntity.class.getName());
+        }
+        
+        // Create the catalog items
+        Iterables.getOnlyElement(addCatalogItems(String.format(appCatalogFormat, appVersion)));
+        
+        // Create an app, using that catalog item
+        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());
+        origEntity.config().set(TestEntity.CONF_OBJECT, val);
+        
+        // Rebind
+        rebind();
+
+        Entity newEntity = Iterables.getOnlyElement(newApp.getChildren());
+        assertEquals(newEntity.config().get(TestEntity.CONF_OBJECT), val);
+    }
+
+    @Test
+    public void testValInEntityFromOtherBundle() throws Exception {
+        installBundle(mgmt(), OSGI_BUNDLE_URL);
+        bundleUrlsToInstallOnRebind.add(OSGI_BUNDLE_URL);
+        
+        // Create an app, using that catalog item
+        String appBlueprintYaml = Joiner.on("\n").join(
+                "services:",
+                "- type: " + TestEntity.class.getName());
+        origApp = (StartableApplication) createAndStartApplication(appBlueprintYaml);
+        Entity origEntity = Iterables.getOnlyElement(origApp.getChildren());
+
+        Object configVal = newOsgiSimpleObject("myEntityConfigVal");
+        origEntity.config().set(ConfigKeys.newConfigKey(Object.class, OSGI_ENTITY_CONFIG_NAME), configVal);
+        
+        // Rebind
+        rebind();
+
+        // Ensure app is still there, and that it is usable - e.g. "stop" effector functions as expected
+        Entity newEntity = Iterables.getOnlyElement(newApp.getChildren());
+
+        Object newConfigVal = newEntity.config().get(ConfigKeys.newConfigKey(Object.class, OSGI_ENTITY_CONFIG_NAME));
+        assertOsgiSimpleObjectsEqual(newConfigVal, configVal);
+    }
+    
+    @Test
+    public void testEntityAndPolicyFromCatalogOsgi() throws Exception {
+        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:",
+                "  - " + OSGI_BUNDLE_URL,
+                "  item:",
+                "    type: " + OSGI_ENTITY_TYPE,
+                "    brooklyn.policies:",
+                "    - type: " + OSGI_POLICY_TYPE);
+        
+        // Create the catalog items
+        Iterables.getOnlyElement(addCatalogItems(String.format(appCatalogFormat, appVersion)));
+        
+        // Create an app, using that catalog item
+        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());
+        Policy origPolicy = Iterables.getOnlyElement(origEntity.policies());
+
+        // Rebind
+        rebind();
+
+        // Ensure app is still there, and that it is usable - e.g. "stop" effector functions as expected
+        Entity newEntity = Iterables.getOnlyElement(newApp.getChildren());
+        Policy newPolicy = Iterables.getOnlyElement(newEntity.policies());
+        assertEquals(newEntity.getCatalogItemId(), appSymbolicName+":"+appVersion);
+        assertEquals(newPolicy.getId(), origPolicy.getId());
+
+        // Ensure stop works as expected
+        newApp.stop();
+        assertFalse(Entities.isManaged(newApp));
+        assertFalse(Entities.isManaged(newEntity));
+        
+        // Ensure can still use catalog item to deploy a new entity
+        StartableApplication app2 = (StartableApplication) createAndStartApplication(appBlueprintYaml);
+        Entity entity2 = Iterables.getOnlyElement(app2.getChildren());
+        assertEquals(entity2.getCatalogItemId(), appSymbolicName+":"+appVersion);
+    }
+
+    @Test
+    public void testJavaPojoFromCatalogOsgi() throws Exception {
+        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:",
+                "  - " + OSGI_BUNDLE_URL,
+                "  item:",
+                "    type: " + OSGI_ENTITY_TYPE);
+        
+        // Create the catalog items
+        Iterables.getOnlyElement(addCatalogItems(String.format(appCatalogFormat, appVersion)));
+        
+        // Create an app, using that catalog item
+        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 = newOsgiSimpleObject("myEntityConfigVal");
+        Object sensorVal = newOsgiSimpleObject("myEntitySensorVal");
+        origEntity.config().set(ConfigKeys.newConfigKey(Object.class, OSGI_ENTITY_CONFIG_NAME), configVal);
+        origEntity.sensors().set(Sensors.newSensor(Object.class, OSGI_ENTITY_SENSOR_NAME), sensorVal);
+        
+        // Rebind
+        rebind();
+
+        // Ensure app is still there, and that it is usable - e.g. "stop" effector functions as expected
+        Entity newEntity = Iterables.getOnlyElement(newApp.getChildren());
+
+        Object newConfigVal = newEntity.config().get(ConfigKeys.newConfigKey(Object.class, OSGI_ENTITY_CONFIG_NAME));
+        Object newSensorVal = newEntity.sensors().get(Sensors.newSensor(Object.class, OSGI_ENTITY_SENSOR_NAME));
+        assertOsgiSimpleObjectsEqual(newConfigVal, configVal);
+        assertOsgiSimpleObjectsEqual(newSensorVal, sensorVal);
+    }
+    
+    @Test
+    public void testBrooklynObjectDslFromCatalogOsgi() throws Exception {
+        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:",
+                "  - " + OSGI_BUNDLE_URL,
+                "  item:",
+                "    type: " + OSGI_ENTITY_TYPE,
+                "    brooklyn.config:",
+                "      " + OSGI_ENTITY_CONFIG_NAME + ":",
+                "        $brooklyn:object:",
+                "          type: " + OSGI_OBJECT_TYPE,
+                "          object.fields:",
+                "            val: myEntityVal");
+        
+        // Create the catalog items
+        Iterables.getOnlyElement(addCatalogItems(String.format(appCatalogFormat, appVersion)));
+        
+        // Create an app, using that catalog item
+        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, OSGI_ENTITY_CONFIG_NAME));
+        assertEquals(getOsgiSimpleObjectsVal(configVal), "myEntityVal");
+        
+        // Rebind
+        rebind();
+
+        // Ensure app is still there, and that it is usable - e.g. "stop" effector functions as expected
+        Entity newEntity = Iterables.getOnlyElement(newApp.getChildren());
+
+        Object newConfigVal = newEntity.config().get(ConfigKeys.newConfigKey(Object.class, OSGI_ENTITY_CONFIG_NAME));
+        assertOsgiSimpleObjectsEqual(newConfigVal, configVal);
+        
+        // Ensure stop works as expected
+        newApp.stop();
+        assertFalse(Entities.isManaged(newApp));
+        assertFalse(Entities.isManaged(newEntity));
+        
+        // Ensure can still use catalog item to deploy a new entity
+        StartableApplication app2 = (StartableApplication) createAndStartApplication(appBlueprintYaml);
+        Entity entity2 = Iterables.getOnlyElement(app2.getChildren());
+        assertEquals(entity2.getCatalogItemId(), appSymbolicName+":"+appVersion);
+    }
+    
+    // TODO Does not do rebind; the config isn't there after rebind.
+    // Need to reproduce that in a simpler use-case.
+    @Test
+    public void testBrooklynObjectDslFromCatalogOsgiInPolicy() throws Exception {
+        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:",
+                "  - " + OSGI_BUNDLE_URL,
+                "  item:",
+                "    type: " + OSGI_ENTITY_TYPE,
+                "    brooklyn.policies:",
+                "    - type: " + OSGI_POLICY_TYPE,
+                "      brooklyn.config:",
+                "        " + OSGI_ENTITY_CONFIG_NAME + ":",
+                "          $brooklyn:object:",
+                "            type: " + OSGI_OBJECT_TYPE,
+                "            object.fields:",
+                "              val: myPolicyVal");
+        
+        // Create the catalog items
+        Iterables.getOnlyElement(addCatalogItems(String.format(appCatalogFormat, appVersion)));
+        
+        // Create an app, using that catalog item
+        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());
+        Policy origPolicy = Iterables.getOnlyElement(origEntity.policies());
+
+        Object policyConfigVal = origPolicy.config().get(ConfigKeys.newConfigKey(Object.class, OSGI_ENTITY_CONFIG_NAME));
+        assertEquals(getOsgiSimpleObjectsVal(policyConfigVal), "myPolicyVal");
+    }
+    
+    private Bundle getBundle(ManagementContext mgmt, final String symbolicName) throws Exception {
+        OsgiManager osgiManager = ((ManagementContextInternal)mgmt).getOsgiManager().get();
+        Framework framework = osgiManager.getFramework();
+        Maybe<Bundle> result = Osgis.bundleFinder(framework)
+                .symbolicName(symbolicName)
+                .find();
+        return result.get();
+    }
+    
+    private Object newOsgiSimpleObject(String val) throws Exception {
+        Class<?> osgiObjectClazz = getBundle(mgmt(), OSGI_BUNDLE_SYMBOLIC_NAME).loadClass(OSGI_OBJECT_TYPE);
+        return Reflections.invokeConstructorWithArgs(osgiObjectClazz, val).get();
+    }
+    
+    private void assertOsgiSimpleObjectsEqual(Object val1, Object val2) throws Exception {
+        if (val2 == null) {
+            assertNull(val1);
+        } else {
+            assertNotNull(val1);
+        }
+        assertEquals(val1.getClass().getName(), val2.getClass().getName());
+        assertEquals(getOsgiSimpleObjectsVal(val1), getOsgiSimpleObjectsVal(val2));
+    }
+
+    private String getOsgiSimpleObjectsVal(Object val) throws Exception {
+        assertNotNull(val);
+        return (String) Reflections.invokeMethodWithArgs(val, "getVal", ImmutableList.of()).get();
+    }
+    
+    private Bundle installBundle(ManagementContext mgmt, String bundleUrl) throws Exception {
+        OsgiManager osgiManager = ((ManagementContextInternal)mgmt).getOsgiManager().get();
+        Framework framework = osgiManager.getFramework();
+        return Osgis.install(framework, bundleUrl);
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/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 28b20b3..2c68c8a 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
@@ -45,7 +45,6 @@ import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.os.Os;
 import org.apache.brooklyn.util.os.Os.DeletionResult;
-import org.apache.brooklyn.util.osgi.SystemFramework;
 import org.apache.brooklyn.util.repeat.Repeater;
 import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.time.Duration;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java
index 58df79f..15b3346 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializer.java
@@ -61,22 +61,29 @@ import org.apache.brooklyn.core.mgmt.rebind.dto.BasicPolicyMemento;
 import org.apache.brooklyn.core.mgmt.rebind.dto.MutableBrooklynMemento;
 import org.apache.brooklyn.core.sensor.BasicAttributeSensor;
 import org.apache.brooklyn.util.core.ClassLoaderUtils;
+import org.apache.brooklyn.util.core.osgi.Osgis;
 import org.apache.brooklyn.util.core.xstream.XmlSerializer;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.text.Strings;
+import org.osgi.framework.Bundle;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.thoughtworks.xstream.MarshallingStrategy;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
 import com.thoughtworks.xstream.converters.Converter;
 import com.thoughtworks.xstream.converters.MarshallingContext;
 import com.thoughtworks.xstream.converters.SingleValueConverter;
 import com.thoughtworks.xstream.converters.UnmarshallingContext;
 import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
+import com.thoughtworks.xstream.core.ClassLoaderReference;
 import com.thoughtworks.xstream.core.ReferencingMarshallingContext;
+import com.thoughtworks.xstream.core.util.Primitives;
 import com.thoughtworks.xstream.io.HierarchicalStreamReader;
 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
 import com.thoughtworks.xstream.io.path.PathTrackingReader;
+import com.thoughtworks.xstream.mapper.CannotResolveClassException;
+import com.thoughtworks.xstream.mapper.DefaultMapper;
 import com.thoughtworks.xstream.mapper.Mapper;
 import com.thoughtworks.xstream.mapper.MapperWrapper;
 
@@ -88,30 +95,17 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento
 
     private static final Logger LOG = LoggerFactory.getLogger(XmlMementoSerializer.class);
 
-    private final ClassLoader classLoader;
+    private final OsgiClassLoader delegatingClassLoader;
     private LookupContext lookupContext;
-
+    
     public XmlMementoSerializer(ClassLoader classLoader) {
         this(classLoader, DeserializingClassRenamesProvider.loadDeserializingClassRenames());
     }
     
-    private static class CustomClassLoader extends ClassLoader {
-        private ClassLoaderUtils loader;
-        private CustomClassLoader(ClassLoader cl) {
-            loader = new ClassLoaderUtils(cl);
-        }
-
-        @Override
-        protected Class<?> findClass(String name) throws ClassNotFoundException {
-            return loader.loadClass(name);
-        }
-        
-    }
-    
     public XmlMementoSerializer(ClassLoader classLoader, Map<String, String> deserializingClassRenames) {
         super(deserializingClassRenames);
-        this.classLoader = checkNotNull(classLoader, "classLoader");
-        xstream.setClassLoader(new CustomClassLoader(this.classLoader));
+        this.delegatingClassLoader = new OsgiClassLoader(classLoader);
+        xstream.setClassLoader(this.delegatingClassLoader);
         
         // old (deprecated in 070? or earlier) single-file persistence uses this keyword; TODO remove soon in 080 ?
         xstream.alias("brooklyn", MutableBrooklynMemento.class);
@@ -156,6 +150,7 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento
     @Override
     protected MapperWrapper wrapMapperForNormalUsage(Mapper next) {
         MapperWrapper mapper = super.wrapMapperForNormalUsage(next);
+        mapper = new OsgiClassnameMapper(mapper);
         mapper = new CustomMapper(mapper, Entity.class, "entityProxy");
         mapper = new CustomMapper(mapper, Location.class, "locationProxy");
         mapper = new UnwantedStateLoggingMapper(mapper);
@@ -175,6 +170,7 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento
     @Override
     public void setLookupContext(LookupContext lookupContext) {
         this.lookupContext = checkNotNull(lookupContext, "lookupContext");
+        delegatingClassLoader.setManagementContext(lookupContext.lookupManagementContext());
     }
 
     @Override
@@ -410,6 +406,47 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento
         }
     }
 
+    public class OsgiClassnameMapper extends MapperWrapper {
+        private final ClassLoaderUtils whiteListRetriever;
+        
+        OsgiClassnameMapper(MapperWrapper mapper) {
+            super(mapper);
+            whiteListRetriever = new ClassLoaderUtils(getClass());
+        }
+        
+        @Override
+        public String serializedClass(Class type) {
+            // TODO What if previous stages have already renamed it?
+            // For example the "outer class renaming stuff"?!
+            String superResult = super.serializedClass(type);
+            if (type != null && type.getName().equals(superResult)) {
+                Optional<Bundle> bundle  = Osgis.getBundleOf(type);
+                if (bundle.isPresent() && !whiteListRetriever.isBundleWhiteListed(bundle.get())) {
+                    return bundle.get().getSymbolicName() + ":" + superResult;
+                }
+            }
+            return superResult;
+        }
+        
+        @Override
+        public Class realClass(String elementName) {
+            CannotResolveClassException tothrow;
+            try {
+                return super.realClass(elementName);
+            } catch (CannotResolveClassException e) {
+                tothrow = e;
+            }
+
+            // Class.forName(elementName, false, classLader) does not seem to like us returned a 
+            // class whose name does not match that passed in. Therefore fallback to using loadClass.
+            try {
+                return xstream.getClassLoaderReference().getReference().loadClass(elementName);
+            } catch (ClassNotFoundException e) {
+                throw new CannotResolveClassException(elementName + " via loadClass", tothrow);
+            }
+        }
+    }
+    
     /** When reading/writing specs, it checks whether there is a catalog item id set and uses it to load */
     public class SpecConverter extends ReflectionConverter {
         SpecConverter() {
@@ -462,7 +499,7 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento
                     RegisteredType cat = lookupContext.lookupManagementContext().getTypeRegistry().get(catalogItemId);
                     if (cat==null) throw new NoSuchElementException("catalog item: "+catalogItemId);
                     BrooklynClassLoadingContext clcNew = CatalogUtils.newClassLoadingContext(lookupContext.lookupManagementContext(), cat);
-                    pushXstreamCustomClassLoader(clcNew);
+                    delegatingClassLoader.pushXstreamCustomClassLoader(clcNew);
                     customLoaderSet = true;
                 }
                 
@@ -473,7 +510,7 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento
             } finally {
                 context.put("SpecConverter.instance", null);
                 if (customLoaderSet) {
-                    popXstreamCustomClassLoader();
+                    delegatingClassLoader.popXstreamCustomClassLoader();
                 }
             }
         }
@@ -497,68 +534,99 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento
         }
     }
     
-    Stack<BrooklynClassLoadingContext> contexts = new Stack<BrooklynClassLoadingContext>();
-    Stack<ClassLoader> cls = new Stack<ClassLoader>();
-    AtomicReference<Thread> xstreamLockOwner = new AtomicReference<Thread>();
-    int lockCount;
-    
-    /** Must be accompanied by a corresponding {@link #popXstreamCustomClassLoader()} when finished. */
-    @SuppressWarnings("deprecation")
-    protected void pushXstreamCustomClassLoader(BrooklynClassLoadingContext clcNew) {
-        acquireXstreamLock();
-        BrooklynClassLoadingContext oldClc;
-        if (!contexts.isEmpty()) {
-            oldClc = contexts.peek();
-        } else {
-            // TODO XmlMementoSerializer should take a BCLC instead of a CL
-            oldClc = JavaBrooklynClassLoadingContext.create(lookupContext.lookupManagementContext(), xstream.getClassLoader());
-        }
-        BrooklynClassLoadingContextSequential clcMerged = new BrooklynClassLoadingContextSequential(lookupContext.lookupManagementContext(),
-            oldClc, clcNew);
-        contexts.push(clcMerged);
-        cls.push(xstream.getClassLoader());
-        ClassLoader newCL = ClassLoaderFromBrooklynClassLoadingContext.of(clcMerged);
-        xstream.setClassLoader(newCL);
-    }
+    @VisibleForTesting
+    static class OsgiClassLoader extends ClassLoader {
+        private final Stack<BrooklynClassLoadingContext> contexts = new Stack<BrooklynClassLoadingContext>();
+        private final Stack<ClassLoader> cls = new Stack<ClassLoader>();
+        private final AtomicReference<Thread> xstreamLockOwner = new AtomicReference<Thread>();
+        private ManagementContext mgmt;
+        private ClassLoader currentClassLoader;
+        private AtomicReference<ClassLoaderUtils> currentLoader = new AtomicReference<>();
+        private int lockCount;
+        
+        protected OsgiClassLoader(ClassLoader classLoader) {
+            setCurrentClassLoader(classLoader);
+        }
+        
+        protected void setManagementContext(ManagementContext mgmt) {
+            this.mgmt = checkNotNull(mgmt, "mgmt");
+            currentLoader.set(new ClassLoaderUtils(currentClassLoader, mgmt));
+        }
 
-    protected void popXstreamCustomClassLoader() {
-        synchronized (xstreamLockOwner) {
-            releaseXstreamLock();
-            xstream.setClassLoader(cls.pop());
-            contexts.pop();
+        @Override
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+            return currentLoader.get().loadClass(name);
         }
-    }
-    
-    protected void acquireXstreamLock() {
-        synchronized (xstreamLockOwner) {
-            while (true) {
-                if (xstreamLockOwner.compareAndSet(null, Thread.currentThread()) || 
-                    Thread.currentThread().equals( xstreamLockOwner.get() )) {
-                    break;
-                }
-                try {
-                    xstreamLockOwner.wait(1000);
-                } catch (InterruptedException e) {
-                    throw Exceptions.propagate(e);
+
+        /** Must be accompanied by a corresponding {@link #popXstreamCustomClassLoader()} when finished. */
+        @SuppressWarnings("deprecation")
+        protected void pushXstreamCustomClassLoader(BrooklynClassLoadingContext clcNew) {
+            acquireXstreamLock();
+            BrooklynClassLoadingContext oldClc;
+            if (!contexts.isEmpty()) {
+                oldClc = contexts.peek();
+            } else {
+                // TODO XmlMementoSerializer should take a BCLC instead of a CL
+                oldClc = JavaBrooklynClassLoadingContext.create(mgmt, getCurrentClassLoader());
+            }
+            BrooklynClassLoadingContextSequential clcMerged = new BrooklynClassLoadingContextSequential(mgmt, oldClc, clcNew);
+            ClassLoader newCL = ClassLoaderFromBrooklynClassLoadingContext.of(clcMerged);
+            contexts.push(clcMerged);
+            cls.push(getCurrentClassLoader());
+            setCurrentClassLoader(newCL);
+        }
+
+        protected void popXstreamCustomClassLoader() {
+            synchronized (xstreamLockOwner) {
+                releaseXstreamLock();
+                setCurrentClassLoader(cls.pop());
+                contexts.pop();
+            }
+        }
+        
+        private ClassLoader getCurrentClassLoader() {
+            return currentClassLoader;
+        }
+        
+        private void setCurrentClassLoader(ClassLoader classLoader) {
+            currentClassLoader = checkNotNull(classLoader);
+            if (mgmt != null) {
+                currentLoader.set(new ClassLoaderUtils(currentClassLoader, mgmt));
+            } else {
+                currentLoader.set(new ClassLoaderUtils(currentClassLoader));
+            }
+        }
+        
+        protected void acquireXstreamLock() {
+            synchronized (xstreamLockOwner) {
+                while (true) {
+                    if (xstreamLockOwner.compareAndSet(null, Thread.currentThread()) || 
+                        Thread.currentThread().equals( xstreamLockOwner.get() )) {
+                        break;
+                    }
+                    try {
+                        xstreamLockOwner.wait(1000);
+                    } catch (InterruptedException e) {
+                        throw Exceptions.propagate(e);
+                    }
                 }
+                lockCount++;
             }
-            lockCount++;
         }
-    }
 
-    protected void releaseXstreamLock() {
-        synchronized (xstreamLockOwner) {
-            if (lockCount<=0) {
-                throw new IllegalStateException("xstream not locked");
-            }
-            if (--lockCount == 0) {
-                if (!xstreamLockOwner.compareAndSet(Thread.currentThread(), null)) {
-                    Thread oldOwner = xstreamLockOwner.getAndSet(null);
-                    throw new IllegalStateException("xstream was locked by "+oldOwner+" but unlock attempt by "+Thread.currentThread());
+        protected void releaseXstreamLock() {
+            synchronized (xstreamLockOwner) {
+                if (lockCount<=0) {
+                    throw new IllegalStateException("xstream not locked");
+                }
+                if (--lockCount == 0) {
+                    if (!xstreamLockOwner.compareAndSet(Thread.currentThread(), null)) {
+                        Thread oldOwner = xstreamLockOwner.getAndSet(null);
+                        throw new IllegalStateException("xstream was locked by "+oldOwner+" but unlock attempt by "+Thread.currentThread());
+                    }
+                    xstreamLockOwner.notifyAll();
                 }
-                xstreamLockOwner.notifyAll();
             }
         }
     }
-
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
index 1f1ac2e..4aee9a8 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
@@ -976,9 +976,9 @@ public abstract class RebindIteration {
                         return new LoadedClass<T>((Class<? extends T>) catalogClass.get(), catalogItemId);
                     }
                 }
-                throw new IllegalStateException("No catalogItemId specified for "+contextSuchAsId+" and can't load class from either classpath or catalog items");
+                throw new IllegalStateException("No catalogItemId specified for "+contextSuchAsId+" and can't load class (" + jType + ") from either classpath or catalog items");
             } else {
-                throw new IllegalStateException("No catalogItemId specified for "+contextSuchAsId+" and can't load class from classpath");
+                throw new IllegalStateException("No catalogItemId specified for "+contextSuchAsId+" and can't load class (" + jType + ") from classpath");
             }
         }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/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 00fd05f..bc46b60 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
@@ -15,6 +15,8 @@
  */
 package org.apache.brooklyn.util.core;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import java.util.List;
 import java.util.regex.Pattern;
 
@@ -41,6 +43,7 @@ import org.osgi.framework.launch.Framework;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Predicate;
 
 public class ClassLoaderUtils {
@@ -69,27 +72,33 @@ public class ClassLoaderUtils {
     }
 
     public ClassLoaderUtils(Class<?> callingClass) {
-        this.classLoader = callingClass.getClassLoader();
+        this.classLoader = checkNotNull(callingClass, "callingClass").getClassLoader();
         this.entity = null;
         this.mgmt = null;
     }
 
     public ClassLoaderUtils(ClassLoader cl) {
-        this.classLoader = cl;
+        this.classLoader = checkNotNull(cl, "classLoader");
         this.entity = null;
         this.mgmt = null;
     }
 
+    public ClassLoaderUtils(ClassLoader cl, @Nullable ManagementContext mgmt) {
+        this.classLoader = checkNotNull(cl, "classLoader");
+        this.entity = null;
+        this.mgmt = checkNotNull(mgmt, "mgmt");
+    }
+
     public ClassLoaderUtils(Class<?> callingClass, Entity entity) {
-        this.classLoader = callingClass.getClassLoader();
-        this.entity = entity;
+        this.classLoader = checkNotNull(callingClass, "callingClass").getClassLoader();
+        this.entity = checkNotNull(entity, "entity");
         this.mgmt = ((EntityInternal)entity).getManagementContext();
     }
 
     public ClassLoaderUtils(Class<?> callingClass, @Nullable ManagementContext mgmt) {
-        this.classLoader = callingClass.getClassLoader();
+        this.classLoader = checkNotNull(callingClass, "callingClass").getClassLoader();
         this.entity = null;
-        this.mgmt = mgmt;
+        this.mgmt = checkNotNull(mgmt, "mgmt");
     }
 
     public Class<?> loadClass(String name) throws ClassNotFoundException {
@@ -139,7 +148,9 @@ public class ClassLoaderUtils {
 
         try {
             // Used instead of callingClass.getClassLoader().loadClass(...) as it could be null (only for bootstrap classes)
-            return Class.forName(name, true, classLoader);
+            // 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);
         } catch (ClassNotFoundException e) {
         }
 
@@ -181,6 +192,12 @@ public class ClassLoaderUtils {
         }
     }
 
+    @Beta
+    public boolean isBundleWhiteListed(Bundle bundle) {
+        WhiteListBundlePredicate p = createBundleMatchingPredicate();
+        return p.apply(bundle);
+    }
+
     protected Framework getFramework() {
         if (mgmt != null) {
             Maybe<OsgiManager> osgiManager = ((ManagementContextInternal)mgmt).getOsgiManager();
@@ -206,11 +223,11 @@ public class ClassLoaderUtils {
 
 
     private static class WhiteListBundlePredicate implements Predicate<Bundle> {
-        private Pattern symbolicName;
-        private Pattern version;
+        private final Pattern symbolicName;
+        private final Pattern version;
 
         private WhiteListBundlePredicate(String symbolicName, String version) {
-            this.symbolicName = Pattern.compile(symbolicName);
+            this.symbolicName = Pattern.compile(checkNotNull(symbolicName, "symbolicName"));
             this.version = version != null ? Pattern.compile(version) : null;
         }
 
@@ -219,7 +236,6 @@ public class ClassLoaderUtils {
             return symbolicName.matcher(input.getSymbolicName()).matches() &&
                     (version == null || version.matcher(input.getVersion().toString()).matches());
         }
-    
     }
 
     private Class<?> tryLoadFromBundleWhiteList(String name) {
@@ -253,5 +269,4 @@ public class ClassLoaderUtils {
         }
         return new WhiteListBundlePredicate(symbolicName, version);
     }
-
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/core/src/main/java/org/apache/brooklyn/util/core/osgi/Osgis.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/osgi/Osgis.java b/core/src/main/java/org/apache/brooklyn/util/core/osgi/Osgis.java
index 82f8723..a33b446 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/osgi/Osgis.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/osgi/Osgis.java
@@ -50,6 +50,7 @@ import org.slf4j.LoggerFactory;
 import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
 import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 
@@ -438,4 +439,9 @@ public class Osgis {
         });
     }
 
+    @Beta
+    public static Optional<Bundle> getBundleOf(Class<?> clazz) {
+        Bundle bundle = org.osgi.framework.FrameworkUtil.getBundle(clazz);
+        return Optional.fromNullable(bundle);
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerDelegatingClassLoaderTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerDelegatingClassLoaderTest.java b/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerDelegatingClassLoaderTest.java
new file mode 100644
index 0000000..deb240a
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerDelegatingClassLoaderTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.core.mgmt.persist;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.entity.AbstractEntity;
+import org.apache.brooklyn.core.entity.Entities;
+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.persist.XmlMementoSerializer.OsgiClassLoader;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.util.core.osgi.Osgis;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.osgi.OsgiTestResources;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.launch.Framework;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Optional;
+
+public class XmlMementoSerializerDelegatingClassLoaderTest {
+
+    private LocalManagementContext mgmt;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (mgmt != null) {
+            Entities.destroyAll(mgmt);
+        }
+    }
+    
+    @Test
+    public void testLoadClassFromBundle() throws Exception {
+        ClassLoader classLoader = getClass().getClassLoader();
+        Bundle apiBundle = getBundle(mgmt, "org.apache.brooklyn.api");
+        Bundle coreBundle = getBundle(mgmt, "org.apache.brooklyn.core");
+        
+        String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
+        Bundle otherBundle = installBundle(mgmt, bundleUrl);
+        
+        assertLoads(classLoader, Entity.class, Optional.of(apiBundle));
+        assertLoads(classLoader, AbstractEntity.class, Optional.of(coreBundle));
+        assertLoads(classLoader, OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY, Optional.of(otherBundle));
+    }
+    
+    @Test
+    public void testLoadClassVanilla() throws Exception {
+        ClassLoader classLoader = getClass().getClassLoader();
+        
+        assertLoads(classLoader, Entity.class, Optional.<Bundle>absent());
+        assertLoads(classLoader, AbstractEntity.class, Optional.<Bundle>absent());
+    }
+    
+    // Tests we can do funny stuff, like return a differently named class from that expected!
+    @Test
+    public void testLoadClassReturningDifferentlyNamedClass() throws Exception {
+        final String specialClassName = "my.madeup.Clazz";
+        
+        ClassLoader classLoader = new ClassLoader() {
+            @Override
+            protected Class<?> findClass(String name) throws ClassNotFoundException {
+                if (name != null && name.equals(specialClassName)) {
+                    return Entity.class;
+                }
+                return getClass().getClassLoader().loadClass(name);
+            }
+        };
+        
+        OsgiClassLoader ocl = new XmlMementoSerializer.OsgiClassLoader(classLoader);
+        ocl.setManagementContext(mgmt);
+        assertEquals(ocl.loadClass(specialClassName), Entity.class);
+        
+        // TODO The line below fails: java.lang.ClassNotFoundException: my/madeup/Clazz
+        //assertEquals(Class.forName(specialClassName, false, ocl).getName(), Entity.class.getName());
+    }
+    
+    private void assertLoads(ClassLoader delegateClassLoader, Class<?> clazz, Optional<Bundle> bundle) throws Exception {
+        OsgiClassLoader ocl = new XmlMementoSerializer.OsgiClassLoader(delegateClassLoader);
+        ocl.setManagementContext(mgmt);
+        String classname = (bundle.isPresent() ? bundle.get().getSymbolicName() + ":" : "") + clazz.getName();
+        assertEquals(ocl.loadClass(classname), clazz);
+        
+        // TODO The line below fails, e.g.: java.lang.ClassNotFoundException: org/apache/brooklyn/api:org/apache/brooklyn/api/entity/Entity
+        //assertEquals(Class.forName(classname, false, ocl), clazz);
+        
+    }
+
+    private void assertLoads(ClassLoader delegateClassLoader, String clazz, Optional<Bundle> bundle) throws Exception {
+        OsgiClassLoader ocl = new XmlMementoSerializer.OsgiClassLoader(delegateClassLoader);
+        ocl.setManagementContext(mgmt);
+        String classname = (bundle.isPresent() ? bundle.get().getSymbolicName() + ":" : "") + clazz;
+        assertEquals(ocl.loadClass(classname).getName(), clazz);
+        
+        // TODO The line below fails, e.g.: java.lang.ClassNotFoundException: org/apache/brooklyn/test/resources/osgi/brooklyn-test-osgi-entities:org/apache/brooklyn/test/osgi/entities/SimpleEntity
+        //assertEquals(Class.forName(classname, false, ocl).getName(), clazz);
+    }
+
+    private Bundle getBundle(ManagementContext mgmt, final String symbolicName) throws Exception {
+        OsgiManager osgiManager = ((ManagementContextInternal)mgmt).getOsgiManager().get();
+        Framework framework = osgiManager.getFramework();
+        Maybe<Bundle> result = Osgis.bundleFinder(framework)
+                .symbolicName(symbolicName)
+                .find();
+        return result.get();
+    }
+    
+    private Bundle installBundle(ManagementContext mgmt, String bundleUrl) throws Exception {
+        OsgiManager osgiManager = ((ManagementContextInternal)mgmt).getOsgiManager().get();
+        Framework framework = osgiManager.getFramework();
+        return Osgis.install(framework, bundleUrl);
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/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 aefd623..9251354 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
@@ -50,6 +50,9 @@ import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder;
 import org.apache.brooklyn.core.catalog.internal.CatalogItemDtoAbstract;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.location.SimulatedLocation;
+import org.apache.brooklyn.core.mgmt.ha.OsgiManager;
+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.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.core.test.entity.TestApplication;
@@ -59,16 +62,22 @@ import org.apache.brooklyn.test.support.TestResourceUnavailableException;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.core.osgi.Osgis;
+import org.apache.brooklyn.util.javalang.Reflections;
 import org.apache.brooklyn.util.net.Networking;
 import org.apache.brooklyn.util.net.UserAndHostAndPort;
 import org.apache.brooklyn.util.osgi.OsgiTestResources;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.launch.Framework;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.google.api.client.repackaged.com.google.common.base.Joiner;
 import com.google.common.base.Objects;
+import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -84,12 +93,21 @@ public class XmlMementoSerializerTest {
     private static final Logger LOG = LoggerFactory.getLogger(XmlMementoSerializerTest.class);
 
     private XmlMementoSerializer<Object> serializer;
-
+    private ManagementContext mgmt;
+    
     @BeforeMethod(alwaysRun=true)
     public void setUp() throws Exception {
         serializer = new XmlMementoSerializer<Object>(XmlMementoSerializerTest.class.getClassLoader());
     }
 
+    @AfterMethod(alwaysRun=true)
+    private void tearDown() {
+        if (mgmt != null) {
+            Entities.destroyAllCatching(mgmt);
+            mgmt = null;
+        }
+    }
+    
     @Test
     public void testRenamedClass() throws Exception {
         serializer = new XmlMementoSerializer<Object>(XmlMementoSerializerTest.class.getClassLoader(),
@@ -337,23 +355,90 @@ public class XmlMementoSerializerTest {
     @Test
     public void testEntitySpecFromOsgi() throws Exception {
         TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_V1_PATH);
-        ManagementContext mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
-        try {
-            RegisteredType ci = OsgiVersionMoreEntityTest.addMoreEntityV1(mgmt, "1.0");
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
+        
+        RegisteredType ci = OsgiVersionMoreEntityTest.addMoreEntityV1(mgmt, "1.0");
             
-            EntitySpec<DynamicCluster> spec = EntitySpec.create(DynamicCluster.class)
-                .configure(DynamicCluster.INITIAL_SIZE, 1)
-                .configure(DynamicCluster.MEMBER_SPEC, mgmt.getTypeRegistry().createSpec(ci, null, EntitySpec.class));
+        EntitySpec<DynamicCluster> spec = EntitySpec.create(DynamicCluster.class)
+            .configure(DynamicCluster.INITIAL_SIZE, 1)
+            .configure(DynamicCluster.MEMBER_SPEC, mgmt.getTypeRegistry().createSpec(ci, null, EntitySpec.class));
+
+        serializer.setLookupContext(new LookupContextImpl(mgmt,
+            ImmutableList.<Entity>of(), ImmutableList.<Location>of(), ImmutableList.<Policy>of(),
+            ImmutableList.<Enricher>of(), ImmutableList.<Feed>of(), ImmutableList.<CatalogItem<?,?>>of(), true));
+        assertSerializeAndDeserialize(spec);
+    }
     
-            serializer.setLookupContext(new LookupContextImpl(mgmt,
+    @Test
+    public void testOsgiBundleNameNotIncludedForWhiteListed() throws Exception {
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
+
+        serializer.setLookupContext(new LookupContextImpl(mgmt,
+            ImmutableList.<Entity>of(), ImmutableList.<Location>of(), ImmutableList.<Policy>of(),
+            ImmutableList.<Enricher>of(), ImmutableList.<Feed>of(), ImmutableList.<CatalogItem<?,?>>of(), true));
+        
+        Object obj = PersistMode.AUTO;
+        
+        assertSerializeAndDeserialize(obj);
+
+        // i.e. not pre-pended with "org.apache.brooklyn.core:"
+        String expectedForm = "<"+PersistMode.class.getName()+">AUTO</"+PersistMode.class.getName()+">";
+        String serializedForm = serializer.toString(obj);
+        assertEquals(serializedForm.trim(), expectedForm.trim());
+    }
+
+    @Test
+    public void testOsgiBundleNamePrefixIncluded() throws Exception {
+        String bundlePath = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH;
+        String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL;
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), bundlePath);
+        
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
+
+        serializer.setLookupContext(new LookupContextImpl(mgmt,
                 ImmutableList.<Entity>of(), ImmutableList.<Location>of(), ImmutableList.<Policy>of(),
                 ImmutableList.<Enricher>of(), ImmutableList.<Feed>of(), ImmutableList.<CatalogItem<?,?>>of(), true));
-            assertSerializeAndDeserialize(spec);
-        } finally {
-            Entities.destroyAllCatching(mgmt);
-        }
+        
+        Bundle bundle = installBundle(mgmt, bundleUrl);
+        
+        String classname = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_OBJECT;
+        Class<?> osgiObjectClazz = bundle.loadClass(classname);
+        Object obj = Reflections.invokeConstructorWithArgs(osgiObjectClazz, "myval").get();
+
+        assertSerializeAndDeserialize(obj);
+
+        // i.e. prepended with bundle name
+        String expectedForm = Joiner.on("\n").join(
+                "<"+bundle.getSymbolicName()+":"+classname+">",
+                "  <val>myval</val>",
+                "</"+bundle.getSymbolicName()+":"+classname+">");
+        String serializedForm = serializer.toString(obj);
+        assertEquals(serializedForm.trim(), expectedForm.trim());
     }
+    
+    // TODO This doesn't get the bundleName - should we expect it to? Is this because of 
+    // how we're using Felix? Would it also be true in Karaf?
+    @Test(groups="Broken")
+    public void testOsgiBundleNamePrefixIncludedForDownstreamDependency() throws Exception {
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
 
+        serializer.setLookupContext(new LookupContextImpl(mgmt,
+                ImmutableList.<Entity>of(), ImmutableList.<Location>of(), ImmutableList.<Policy>of(),
+                ImmutableList.<Enricher>of(), ImmutableList.<Feed>of(), ImmutableList.<CatalogItem<?,?>>of(), true));
+        
+        // Using a guava type (which is a downstream dependency of Brooklyn)
+        String bundleName = "com.goole.guava";
+        String classname = "com.google.common.base.Predicates_-ObjectPredicate";
+        Object obj = Predicates.alwaysTrue();
+
+        assertSerializeAndDeserialize(obj);
+
+        // i.e. prepended with bundle name
+        String expectedForm = "<"+bundleName+":"+classname+">ALWAYS_TRUE</"+bundleName+":"+classname+">";
+        String serializedForm = serializer.toString(obj);
+        assertEquals(serializedForm.trim(), expectedForm.trim());
+    }
+    
     @Test
     public void testImmutableCollectionsWithDanglingEntityRef() throws Exception {
         // If there's a dangling entity in an ImmutableList etc, then discard it entirely.
@@ -647,4 +732,10 @@ public class XmlMementoSerializerTest {
             return Objects.hashCode(myStaticInnerField);
         }
     }
+    
+    private Bundle installBundle(ManagementContext mgmt, String bundleUrl) throws Exception {
+        OsgiManager osgiManager = ((ManagementContextInternal)mgmt).getOsgiManager().get();
+        Framework framework = osgiManager.getFramework();
+        return Osgis.install(framework, bundleUrl);
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/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 bfa5adc..3f2a8e1 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
@@ -19,6 +19,8 @@
 package org.apache.brooklyn.util.core;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
@@ -132,6 +134,16 @@ public class ClassLoaderUtilsTest {
         assertEquals(clu.loadClass(bundle.getSymbolicName() + ":" + bundle.getVersion() + ":" + classname), clazz);
     }
     
+    @Test
+    public void testIsBundleWhiteListed() throws Exception {
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
+        ClassLoaderUtils clu = new ClassLoaderUtils(getClass(), mgmt);
+        
+        assertTrue(clu.isBundleWhiteListed(getBundle(mgmt, "org.apache.brooklyn.core")));
+        assertTrue(clu.isBundleWhiteListed(getBundle(mgmt, "org.apache.brooklyn.api")));
+        assertFalse(clu.isBundleWhiteListed(getBundle(mgmt, "com.google.guava")));
+    }
+    
     private Bundle installBundle(ManagementContext mgmt, String bundleUrl) throws Exception {
         OsgiManager osgiManager = ((ManagementContextInternal)mgmt).getOsgiManager().get();
         Framework framework = osgiManager.getFramework();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleApplication.java
----------------------------------------------------------------------
diff --git a/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleApplication.java b/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleApplication.java
index dcfb495..f561b97 100644
--- a/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleApplication.java
+++ b/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleApplication.java
@@ -18,7 +18,6 @@
  */
 package org.apache.brooklyn.test.osgi.entities;
 
-
 import org.apache.brooklyn.api.entity.ImplementedBy;
 import org.apache.brooklyn.core.entity.StartableApplication;
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleApplicationImpl.java
----------------------------------------------------------------------
diff --git a/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleApplicationImpl.java b/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleApplicationImpl.java
index fe6f1a2..ae4c69d 100644
--- a/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleApplicationImpl.java
+++ b/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleApplicationImpl.java
@@ -18,7 +18,6 @@
  */
 package org.apache.brooklyn.test.osgi.entities;
 
-
 import org.apache.brooklyn.core.entity.AbstractApplication;
 import org.apache.brooklyn.core.entity.StartableApplication;
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleEntity.java
----------------------------------------------------------------------
diff --git a/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleEntity.java b/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleEntity.java
index ffed15f..f256af7 100644
--- a/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleEntity.java
+++ b/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleEntity.java
@@ -19,13 +19,20 @@
 package org.apache.brooklyn.test.osgi.entities;
 
 
+import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.ImplementedBy;
-import org.apache.brooklyn.api.catalog.Catalog;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.sensor.Sensors;
 
 
 @Catalog(name="A Simple Entity", description="Simple entity for test purposes")
 @ImplementedBy(SimpleEntityImpl.class)
 public interface SimpleEntity extends Entity {
+    
+    ConfigKey<Object> SIMPLE_CONFIG = ConfigKeys.newConfigKey(Object.class, "simple.config");
 
+    AttributeSensor<Object> SIMPLE_SENSOR = Sensors.newSensor(Object.class, "simple.sensor");
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleEntityImpl.java
----------------------------------------------------------------------
diff --git a/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleEntityImpl.java b/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleEntityImpl.java
index 2595c39..16ecd68 100644
--- a/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleEntityImpl.java
+++ b/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleEntityImpl.java
@@ -20,7 +20,6 @@ package org.apache.brooklyn.test.osgi.entities;
 
 import org.apache.brooklyn.core.entity.AbstractEntity;
 
-
 public class SimpleEntityImpl extends AbstractEntity implements SimpleEntity {
 
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleObject.java
----------------------------------------------------------------------
diff --git a/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleObject.java b/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleObject.java
new file mode 100644
index 0000000..dad1ab2
--- /dev/null
+++ b/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimpleObject.java
@@ -0,0 +1,56 @@
+/*
+ * 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.test.osgi.entities;
+
+public class SimpleObject {
+    
+    private String val;
+    
+    public SimpleObject() {
+    }
+
+    public SimpleObject(String val) {
+        this.val = val;
+    }
+
+    public String getVal() {
+        return val;
+    }
+    
+    public void setVal(String val) {
+        this.val = val;
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof SimpleObject)) return false;
+        String oVal = ((SimpleObject)obj).val;
+        return (val == null) ? oVal == null : val.equals(oVal);
+    }
+    
+    @Override
+    public int hashCode() {
+        return (val == null) ? 0 : val.hashCode();
+    }
+    
+    @Override
+    public String toString() {
+        return "SimpleObject["+val+"]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimplePolicy.java
----------------------------------------------------------------------
diff --git a/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimplePolicy.java b/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimplePolicy.java
index fdea821..f6589c5 100644
--- a/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimplePolicy.java
+++ b/utils/common/dependencies/osgi/entities/src/main/java/org/apache/brooklyn/test/osgi/entities/SimplePolicy.java
@@ -18,7 +18,6 @@
  */
 package org.apache.brooklyn.test.osgi.entities;
 
-
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.policy.AbstractPolicy;
@@ -33,4 +32,9 @@ public class SimplePolicy extends AbstractPolicy {
 
     @SetFromFlag("config3")
     public static final ConfigKey<String> CONFIG3 = ConfigKeys.newStringConfigKey("config3");
+    
+    @Override
+    protected <T> void doReconfigureConfig(ConfigKey<T> key, T val) {
+        // no-op; allow any config to be set
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/utils/common/src/main/java/org/apache/brooklyn/util/osgi/VersionedName.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/osgi/VersionedName.java b/utils/common/src/main/java/org/apache/brooklyn/util/osgi/VersionedName.java
index df36a80..2d89be2 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/osgi/VersionedName.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/osgi/VersionedName.java
@@ -15,6 +15,10 @@
  */
 package org.apache.brooklyn.util.osgi;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.Nullable;
+
 import com.google.common.base.Objects;
 import org.apache.brooklyn.util.text.Strings;
 import org.osgi.framework.Bundle;
@@ -29,12 +33,11 @@ public class VersionedName {
     private final Version version;
 
     public VersionedName(Bundle b) {
-        this.symbolicName = b.getSymbolicName();
-        this.version = b.getVersion();
+        this(b.getSymbolicName(), b.getVersion());
     }
 
-    public VersionedName(String symbolicName, Version version) {
-        this.symbolicName = symbolicName;
+    public VersionedName(String symbolicName, @Nullable Version version) {
+        this.symbolicName = checkNotNull(symbolicName, "symbolicName");
         this.version = version;
     }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/a555beac/utils/common/src/test/java/org/apache/brooklyn/util/osgi/OsgiTestResources.java
----------------------------------------------------------------------
diff --git a/utils/common/src/test/java/org/apache/brooklyn/util/osgi/OsgiTestResources.java b/utils/common/src/test/java/org/apache/brooklyn/util/osgi/OsgiTestResources.java
index 21827a0..181849d 100644
--- a/utils/common/src/test/java/org/apache/brooklyn/util/osgi/OsgiTestResources.java
+++ b/utils/common/src/test/java/org/apache/brooklyn/util/osgi/OsgiTestResources.java
@@ -44,6 +44,9 @@ public class OsgiTestResources {
     public static final String BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_APPLICATION = "org.apache.brooklyn.test.osgi.entities.SimpleApplication";
     public static final String BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY = "org.apache.brooklyn.test.osgi.entities.SimpleEntity";
     public static final String BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_POLICY = "org.apache.brooklyn.test.osgi.entities.SimplePolicy";
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_OBJECT = "org.apache.brooklyn.test.osgi.entities.SimpleObject";
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY_CONFIG_NAME = "simple.config";
+    public static final String BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY_SENSOR_NAME = "simple.sensor";
 
     /**
      * brooklyn-test-osgi-more-entities_0.1.0 -


[3/8] brooklyn-server git commit: $brooklyn:object: handle osgi classes

Posted by sv...@apache.org.
$brooklyn:object: handle osgi classes

If can\u2019t load immediately, then defer until we know the entity thus
the catalog (and libraries).

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

Branch: refs/heads/master
Commit: 25605caee4a2b2313eb3dc31759967f65be67b87
Parents: a4930f0
Author: Aled Sage <al...@gmail.com>
Authored: Thu Jul 7 00:07:25 2016 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Sat Jul 9 10:58:36 2016 +0100

----------------------------------------------------------------------
 .../spi/dsl/methods/BrooklynDslCommon.java      | 63 ++++++++++++++------
 1 file changed, 45 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/25605cae/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
index d496fd7..23cc3a3 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
@@ -18,6 +18,8 @@
  */
 package org.apache.brooklyn.camp.brooklyn.spi.dsl.methods;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
@@ -39,7 +41,9 @@ import org.apache.brooklyn.camp.brooklyn.spi.dsl.BrooklynDslDeferredSupplier;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.DslUtils;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope;
 import org.apache.brooklyn.core.config.external.ExternalConfigSupplier;
+import org.apache.brooklyn.core.entity.AbstractEntity;
 import org.apache.brooklyn.core.entity.EntityDynamicType;
+import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.mgmt.internal.ExternalConfigSupplierRegistry;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.mgmt.persist.DeserializingClassRenamesProvider;
@@ -57,6 +61,8 @@ import org.apache.brooklyn.util.javalang.coerce.ClassCoercionException;
 import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
 import org.apache.brooklyn.util.text.Strings;
 import org.apache.commons.beanutils.BeanUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
@@ -66,6 +72,8 @@ import com.google.common.collect.Lists;
 /** static import functions which can be used in `$brooklyn:xxx` contexts */
 public class BrooklynDslCommon {
 
+    private static final Logger LOG = LoggerFactory.getLogger(BrooklynDslCommon.class);
+
     // Access specific entities
 
     public static DslComponent self() {
@@ -162,8 +170,9 @@ public class BrooklynDslCommon {
 
     /**
      * Return an instance of the specified class with its fields set according
-     * to the {@link Map} or a {@link BrooklynDslDeferredSupplier} if the arguments are not
-     * yet fully resolved.
+     * to the {@link Map}. Or a {@link BrooklynDslDeferredSupplier} if either the arguments are 
+     * not yet fully resolved, or the class cannot be loaded yet (e.g. needs the catalog's OSGi 
+     * bundles).
      */
     @SuppressWarnings("unchecked")
     public static Object object(Map<String, Object> arguments) {
@@ -171,22 +180,24 @@ public class BrooklynDslCommon {
         String typeName = BrooklynYamlTypeInstantiator.InstantiatorFromKey.extractTypeName("object", config).orNull();
         Map<String,Object> objectFields = (Map<String, Object>) config.getStringKeyMaybe("object.fields").or(MutableMap.of());
         Map<String,Object> brooklynConfig = (Map<String, Object>) config.getStringKeyMaybe(BrooklynCampReservedKeys.BROOKLYN_CONFIG).or(MutableMap.of());
+        
+        String mappedTypeName = DeserializingClassRenamesProvider.findMappedName(typeName);
+        Class<?> type;
         try {
-            // TODO Should use catalog's classloader, rather than ClassLoaderUtils; how to get that? Should we return a future?!
-            String mappedTypeName = DeserializingClassRenamesProvider.findMappedName(typeName);
-            Class<?> type = new ClassLoaderUtils(BrooklynDslCommon.class).loadClass(mappedTypeName);
-            
-            if (!Reflections.hasNoArgConstructor(type)) {
-                throw new IllegalStateException(String.format("Cannot construct %s bean: No public no-arg constructor available", type));
-            }
-            if ((objectFields.isEmpty() || DslUtils.resolved(objectFields.values())) &&
-                    (brooklynConfig.isEmpty() || DslUtils.resolved(brooklynConfig.values()))) {
-                return DslObject.create(type, objectFields, brooklynConfig);
-            } else {
-                return new DslObject(type, objectFields, brooklynConfig);
-            }
+            type = new ClassLoaderUtils(BrooklynDslCommon.class).loadClass(mappedTypeName);
         } catch (ClassNotFoundException e) {
-            throw Exceptions.propagate(e);
+            LOG.debug("Cannot load class " + typeName + " for DLS object; assuming it is in OSGi bundle; will defer its loading");
+            return new DslObject(mappedTypeName, objectFields, brooklynConfig);
+        }
+
+        if (!Reflections.hasNoArgConstructor(type)) {
+            throw new IllegalStateException(String.format("Cannot construct %s bean: No public no-arg constructor available", type));
+        }
+        if ((objectFields.isEmpty() || DslUtils.resolved(objectFields.values())) &&
+                (brooklynConfig.isEmpty() || DslUtils.resolved(brooklynConfig.values()))) {
+            return DslObject.create(type, objectFields, brooklynConfig);
+        } else {
+            return new DslObject(type, objectFields, brooklynConfig);
         }
     }
 
@@ -316,11 +327,18 @@ public class BrooklynDslCommon {
 
         private static final long serialVersionUID = 8878388748085419L;
 
+        private String typeName;
         private Class<?> type;
         private Map<String,Object> fields, config;
 
+        public DslObject(String typeName, Map<String,Object> fields,  Map<String,Object> config) {
+            this.typeName = checkNotNull(typeName, "typeName");
+            this.fields = MutableMap.copyOf(fields);
+            this.config = MutableMap.copyOf(config);
+        }
+        
         public DslObject(Class<?> type, Map<String,Object> fields,  Map<String,Object> config) {
-            this.type = type;
+            this.type = checkNotNull(type, "type");
             this.fields = MutableMap.copyOf(fields);
             this.config = MutableMap.copyOf(config);
         }
@@ -328,6 +346,14 @@ public class BrooklynDslCommon {
         @SuppressWarnings("unchecked")
         @Override
         public Task<Object> newTask() {
+            if (type == null) {
+                EntityInternal entity = entity();
+                try {
+                    type = new ClassLoaderUtils(BrooklynDslCommon.class, entity).loadClass(typeName);
+                } catch (ClassNotFoundException e) {
+                    throw Exceptions.propagate(e);
+                }
+            }
             List<TaskAdaptable<Object>> tasks = Lists.newLinkedList();
             for (Object value : Iterables.concat(fields.values(), config.values())) {
                 if (value instanceof TaskAdaptable) {
@@ -336,6 +362,7 @@ public class BrooklynDslCommon {
                     tasks.add(((TaskFactory<TaskAdaptable<Object>>) value).newTask());
                 }
             }
+            
             Map<String,?> flags = MutableMap.<String,String>of("displayName", "building '"+type+"' with "+tasks.size()+" task"+(tasks.size()!=1?"s":""));
             return DependentConfiguration.transformMultiple(flags, new Function<List<Object>, Object>() {
                         @Override
@@ -399,7 +426,7 @@ public class BrooklynDslCommon {
 
         @Override
         public String toString() {
-            return "$brooklyn:object(\""+type.getName()+"\")";
+            return "$brooklyn:object(\""+(type != null ? type.getName() : typeName)+"\")";
         }
     }
 


[7/8] brooklyn-server git commit: Update osgi test jars

Posted by sv...@apache.org.
Update osgi test jars


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

Branch: refs/heads/master
Commit: 3bb478830deaec57ba6714695dd4e3094ba21349
Parents: cde747f
Author: Aled Sage <al...@gmail.com>
Authored: Fri Jul 8 10:47:03 2016 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Sat Jul 9 11:03:18 2016 +0100

----------------------------------------------------------------------
 .../test/osgi/entities/more/MoreObject.java     |  56 +++++++++++++++++++
 .../test/osgi/entities/more/MoreObject.java     |  56 +++++++++++++++++++
 .../osgi/brooklyn-test-osgi-entities.jar        | Bin 17775 -> 19168 bytes
 .../brooklyn-test-osgi-more-entities_0.1.0.jar  | Bin 14964 -> 16004 bytes
 .../brooklyn-test-osgi-more-entities_0.2.0.jar  | Bin 15646 -> 16906 bytes
 5 files changed, 112 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3bb47883/utils/common/dependencies/osgi/more-entities-v1/src/main/java/org/apache/brooklyn/test/osgi/entities/more/MoreObject.java
----------------------------------------------------------------------
diff --git a/utils/common/dependencies/osgi/more-entities-v1/src/main/java/org/apache/brooklyn/test/osgi/entities/more/MoreObject.java b/utils/common/dependencies/osgi/more-entities-v1/src/main/java/org/apache/brooklyn/test/osgi/entities/more/MoreObject.java
new file mode 100644
index 0000000..1725f08
--- /dev/null
+++ b/utils/common/dependencies/osgi/more-entities-v1/src/main/java/org/apache/brooklyn/test/osgi/entities/more/MoreObject.java
@@ -0,0 +1,56 @@
+/*
+ * 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.test.osgi.entities.more;
+
+public class MoreObject {
+    
+    private String val;
+    
+    public MoreObject() {
+    }
+
+    public MoreObject(String val) {
+        this.val = val;
+    }
+
+    public String getVal() {
+        return val;
+    }
+    
+    public void setVal(String val) {
+        this.val = val;
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof MoreObject)) return false;
+        String oVal = ((MoreObject)obj).val;
+        return (val == null) ? oVal == null : val.equals(oVal);
+    }
+    
+    @Override
+    public int hashCode() {
+        return (val == null) ? 0 : val.hashCode();
+    }
+    
+    @Override
+    public String toString() {
+        return "MoreObject-v1["+val+"]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3bb47883/utils/common/dependencies/osgi/more-entities-v2/src/main/java/org/apache/brooklyn/test/osgi/entities/more/MoreObject.java
----------------------------------------------------------------------
diff --git a/utils/common/dependencies/osgi/more-entities-v2/src/main/java/org/apache/brooklyn/test/osgi/entities/more/MoreObject.java b/utils/common/dependencies/osgi/more-entities-v2/src/main/java/org/apache/brooklyn/test/osgi/entities/more/MoreObject.java
new file mode 100644
index 0000000..08bf69c
--- /dev/null
+++ b/utils/common/dependencies/osgi/more-entities-v2/src/main/java/org/apache/brooklyn/test/osgi/entities/more/MoreObject.java
@@ -0,0 +1,56 @@
+/*
+ * 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.test.osgi.entities.more;
+
+public class MoreObject {
+    
+    private String val;
+    
+    public MoreObject() {
+    }
+
+    public MoreObject(String val) {
+        this.val = val;
+    }
+
+    public String getVal() {
+        return val;
+    }
+    
+    public void setVal(String val) {
+        this.val = val;
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof MoreObject)) return false;
+        String oVal = ((MoreObject)obj).val;
+        return (val == null) ? oVal == null : val.equals(oVal);
+    }
+    
+    @Override
+    public int hashCode() {
+        return (val == null) ? 0 : val.hashCode();
+    }
+    
+    @Override
+    public String toString() {
+        return "MoreObject-v2["+val+"]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3bb47883/utils/common/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar
----------------------------------------------------------------------
diff --git a/utils/common/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar b/utils/common/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar
index 63edc5d..42ba7ca 100644
Binary files a/utils/common/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar and b/utils/common/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-entities.jar differ

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3bb47883/utils/common/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.1.0.jar
----------------------------------------------------------------------
diff --git a/utils/common/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.1.0.jar b/utils/common/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.1.0.jar
index 1101112..b30775e 100644
Binary files a/utils/common/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.1.0.jar and b/utils/common/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.1.0.jar differ

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3bb47883/utils/common/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.2.0.jar
----------------------------------------------------------------------
diff --git a/utils/common/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.2.0.jar b/utils/common/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.2.0.jar
index 4de9b94..0dc12b1 100644
Binary files a/utils/common/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.2.0.jar and b/utils/common/src/test/resources/brooklyn/osgi/brooklyn-test-osgi-more-entities_0.2.0.jar differ