test the osgi-prefixing for classes, and improve javadoc

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

Branch: refs/heads/master
Commit: 9d448f3204ee26b7f716f5e339fe07d814fb37c3
Parents: f956627
Author: Alex Heneveld <alex.henev...@cloudsoftcorp.com>
Authored: Wed Jun 7 14:36:39 2017 +0100
Committer: Alex Heneveld <alex.henev...@cloudsoftcorp.com>
Committed: Wed Jun 7 14:43:25 2017 +0100

----------------------------------------------------------------------
 ...rFromStackOfBrooklynClassLoadingContext.java |   5 +
 .../core/mgmt/persist/XmlMementoSerializer.java |  14 ++-
 .../util/core/xstream/ClassRenamingMapper.java  |  22 ++--
 .../util/core/xstream/OsgiClassnameMapper.java  |  14 +++
 .../util/core/xstream/XmlSerializer.java        |  28 ++++-
 .../core/mgmt/osgi/OsgiStandaloneTest.java      |  21 ----
 .../brooklyn/util/core/osgi/OsgiTestBase.java   |  23 ++++
 .../core/xstream/XmlSerializerOsgiTest.java     | 111 +++++++++++++++++++
 .../brooklyn/util/osgi/OsgiTestResources.java   |   3 +-
 9 files changed, 200 insertions(+), 41 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9d448f32/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/ClassLoaderFromStackOfBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/ClassLoaderFromStackOfBrooklynClassLoadingContext.java
 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/ClassLoaderFromStackOfBrooklynClassLoadingContext.java
index f42ecd2..102a27c 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/ClassLoaderFromStackOfBrooklynClassLoadingContext.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/ClassLoaderFromStackOfBrooklynClassLoadingContext.java
@@ -62,6 +62,11 @@ public class 
ClassLoaderFromStackOfBrooklynClassLoadingContext extends ClassLoad
     protected Class<?> findClass(String name) throws ClassNotFoundException {
         return currentLoader.get().loadClass(name);
     }
+    
+    @Override
+    protected Class<?> loadClass(String name, boolean resolve) throws 
ClassNotFoundException {
+        return findClass(name);
+    }
 
     /** Must be accompanied by a corresponding {@link 
#popClassLoadingContext()} when finished. */
     @SuppressWarnings("deprecation")

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9d448f32/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 e83b5ea..9265a1f 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
@@ -90,9 +90,8 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> 
implements Memento
     }
     
     public XmlMementoSerializer(ClassLoader classLoader, Map<String, String> 
deserializingClassRenames) {
-        super(deserializingClassRenames);
-        this.delegatingClassLoader = new 
ClassLoaderFromStackOfBrooklynClassLoadingContext(classLoader);
-        xstream.setClassLoader(this.delegatingClassLoader);
+        super(new 
ClassLoaderFromStackOfBrooklynClassLoadingContext(classLoader), 
deserializingClassRenames);
+        this.delegatingClassLoader = 
(ClassLoaderFromStackOfBrooklynClassLoadingContext) xstream.getClassLoader();
         
         xstream.alias("entity", BasicEntityMemento.class);
         xstream.alias("location", BasicLocationMemento.class);
@@ -128,6 +127,11 @@ public class XmlMementoSerializer<T> extends 
XmlSerializer<T> implements Memento
     
         //For compatibility with existing persistence stores content.
         xstream.aliasField("registeredTypeName", 
BasicCatalogItemMemento.class, "symbolicName");
+        configureXstreamWithDeprecatedItems();
+    }
+
+    @SuppressWarnings("deprecation")
+    private void configureXstreamWithDeprecatedItems() {
         xstream.registerLocalConverter(BasicCatalogItemMemento.class, 
"libraries", new CatalogItemLibrariesConverter());
     }
     
@@ -353,9 +357,9 @@ public class XmlMementoSerializer<T> extends 
XmlSerializer<T> implements Memento
     // Perhaps context.getRequiredType(); can be used instead?
     // Other users of xstream (e.g. jenkinsci) manually check for resoved-to 
and class attributes
     //   for compatibility with older versions of xstream
-    private static Class readClassType(HierarchicalStreamReader reader, Mapper 
mapper) {
+    private static Class<?> readClassType(HierarchicalStreamReader reader, 
Mapper mapper) {
         String classAttribute = readClassAttribute(reader, mapper);
-        Class type;
+        Class<?> type;
         if (classAttribute == null) {
             type = mapper.realClass(reader.getNodeName());
         } else {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9d448f32/core/src/main/java/org/apache/brooklyn/util/core/xstream/ClassRenamingMapper.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/util/core/xstream/ClassRenamingMapper.java
 
b/core/src/main/java/org/apache/brooklyn/util/core/xstream/ClassRenamingMapper.java
index a2fa7ae..5691198 100644
--- 
a/core/src/main/java/org/apache/brooklyn/util/core/xstream/ClassRenamingMapper.java
+++ 
b/core/src/main/java/org/apache/brooklyn/util/core/xstream/ClassRenamingMapper.java
@@ -22,6 +22,7 @@ import static 
com.google.common.base.Preconditions.checkNotNull;
 
 import java.util.Map;
 
+import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.javalang.Reflections;
 import org.slf4j.Logger;
@@ -79,11 +80,6 @@ public class ClassRenamingMapper extends MapperWrapper {
      *  3. As mentioned under the use-cases, the rename could include the full 
bundle name prefix, 
      *     or it might just be the classname. We want to handle both, so need 
to implement yet
      *     more fallback behaviour.
-     * 
-     * ---
-     * TODO Wanted to pass xstream, rather than Supplier<ClassLoader>, in 
constructor. However, 
-     * this caused NPE because of how this is constructed from inside 
-     * XmlMementoSerializer.wrapMapperForNormalUsage, called from within an 
anonymous subclass of XStream!
      */
     
     public static final Logger LOG = 
LoggerFactory.getLogger(ClassRenamingMapper.class);
@@ -94,6 +90,12 @@ public class ClassRenamingMapper extends MapperWrapper {
     public ClassRenamingMapper(Mapper wrapped, Map<String, String> nameToType, 
Supplier<? extends ClassLoader> classLoaderSupplier) {
         super(wrapped);
         this.nameToType = checkNotNull(nameToType, "nameToType");
+        /*
+         * NB: wanted to pass xstream, rather than Supplier<ClassLoader>, in 
constructor. However, 
+         * this caused NPE because of how this is constructed from inside 
+         * XmlMementoSerializer.wrapMapperForNormalUsage, called from within 
an anonymous subclass of XStream!
+         * (Similar as for OsgiClassnameMapper.)
+         */
         this.classLoaderSupplier = checkNotNull(classLoaderSupplier, 
"classLoaderSupplier");
     }
     
@@ -106,10 +108,14 @@ public class ClassRenamingMapper extends MapperWrapper {
             elementName = elementNameOpt.get();
         }
 
-        CannotResolveClassException tothrow;
+        Exception tothrow;
         try {
             return super.realClass(elementName);
-        } catch (CannotResolveClassException e) {
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            // CannotResolveClassException is what should be thrown, but
+            // sneakily you can get other, e.g. 
+            // IAE if you have a ":" in the name and a URLClassLoader tries to 
read it
             LOG.trace("Failed to load class using super.realClass({}), for 
orig class {}, attempting fallbacks: {}", new Object[] {elementName, 
elementNamOrig, e});
             tothrow = e;
         }
@@ -157,7 +163,7 @@ public class ClassRenamingMapper extends MapperWrapper {
             }
         }
         
-        throw tothrow;
+        throw Exceptions.propagate(tothrow);
     }
     
     private boolean hasBundlePrefix(String type) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9d448f32/core/src/main/java/org/apache/brooklyn/util/core/xstream/OsgiClassnameMapper.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/util/core/xstream/OsgiClassnameMapper.java
 
b/core/src/main/java/org/apache/brooklyn/util/core/xstream/OsgiClassnameMapper.java
index 7bdf9f7..f82e743 100644
--- 
a/core/src/main/java/org/apache/brooklyn/util/core/xstream/OsgiClassnameMapper.java
+++ 
b/core/src/main/java/org/apache/brooklyn/util/core/xstream/OsgiClassnameMapper.java
@@ -24,6 +24,20 @@ import com.thoughtworks.xstream.XStream;
 import com.thoughtworks.xstream.mapper.CannotResolveClassException;
 import com.thoughtworks.xstream.mapper.MapperWrapper;
 
+/** Attaches a prefix to _all_ classes written out.
+ * Ensures the xstream class loader is used to read 
+ * (because that's where we set the OSGi-prefix-aware code).
+ * <p>
+ * We also have the context in that loader so if we wanted to optimize
+ * we could scan that for bundles and suppress bundles if it's in scope.
+ * However if we plan to move to referring to RegisteredTypes for anything
+ * serialized that's irrelevant.
+ * <p>
+ * We could have code that uses the search path from that loader
+ * to prefers types in local bundles, ignoring the bundle name
+ * if the class is found there (either always, or just if the bundle is not 
found / deprecated).
+ */
+// TODO above, and also see discussion at 
https://github.com/apache/brooklyn-server/pull/718
 public class OsgiClassnameMapper extends MapperWrapper {
     private final OsgiClassPrefixer prefixer;
     private final Supplier<XStream> xstream;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9d448f32/core/src/main/java/org/apache/brooklyn/util/core/xstream/XmlSerializer.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/util/core/xstream/XmlSerializer.java 
b/core/src/main/java/org/apache/brooklyn/util/core/xstream/XmlSerializer.java
index 26b6788..c0349b6 100644
--- 
a/core/src/main/java/org/apache/brooklyn/util/core/xstream/XmlSerializer.java
+++ 
b/core/src/main/java/org/apache/brooklyn/util/core/xstream/XmlSerializer.java
@@ -50,13 +50,20 @@ public class XmlSerializer<T> {
     }
     
     public XmlSerializer(Map<String, String> deserializingClassRenames) {
+        this(null, deserializingClassRenames);
+    }
+    
+    public XmlSerializer(ClassLoader loader, Map<String, String> 
deserializingClassRenames) {
         this.deserializingClassRenames = deserializingClassRenames;
-        this.xstream = new XStream() {
+        xstream = new XStream() {
             @Override
             protected MapperWrapper wrapMapper(MapperWrapper next) {
                 return XmlSerializer.this.wrapMapperForNormalUsage( 
super.wrapMapper(next) );
             }
         };
+        if (loader!=null) {
+            xstream.setClassLoader(loader);
+        }
         
         xstream.registerConverter(newCustomJavaClassConverter(), 
XStream.PRIORITY_NORMAL);
         
@@ -89,15 +96,18 @@ public class XmlSerializer<T> {
     }
 
     /**
-     * JCC is used when class names are serialized/deserialized and no alias 
is defined;
-     * it is configured in XStream *without* access to the XStream mapper.
+     * JCC is used when Class instances are serialized/deserialized as a value 
+     * (not as tags) and there are no aliases configured for that type.
+     * It is configured in XStream default *without* access to the XStream 
mapper,
+     * which is meant to apply when serializing the type name for instances of 
that type.
+     * <p>
      * However we need a few selected mappers (see {@link 
#wrapMapperForAllLowLevelMentions(Mapper)} )
-     * in order to effect renames at the low level, but many of the mappers 
must NOT be used,
+     * to apply to all class renames, but many of the mappers must NOT be used,
      * e.g. because some might intercept all Class<? extends Entity> references
      * (and that interception is only wanted when serializing <i>instances</i>,
      * as in {@link #wrapMapperForNormalUsage(Mapper)}).
      * <p>
-     * This can typically be done simply by registering our own instance (due 
to order guarantee of PrioritizedList),
+     * This can typically be done simply by registering our own instance of 
this (due to order guarantee of PrioritizedList),
      * after the instance added by XStream.setupConverters()
      */
     private JavaClassConverter newCustomJavaClassConverter() {
@@ -105,7 +115,7 @@ public class XmlSerializer<T> {
     }
     
     /** Adds mappers needed for *any* reference to a class, both "normal" 
usage (when xstream wants a mapper)
-     * and class conversion (when xstream needs to make a class name and 
doesn't have an alias).
+     * and Class conversion (when xstream needs to serialize an instance of 
Class and doesn't have an alias).
      * <p>
      * This should apply when nice names are used for inner classes, or 
classes are renamed;
      * however mappers which affect aliases or intercept references to 
entities are usually 
@@ -114,6 +124,7 @@ public class XmlSerializer<T> {
     // so very few fields are populated
     protected MapperWrapper wrapMapperForAllLowLevelMentions(Mapper next) {
         MapperWrapper result = new 
CompilerIndependentOuterClassFieldMapper(next);
+        
         Supplier<ClassLoader> classLoaderSupplier = new 
Supplier<ClassLoader>() {
             @Override public ClassLoader get() {
                 return xstream.getClassLoaderReference().getReference();
@@ -122,6 +133,11 @@ public class XmlSerializer<T> {
         result = new ClassRenamingMapper(result, deserializingClassRenames, 
classLoaderSupplier);
         result = new OsgiClassnameMapper(new Supplier<XStream>() {
             @Override public XStream get() { return xstream; } }, result);
+        // TODO as noted in ClassRenamingMapper that class can be simplified 
if 
+        // we swap the order of the above calls, because it _will_ be able to 
rely on
+        // OsgiClassnameMapper to attempt to load with the xstream reference 
stack
+        // (not doing it just now because close to a release)
+        
         return result;
     }
     /** Extension point where sub-classes can add mappers wanted when 
instances of a class are serialized, 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9d448f32/core/src/test/java/org/apache/brooklyn/core/mgmt/osgi/OsgiStandaloneTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/core/mgmt/osgi/OsgiStandaloneTest.java 
b/core/src/test/java/org/apache/brooklyn/core/mgmt/osgi/OsgiStandaloneTest.java
index b21ba28..3bb5358 100644
--- 
a/core/src/test/java/org/apache/brooklyn/core/mgmt/osgi/OsgiStandaloneTest.java
+++ 
b/core/src/test/java/org/apache/brooklyn/core/mgmt/osgi/OsgiStandaloneTest.java
@@ -45,9 +45,6 @@ public class OsgiStandaloneTest extends OsgiTestBase {
 
     private static final Logger log = 
LoggerFactory.getLogger(OsgiStandaloneTest.class);
 
-    public static final String BROOKLYN_OSGI_TEST_A_0_1_0_PATH = 
OsgiTestResources.BROOKLYN_OSGI_TEST_A_0_1_0_PATH;
-    public static final String BROOKLYN_OSGI_TEST_A_0_1_0_URL = 
"classpath:"+BROOKLYN_OSGI_TEST_A_0_1_0_PATH;
-
     public static final String BROOKLYN_TEST_OSGI_ENTITIES_PATH = 
OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_PATH;
     public static final String BROOKLYN_TEST_OSGI_ENTITIES_SYMBOLIC_NAME_FULL 
= OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SYMBOLIC_NAME_FULL;
     public static final String BROOKLYN_TEST_OSGI_MORE_ENTITIES_0_1_0_PATH = 
OsgiTestResources.BROOKLYN_TEST_OSGI_MORE_ENTITIES_0_1_0_PATH;
@@ -56,24 +53,6 @@ public class OsgiStandaloneTest extends OsgiTestBase {
     public static final String BROOKLYN_TEST_OSGI_ENTITIES_NAME = 
"org.apache.brooklyn.test.resources.osgi.brooklyn-test-osgi-entities";
     public static final String BROOKLYN_TEST_OSGI_ENTITIES_VERSION = "0.1.0";
 
-
-    protected Bundle install(String url) throws BundleException {
-        try {
-            return Osgis.install(framework, url);
-        } catch (Exception e) {
-            throw new IllegalStateException("test resources not available; may 
be an IDE issue, so try a mvn rebuild of this project", e);
-        }
-    }
-
-    protected Bundle installFromClasspath(String resourceName) throws 
BundleException {
-        
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), 
resourceName);
-        try {
-            return Osgis.install(framework, String.format("classpath:%s", 
resourceName));
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
     @Test
     public void testInstallBundle() throws Exception {
         Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9d448f32/core/src/test/java/org/apache/brooklyn/util/core/osgi/OsgiTestBase.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/util/core/osgi/OsgiTestBase.java 
b/core/src/test/java/org/apache/brooklyn/util/core/osgi/OsgiTestBase.java
index 0e6d751..4bc1e58 100644
--- a/core/src/test/java/org/apache/brooklyn/util/core/osgi/OsgiTestBase.java
+++ b/core/src/test/java/org/apache/brooklyn/util/core/osgi/OsgiTestBase.java
@@ -20,9 +20,12 @@ import java.io.IOException;
 
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.test.support.TestResourceUnavailableException;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.os.Os;
+import org.apache.brooklyn.util.osgi.OsgiTestResources;
 import org.apache.commons.io.FileUtils;
+import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.launch.Framework;
 import org.testng.annotations.AfterMethod;
@@ -34,6 +37,26 @@ import org.testng.annotations.BeforeMethod;
  */
 public class OsgiTestBase {
 
+    public static final String BROOKLYN_OSGI_TEST_A_0_1_0_PATH = 
OsgiTestResources.BROOKLYN_OSGI_TEST_A_0_1_0_PATH;
+    public static final String BROOKLYN_OSGI_TEST_A_0_1_0_URL = 
"classpath:"+BROOKLYN_OSGI_TEST_A_0_1_0_PATH;
+
+    protected Bundle install(String url) throws BundleException {
+        try {
+            return Osgis.install(framework, url);
+        } catch (Exception e) {
+            throw new IllegalStateException("test resources not available; may 
be an IDE issue, so try a mvn rebuild of this project", e);
+        }
+    }
+
+    protected Bundle installFromClasspath(String resourceName) throws 
BundleException {
+        
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), 
resourceName);
+        try {
+            return Osgis.install(framework, String.format("classpath:%s", 
resourceName));
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
     protected Framework framework = null;
     private File storageTempDir;
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9d448f32/core/src/test/java/org/apache/brooklyn/util/core/xstream/XmlSerializerOsgiTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/util/core/xstream/XmlSerializerOsgiTest.java
 
b/core/src/test/java/org/apache/brooklyn/util/core/xstream/XmlSerializerOsgiTest.java
new file mode 100644
index 0000000..5db2a5f
--- /dev/null
+++ 
b/core/src/test/java/org/apache/brooklyn/util/core/xstream/XmlSerializerOsgiTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.util.core.xstream;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.List;
+
+import 
org.apache.brooklyn.core.mgmt.classloading.ClassLoaderFromStackOfBrooklynClassLoadingContext;
+import org.apache.brooklyn.util.core.osgi.OsgiTestBase;
+import org.apache.brooklyn.util.core.osgi.Osgis;
+import org.apache.brooklyn.util.osgi.OsgiTestResources;
+import org.apache.brooklyn.util.text.Strings;
+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.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+public class XmlSerializerOsgiTest extends OsgiTestBase {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(XmlSerializerOsgiTest.class);
+
+    protected XmlSerializer<Object> serializer;
+    
+    /** Simple osgi-prefix-aware class loader. In the real world this function 
is done by OsgiManager. */
+    public static class OsgiPrefixStrippingClassLoader extends ClassLoader {
+        private final Framework framework;
+
+        public OsgiPrefixStrippingClassLoader(Framework framework, ClassLoader 
parent) { 
+            super(parent);
+            this.framework = framework;
+        }
+        
+        @Override
+        protected Class<?> findClass(String name) throws 
ClassNotFoundException {
+            int separator = name.indexOf(":");
+            if (separator>=0) {
+                Bundle bundle = 
Osgis.bundleFinder(framework).symbolicName(name.substring(0, 
separator)).find().get();
+                return bundle.loadClass(name.substring(separator+1));
+            } else {
+                return super.findClass(name);
+            }
+        }
+        
+        @Override
+        protected Class<?> loadClass(String name, boolean resolve) throws 
ClassNotFoundException {
+            int separator = name.indexOf(":");
+            if (separator>=0) {
+                Bundle bundle = 
Osgis.bundleFinder(framework).symbolicName(name.substring(0, 
separator)).find().get();
+                return bundle.loadClass(name.substring(separator+1));
+            } else {
+                return super.loadClass(name, resolve);
+            }
+        }
+    }
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        super.setUp();
+        serializer = new XmlSerializer<Object>(
+            new OsgiPrefixStrippingClassLoader(framework, 
getClass().getClassLoader()), 
+            ImmutableMap.<String, String>of());
+    }
+    
+    @Test
+    public void testPrefixesOnClassAndInstance() throws Exception {
+        Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
+        
+        final String TYPE = "brooklyn.test.osgi.TestA";
+        final String FQTN = 
OsgiTestResources.BROOKLYN_OSGI_TEST_A_SYMBOLIC_NAME + ":" + TYPE;
+        
+        Class<?> aClass = bundle.loadClass(TYPE);
+        String rc = assertSerializeEqualsAndCanDeserialize(aClass, 
"<java-class>"+FQTN+"</java-class>");
+        
+        Object aInst = aClass.newInstance();
+        String ri = assertSerializeEqualsAndCanDeserialize(aInst, 
"<"+FQTN+"/>");
+        
+        List<Object> l = Arrays.asList(aClass, aInst);
+        assertSerializeEqualsAndCanDeserialize(l, 
"<java.util.Arrays_-ArrayList><a>"+rc+ri+"</a></java.util.Arrays_-ArrayList>");
+    }
+
+    protected String assertSerializeEqualsAndCanDeserialize(Object val, String 
expected) throws Exception {
+        String xml = serializer.toString(val);
+        assertEquals(Strings.replaceAllRegex(xml, "\\s+", ""), 
Strings.replaceAllRegex(expected, "\\s+", ""));
+        Object result = serializer.fromString(xml);
+        LOG.debug("val="+val+"'; xml="+xml+"; result="+result);
+        assertEquals(result.getClass(), val.getClass());
+        return expected;
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/9d448f32/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 f9f1998..048e0f5 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
@@ -33,7 +33,8 @@ public interface OsgiTestResources {
      * defines TestA which has a "times" method and a static multiplier field;
      * we set the multiplier to determine when we are sharing versions and 
when not
      */
-    public static final String BROOKLYN_OSGI_TEST_A_0_1_0_PATH = 
"/brooklyn/osgi/brooklyn-osgi-test-a_0.1.0.jar";
+    public static final String BROOKLYN_OSGI_TEST_A_SYMBOLIC_NAME = 
"brooklyn-osgi-test-a";
+    public static final String BROOKLYN_OSGI_TEST_A_0_1_0_PATH = 
"/brooklyn/osgi/" + BROOKLYN_OSGI_TEST_A_SYMBOLIC_NAME + "_0.1.0.jar";
 
     /**
      * brooklyn-test-osgi-entities (v 0.1.0) -

Reply via email to