You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2015/09/01 17:16:37 UTC

[14/28] incubator-brooklyn git commit: Builds on #872 to add renamed-class checks in a few other places classes are loaded.

Builds on #872 to add renamed-class checks in a few other places classes are loaded.

Also tidies some error messages and adds a cache for renamed classes.


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

Branch: refs/heads/master
Commit: 9fca8a279c372088c99c5e120db35dd9c74c538c
Parents: be3e182
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Aug 31 15:38:56 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Mon Aug 31 17:04:24 2015 +0100

----------------------------------------------------------------------
 .../catalog/internal/CatalogInitialization.java |  7 +--
 .../factory/BasicConfigurableEntityFactory.java |  3 +-
 .../JavaBrooklynClassLoadingContext.java        |  2 +
 .../OsgiBrooklynClassLoadingContext.java        |  1 -
 .../core/mgmt/entitlement/Entitlements.java     |  7 +--
 .../DeserializingClassRenamesProvider.java      | 31 +++++++------
 .../core/mgmt/persist/XmlMementoSerializer.java |  4 +-
 .../core/mgmt/rebind/RebindIteration.java       | 11 +++--
 .../brooklyn/core/plan/PlanToSpecFactory.java   |  3 +-
 .../util/core/xstream/ClassRenamingMapper.java  | 33 +++-----------
 .../util/core/xstream/XmlSerializer.java        | 38 ++++++++++++++--
 .../mgmt/persist/XmlMementoSerializerTest.java  |  5 ++-
 .../service/BrooklynServiceTypeResolver.java    | 24 +---------
 .../brooklyn/util/exceptions/Exceptions.java    | 47 +++++++++++++-------
 .../brooklyn/util/javalang/Reflections.java     | 41 +++++++++++++++++
 15 files changed, 157 insertions(+), 100 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9fca8a27/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogInitialization.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogInitialization.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogInitialization.java
index 80d29ad..907cbb8 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogInitialization.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/CatalogInitialization.java
@@ -434,14 +434,15 @@ public class CatalogInitialization implements ManagementContextInjectable {
         if (throwable instanceof RuntimeInterruptedException)
             throw (RuntimeInterruptedException) throwable;
 
-        log.error("Error loading catalog item '"+details+"': "+throwable);
-        log.debug("Trace for error loading catalog item '"+details+"': "+throwable, throwable);
+        String throwableText = Exceptions.collapseText(throwable);
+        log.error("Error loading catalog item '"+details+"': "+throwableText);
+        log.debug("Trace for error loading catalog item '"+details+"': "+throwableText, throwable);
 
         // TODO give more detail when adding
         ((ManagementContextInternal)getManagementContext()).errors().add(throwable);
         
         if (isStartingUp && failOnStartupErrors) {
-            throw new FatalRuntimeException("Unable to load catalog item '"+details+"': "+throwable, throwable);
+            throw new FatalRuntimeException("Unable to load catalog item '"+details+"': "+throwableText, throwable);
         }
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9fca8a27/core/src/main/java/org/apache/brooklyn/core/entity/factory/BasicConfigurableEntityFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/factory/BasicConfigurableEntityFactory.java b/core/src/main/java/org/apache/brooklyn/core/entity/factory/BasicConfigurableEntityFactory.java
index 3011c9a..8f6e3f6 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/factory/BasicConfigurableEntityFactory.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/factory/BasicConfigurableEntityFactory.java
@@ -28,6 +28,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.mgmt.persist.DeserializingClassRenamesProvider;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Throwables;
@@ -45,7 +46,7 @@ public class BasicConfigurableEntityFactory<T extends Entity> extends AbstractCo
     public BasicConfigurableEntityFactory(Map flags, Class<? extends T> clazz) {
         super(flags);
         this.clazz = checkNotNull(clazz, "clazz");
-        this.clazzName = clazz.getName();
+        this.clazzName = DeserializingClassRenamesProvider.findMappedName(clazz.getName());
     }
 
     public T newEntity2(Map flags, Entity parent) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9fca8a27/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/JavaBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/JavaBrooklynClassLoadingContext.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/JavaBrooklynClassLoadingContext.java
index 18f328f..51007d7 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/JavaBrooklynClassLoadingContext.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/JavaBrooklynClassLoadingContext.java
@@ -27,6 +27,7 @@ import java.util.Collections;
 import java.util.Enumeration;
 
 import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.mgmt.persist.DeserializingClassRenamesProvider;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 
@@ -79,6 +80,7 @@ public class JavaBrooklynClassLoadingContext extends AbstractBrooklynClassLoadin
     @SuppressWarnings({ "rawtypes", "unchecked" })
     public Maybe<Class<?>> tryLoadClass(String className) {
         try {
+            className = DeserializingClassRenamesProvider.findMappedName(className);
             return (Maybe) Maybe.of(getClassLoader().loadClass(className));
         } catch (Exception e) {
             Exceptions.propagateIfFatal(e);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9fca8a27/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/OsgiBrooklynClassLoadingContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/OsgiBrooklynClassLoadingContext.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/OsgiBrooklynClassLoadingContext.java
index fd1acb0..fa12aa2 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/OsgiBrooklynClassLoadingContext.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/classloading/OsgiBrooklynClassLoadingContext.java
@@ -25,7 +25,6 @@ import java.util.Collections;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
-import org.apache.brooklyn.api.mgmt.entitlement.EntitlementClass;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
 import org.apache.brooklyn.core.mgmt.ha.OsgiManager;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9fca8a27/core/src/main/java/org/apache/brooklyn/core/mgmt/entitlement/Entitlements.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/entitlement/Entitlements.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/entitlement/Entitlements.java
index d916c5c..6c281fc 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/entitlement/Entitlements.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/entitlement/Entitlements.java
@@ -31,10 +31,11 @@ import org.apache.brooklyn.api.mgmt.entitlement.EntitlementContext;
 import org.apache.brooklyn.api.mgmt.entitlement.EntitlementManager;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
-import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.config.Sanitizer;
 import org.apache.brooklyn.core.internal.BrooklynProperties;
 import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.mgmt.persist.DeserializingClassRenamesProvider;
 import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.javalang.Reflections;
@@ -278,7 +279,7 @@ public class Entitlements {
                 @Override
                 public boolean apply(EntityAndItem<String> input) {
                     if (input == null) return false;
-                    return !Entities.isSecret(input.getItem());
+                    return !Sanitizer.IS_SECRET_PREDICATE.apply(input.getItem());
                 }
                 @Override
                 public String toString() {
@@ -390,7 +391,7 @@ public class Entitlements {
             try {
                 ClassLoader cl = mgmt==null ? null : ((ManagementContextInternal)mgmt).getCatalogClassLoader();
                 if (cl==null) cl = Entitlements.class.getClassLoader();
-                Class<?> clazz = cl.loadClass(type);
+                Class<?> clazz = cl.loadClass(DeserializingClassRenamesProvider.findMappedName(type));
                 return (EntitlementManager) instantiate(clazz, ImmutableList.of(
                         new Object[] {mgmt, brooklynProperties},
                         new Object[] {mgmt},

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9fca8a27/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/DeserializingClassRenamesProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/DeserializingClassRenamesProvider.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/DeserializingClassRenamesProvider.java
index e8ac5a4..9fbac01 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/DeserializingClassRenamesProvider.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/DeserializingClassRenamesProvider.java
@@ -24,22 +24,34 @@ import java.util.Enumeration;
 import java.util.Map;
 import java.util.Properties;
 
+import org.apache.brooklyn.util.core.ResourceUtils;
 import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.javalang.Reflections;
 import org.apache.brooklyn.util.stream.Streams;
 
 import com.google.common.annotations.Beta;
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 
 @Beta
 public class DeserializingClassRenamesProvider {
 
-    public static final String DESERIALIZING_CLASS_RENAMES_PROPERTIES_PATH = "classpath://org/apache/brooklyn/deserializingClassRenames.properties";
+    public static final String DESERIALIZING_CLASS_RENAMES_PROPERTIES_PATH = "classpath://org/apache/brooklyn/core/mgmt/persist/deserializingClassRenames.properties";
+    
+    private static Map<String, String> cache = null;
     
     @Beta
     public static Map<String, String> loadDeserializingClassRenames() {
-        InputStream resource = XmlMementoSerializer.class.getClassLoader().getResourceAsStream(DESERIALIZING_CLASS_RENAMES_PROPERTIES_PATH);
+        if (cache!=null) return cache;
+        synchronized (DeserializingClassRenamesProvider.class) {
+            cache = loadDeserializingClassRenamesCache();
+            return cache;
+        }
+    }
+    
+    private synchronized static Map<String, String> loadDeserializingClassRenamesCache() {
+        if (cache!=null) return cache;
+        InputStream resource = new ResourceUtils(DeserializingClassRenamesProvider.class).getResourceFromUrl(DESERIALIZING_CLASS_RENAMES_PROPERTIES_PATH);
         if (resource != null) {
             try {
                 Properties props = new Properties();
@@ -63,16 +75,7 @@ public class DeserializingClassRenamesProvider {
     }
 
     @Beta
-    public static Optional<String> tryFindMappedName(Map<String, String> renames, String name) {
-        String mappedName = (String) renames.get(name);
-        if (mappedName != null) {
-            return Optional.of(mappedName);
-        }
-        for (Map.Entry<String, String> entry : renames.entrySet()) {
-            if (name.startsWith(entry.getKey())) {
-                return Optional.of(entry.getValue()+ name.substring(entry.getKey().length()));
-            }
-        }
-        return Optional.<String>absent();
+    public static String findMappedName(String name) {
+        return Reflections.findMappedNameAndLog(loadDeserializingClassRenames(), name);
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9fca8a27/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 a8a20b7..ec7fb6f 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
@@ -139,8 +139,8 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento
     
     // Warning: this is called in the super-class constuctor, so before this constructor!
     @Override
-    protected MapperWrapper wrapMapper(MapperWrapper next) {
-        MapperWrapper mapper = super.wrapMapper(next);
+    protected MapperWrapper wrapMapperForNormalUsage(Mapper next) {
+        MapperWrapper mapper = super.wrapMapperForNormalUsage(next);
         mapper = new CustomMapper(mapper, Entity.class, "entityProxy");
         mapper = new CustomMapper(mapper, Location.class, "locationProxy");
         return mapper;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9fca8a27/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 3f468ba..7b00ff8 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
@@ -72,12 +72,14 @@ import org.apache.brooklyn.core.feed.AbstractFeed;
 import org.apache.brooklyn.core.location.AbstractLocation;
 import org.apache.brooklyn.core.location.internal.LocationInternal;
 import org.apache.brooklyn.core.mgmt.classloading.BrooklynClassLoadingContext;
+import org.apache.brooklyn.core.mgmt.classloading.JavaBrooklynClassLoadingContext;
 import org.apache.brooklyn.core.mgmt.internal.BrooklynObjectManagementMode;
 import org.apache.brooklyn.core.mgmt.internal.BrooklynObjectManagerInternal;
 import org.apache.brooklyn.core.mgmt.internal.EntityManagerInternal;
 import org.apache.brooklyn.core.mgmt.internal.LocationManagerInternal;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.mgmt.internal.ManagementTransitionMode;
+import org.apache.brooklyn.core.mgmt.persist.DeserializingClassRenamesProvider;
 import org.apache.brooklyn.core.mgmt.persist.PersistenceActivityMetrics;
 import org.apache.brooklyn.core.mgmt.rebind.RebindManagerImpl.RebindTracker;
 import org.apache.brooklyn.core.objs.AbstractBrooklynObject;
@@ -211,7 +213,7 @@ public abstract class RebindIteration {
         
         managementContext = rebindManager.getManagementContext();
         rebindContext = new RebindContextImpl(managementContext, exceptionHandler, classLoader);
-        reflections = new Reflections(classLoader);
+        reflections = new Reflections(classLoader).applyClassRenames(DeserializingClassRenamesProvider.loadDeserializingClassRenames());
         instantiator = new BrooklynObjectInstantiator(classLoader, rebindContext, reflections);
         
         if (mode==ManagementNodeState.HOT_STANDBY || mode==ManagementNodeState.HOT_BACKUP) {
@@ -822,17 +824,14 @@ public abstract class RebindIteration {
 
             //As a last resort go through all catalog items trying to load the type and use the first that succeeds.
             //But first check if can be loaded from the default classpath
-            try {
-                cl.loadClass(entityManifest.getType());
+            if (JavaBrooklynClassLoadingContext.create(managementContext).tryLoadClass(entityManifest.getType()).isPresent())
                 return null;
-            } catch (ClassNotFoundException e) {
-            }
 
             for (CatalogItem<?, ?> item : catalog.getCatalogItems()) {
                 BrooklynClassLoadingContext loader = CatalogUtils.newClassLoadingContext(managementContext, item);
                 boolean canLoadClass = loader.tryLoadClass(entityManifest.getType()).isPresent();
                 if (canLoadClass) {
-                    LOG.warn("Missing catalog item for "+entityManifest.getId()+", inferring as "+item.getId()+" because that is able to load the item");
+                    LOG.warn("Missing catalog item for "+entityManifest.getId()+" ("+entityManifest.getType()+"), inferring as "+item.getId()+" because that is able to load the item");
                     return item.getId();
                 }
             }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9fca8a27/core/src/main/java/org/apache/brooklyn/core/plan/PlanToSpecFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/plan/PlanToSpecFactory.java b/core/src/main/java/org/apache/brooklyn/core/plan/PlanToSpecFactory.java
index 6f74855..a2e6500 100644
--- a/core/src/main/java/org/apache/brooklyn/core/plan/PlanToSpecFactory.java
+++ b/core/src/main/java/org/apache/brooklyn/core/plan/PlanToSpecFactory.java
@@ -101,7 +101,8 @@ public class PlanToSpecFactory {
                     (Strings.isNonBlank(e.getMessage()) ? " ("+e.getMessage()+")" : ""));
             } catch (Exception e) {
                 Exceptions.propagateIfFatal(e);
-                otherProblemsFromTransformers.add(new IllegalArgumentException("Transformer for "+t.getShortDescription()+" gave an error creating this plan", e));
+                otherProblemsFromTransformers.add(new IllegalArgumentException("Transformer for "+t.getShortDescription()+" gave an error creating this plan: "+
+                    Exceptions.collapseText(e), e));
             }
         }
         // failed

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9fca8a27/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 13fd032..aae4a6e 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,7 +22,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.util.Map;
 
-import org.apache.brooklyn.core.mgmt.persist.DeserializingClassRenamesProvider;
+import org.apache.brooklyn.util.javalang.Reflections;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -41,32 +41,13 @@ public class ClassRenamingMapper extends MapperWrapper {
     }
     
     @Override
-    public Class realClass(String elementName) {
-        String nameToUse;
-        Optional<String> mappedName = DeserializingClassRenamesProvider.tryFindMappedName(nameToType, elementName);
-        if (mappedName.isPresent()) {
-            LOG.debug("Transforming xstream "+elementName+" to "+mappedName);
-            nameToUse = mappedName.get();
-        } else {
-            nameToUse = elementName;
+    public Class<?> realClass(String elementName) {
+        Optional<String> elementNameOpt = Reflections.tryFindMappedName(nameToType, elementName);
+        if (elementNameOpt.isPresent()) {
+            LOG.debug("Mapping class '"+elementName+"' to '"+elementNameOpt.get()+"'");
+            elementName = elementNameOpt.get();
         }
-        return super.realClass(nameToUse);
+        return super.realClass(elementName);
     }
 
-//    public boolean aliasIsAttribute(String name) {
-//        return nameToType.containsKey(name);
-//    }
-//    
-//    private Object readResolve() {
-//        nameToType = new HashMap();
-//        for (final Iterator iter = classToName.keySet().iterator(); iter.hasNext();) {
-//            final Object type = iter.next();
-//            nameToType.put(classToName.get(type), type);
-//        }
-//        for (final Iterator iter = typeToName.keySet().iterator(); iter.hasNext();) {
-//            final Class type = (Class)iter.next();
-//            nameToType.put(typeToName.get(type), type.getName());
-//        }
-//        return this;
-//    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9fca8a27/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 9ed5b88..c83e25c 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
@@ -34,12 +34,16 @@ import org.apache.brooklyn.util.collections.MutableSet;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.converters.extended.JavaClassConverter;
+import com.thoughtworks.xstream.mapper.DefaultMapper;
+import com.thoughtworks.xstream.mapper.Mapper;
 import com.thoughtworks.xstream.mapper.MapperWrapper;
 
 public class XmlSerializer<T> {
 
     private final Map<String, String> deserializingClassRenames;
-    protected final XStream xstream;
+    // XXX protected
+    public final XStream xstream;
 
     public XmlSerializer() {
         this(ImmutableMap.<String, String>of());
@@ -50,11 +54,12 @@ public class XmlSerializer<T> {
         this.xstream = new XStream() {
             @Override
             protected MapperWrapper wrapMapper(MapperWrapper next) {
-                MapperWrapper result = super.wrapMapper(next);
-                return XmlSerializer.this.wrapMapper(result);
+                return XmlSerializer.this.wrapMapperForNormalUsage( super.wrapMapper(next) );
             }
         };
         
+        xstream.registerConverter(newCustomJavaClassConverter(), XStream.PRIORITY_NORMAL);
+        
         // list as array list is default
         xstream.alias("map", Map.class, LinkedHashMap.class);
         xstream.alias("set", Set.class, LinkedHashSet.class);
@@ -77,11 +82,36 @@ public class XmlSerializer<T> {
         xstream.registerConverter(new EnumCaseForgivingConverter());
         xstream.registerConverter(new Inet4AddressConverter());
     }
+
+    /**
+     * 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.
+     * 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,
+     * 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),
+     * after the instance added by XStream.setupConverters()
+     */
+    private JavaClassConverter newCustomJavaClassConverter() {
+        return new JavaClassConverter(wrapMapperForAllLowLevelMentions(new DefaultMapper(xstream.getClassLoaderReference()))) {};
+    }
     
-    protected MapperWrapper wrapMapper(MapperWrapper next) {
+    /** Adds mappers needed for *any* reference to a class, e.g. when names are used for inner classes, or classes are renamed;
+     * this *excludes* basic mentions, however, because most rewrites should *not* be applied at this deep level;
+     * mappers which effect aliases or intercept references to entities are usually NOT be invoked in this low-level pathway.
+     * See {@link #newCustomJavaClassConverter()}. */
+    protected MapperWrapper wrapMapperForAllLowLevelMentions(Mapper next) {
         MapperWrapper result = new CompilerIndependentOuterClassFieldMapper(next);
         return new ClassRenamingMapper(result, deserializingClassRenames);
     }
+    /** Extension point where sub-classes can add mappers wanted when instances of a class are serialized, 
+     * including {@link #wrapMapperForAllLowLevelMentions(Mapper)}, plus any usual domain mappings. */
+    protected MapperWrapper wrapMapperForNormalUsage(Mapper next) {
+        return wrapMapperForAllLowLevelMentions(next);
+    }
 
     public void serialize(Object object, Writer writer) {
         xstream.toXML(object, writer);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9fca8a27/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 0d94819..8f1a248 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
@@ -71,7 +71,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
-import com.google.common.net.HostAndPort;
+import com.thoughtworks.xstream.converters.Converter;
 
 public class XmlMementoSerializerTest {
 
@@ -263,7 +263,6 @@ public class XmlMementoSerializerTest {
         final TestApplication app = TestApplication.Factory.newManagedInstanceForTests();
         ManagementContext managementContext = app.getManagementContext();
         try {
-            @SuppressWarnings("deprecation")
             final Location loc = managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class));
             serializer.setLookupContext(new LookupContextImpl(managementContext,
                     ImmutableList.<Entity>of(), ImmutableList.of(loc), ImmutableList.<Policy>of(),
@@ -401,6 +400,8 @@ public class XmlMementoSerializerTest {
 
     @SuppressWarnings("unchecked")
     private <T> T assertSerializeAndDeserialize(T obj) throws Exception {
+Converter x = serializer.xstream.getConverterLookup().lookupConverterForType(Class.class);
+System.out.println("XXX: "+x);
         String serializedForm = serializer.toString(obj);
         LOG.info("serializedForm=" + serializedForm);
         Object deserialized = serializer.fromString(serializedForm);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9fca8a27/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/BrooklynServiceTypeResolver.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/BrooklynServiceTypeResolver.java b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/BrooklynServiceTypeResolver.java
index c6251df..f4bc9aa 100644
--- a/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/BrooklynServiceTypeResolver.java
+++ b/usage/camp/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/service/BrooklynServiceTypeResolver.java
@@ -48,14 +48,7 @@ public class BrooklynServiceTypeResolver implements ServiceTypeResolver {
 
     private static final Logger LOG = LoggerFactory.getLogger(ServiceTypeResolver.class);
     
-    private final Map<String, String> deserializingClassRenames;
-
     public BrooklynServiceTypeResolver() {
-        this(DeserializingClassRenamesProvider.loadDeserializingClassRenames());
-    }
-    
-    public BrooklynServiceTypeResolver(Map<String, String> deserializingClassRenames) {
-        this.deserializingClassRenames = checkNotNull(deserializingClassRenames, "deserializingClassRenames");
     }
     
     @Override
@@ -87,20 +80,7 @@ public class BrooklynServiceTypeResolver implements ServiceTypeResolver {
     }
 
     protected CatalogItem<Entity,EntitySpec<?>> getCatalogItemImpl(ManagementContext mgmt, String brooklynType) {
-        try {
-            return CatalogUtils.getCatalogItemOptionalVersion(mgmt, Entity.class,  brooklynType);
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            Optional<String> alternativeType = DeserializingClassRenamesProvider.tryFindMappedName(deserializingClassRenames, brooklynType);
-            if (alternativeType.isPresent()) {
-                LOG.debug("Transforming entity "+brooklynType+" to "+alternativeType.get());
-                try {
-                    return CatalogUtils.getCatalogItemOptionalVersion(mgmt, Entity.class,  alternativeType.get());
-                } catch (Exception e2) {
-                    LOG.debug("Problem getting catalog for transformed type "+alternativeType.get()+"; throwing of untransformed type "+brooklynType, e);
-                }
-            }
-            throw Exceptions.propagate(e);
-        }
+        brooklynType = DeserializingClassRenamesProvider.findMappedName(brooklynType);
+        return CatalogUtils.getCatalogItemOptionalVersion(mgmt, Entity.class,  brooklynType);
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9fca8a27/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java b/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java
index 583104b..eac137d 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java
@@ -27,16 +27,19 @@ import java.lang.reflect.UndeclaredThrowableException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 
 import javax.annotation.Nullable;
 
+import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.text.Strings;
 
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
@@ -137,17 +140,18 @@ public class Exceptions {
     /** as {@link #collapse(Throwable)} but includes causal messages in the message as per {@link #collapseTextIncludingAllCausalMessages(Throwable)};
      * use with care (limit once) as repeated usage can result in multiple copies of the same message */ 
     public static Throwable collapseIncludingAllCausalMessages(Throwable source) {
-        return collapse(source, true, true);
+        return collapse(source, true, true, ImmutableSet.<Throwable>of());
     }
     
     /** creates (but does not throw) a new {@link PropagatedRuntimeException} whose 
      * message is taken from the first _interesting_ element in the source,
      * and optionally also the causal chain */
     public static Throwable collapse(Throwable source, boolean collapseCausalChain) {
-        return collapse(source, collapseCausalChain, false);
+        return collapse(source, collapseCausalChain, false, ImmutableSet.<Throwable>of());
     }
     
-    private static Throwable collapse(Throwable source, boolean collapseCausalChain, boolean includeAllCausalMessages) {
+    private static Throwable collapse(Throwable source, boolean collapseCausalChain, boolean includeAllCausalMessages, Set<Throwable> visited) {
+        visited = MutableSet.copyOf(visited);
         String message = "";
         Throwable collapsed = source;
         int collapseCount = 0;
@@ -156,9 +160,14 @@ public class Exceptions {
         while (isBoring(collapsed)  && !messageIsFinal) {
             collapseCount++;
             Throwable cause = collapsed.getCause();
-            if (cause==null)
+            if (cause==null) {
                 // everything in the tree is boring...
                 return source;
+            }
+            if (visited.add(collapsed)) {
+                // there is a recursive loop
+                break;
+            }
             String collapsedS = collapsed.getMessage();
             if (collapsed instanceof PropagatedRuntimeException && ((PropagatedRuntimeException)collapsed).isCauseEmbeddedInMessage()) {
                 message = collapsed.getMessage();
@@ -180,7 +189,7 @@ public class Exceptions {
                 messagesCause = messagesCause.getCause();
                 break;
             }
-            messagesCause = messagesCause.getCause();
+            visited.add(messagesCause); messagesCause = messagesCause.getCause();
         }
         
         if (collapseCount==0 && !includeAllCausalMessages)
@@ -191,15 +200,12 @@ public class Exceptions {
             messagesCause = messagesCause.getCause();
         }
         
-        if (Strings.isBlank(message)) {
-            return new PropagatedRuntimeException(collapseCausalChain ? collapsed : source);
-        } else {
-            if (messagesCause!=null && !messageIsFinal) {
-                String extraMessage = collapseText(messagesCause, includeAllCausalMessages);
-                message = appendSeparator(message, extraMessage);
-            }
-            return new PropagatedRuntimeException(message, collapseCausalChain ? collapsed : source, true);
+        if (messagesCause!=null && !messageIsFinal) {
+            String extraMessage = collapseText(messagesCause, includeAllCausalMessages, ImmutableSet.copyOf(visited));
+            message = appendSeparator(message, extraMessage);
         }
+        if (message==null) message = "";
+        return new PropagatedRuntimeException(message, collapseCausalChain ? collapsed : source, true);
     }
     
     static String appendSeparator(String message, String next) {
@@ -233,14 +239,25 @@ public class Exceptions {
     }
     
     private static String collapseText(Throwable t, boolean includeAllCausalMessages) {
+        return collapseText(t, includeAllCausalMessages, ImmutableSet.<Throwable>of());
+    }
+    private static String collapseText(Throwable t, boolean includeAllCausalMessages, Set<Throwable> visited) {
         if (t == null) return null;
-        Throwable t2 = collapse(t, true, includeAllCausalMessages);
+        if (visited.contains(t)) {
+            // IllegalStateException sometimes refers to itself; guard against stack overflows
+            if (Strings.isNonBlank(t.getMessage())) return t.getMessage();
+            else return "("+t.getClass().getName()+", recursive cause)";
+        }
+        Throwable t2 = collapse(t, true, includeAllCausalMessages, visited);
+        visited = MutableSet.copyOf(visited);
+        visited.add(t);
+        visited.add(t2);
         if (t2 instanceof PropagatedRuntimeException) {
             if (((PropagatedRuntimeException)t2).isCauseEmbeddedInMessage())
                 // normally
                 return t2.getMessage();
             else if (t2.getCause()!=null)
-                return ""+t2.getCause();
+                return collapseText(t2.getCause(), includeAllCausalMessages, ImmutableSet.copyOf(visited));
             return ""+t2.getClass();
         }
         String result = t2.toString();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9fca8a27/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java
index decc1f0..c622ffc 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/Reflections.java
@@ -38,12 +38,14 @@ import java.util.Comparator;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.Stack;
 
 import javax.annotation.Nullable;
 
 import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -83,10 +85,17 @@ public class Reflections {
     }
 
     private final ClassLoader classLoader;
+    private final Map<String, String> classRenameMap = MutableMap.of();
     
     public Reflections(ClassLoader classLoader) {
         this.classLoader = checkNotNull(classLoader);
     }
+    
+    /** supply a map of known renames, of the form "old-class -> new-class" */ 
+    public Reflections applyClassRenames(Map<String,String> newClassRenames) {
+        this.classRenameMap.putAll(newClassRenames);
+        return this;
+    }
 
     public Object loadInstance(String classname, Object...argValues) throws ReflectionNotFoundException, ReflectionAccessException {
         Class<?> clazz = loadClass(classname);
@@ -119,6 +128,7 @@ public class Reflections {
     /** instantiates the given class from its binary name */
     public Class<?> loadClass(String classname) throws ReflectionNotFoundException {
         try {
+            classname = findMappedNameAndLog(classRenameMap, classname);
             return classLoader.loadClass(classname);
         } catch (ClassNotFoundException e) {
             throw new ReflectionNotFoundException("Failed to load class '" + classname + "' using class loader " + classLoader + ": " + Exceptions.collapseText(e), e);
@@ -785,4 +795,35 @@ public class Reflections {
         if (clazz.getDeclaredFields().length>0) return false;
         return hasNoNonObjectFields(clazz.getSuperclass());
     }
+
+    /** Takes a map of old-class-names to renames classes, and returns the mapped name if matched, or absent */
+    public static Optional<String> tryFindMappedName(Map<String, String> renames, String name) {
+        if (renames==null) return Optional.absent();
+        
+        String mappedName = renames.get(name);
+        if (mappedName != null) {
+            return Optional.of(mappedName);
+        }
+        
+        // look for inner classes by mapping outer class
+        if (name.contains("$")) {
+            String outerClassName = name.substring(0, name.indexOf('$'));
+            mappedName = renames.get(outerClassName);
+            if (mappedName != null) {
+                return Optional.of(mappedName + name.substring(name.indexOf('$')));
+            }
+        }
+        
+        return Optional.absent();
+    }
+
+    public static String findMappedNameAndLog(Map<String, String> renames, String name) {
+        Optional<String> rename = Reflections.tryFindMappedName(renames, name);
+        if (rename.isPresent()) {
+            LOG.debug("Mapping class '"+name+"' to '"+rename.get()+"'");
+            return rename.get();
+        }
+        return name;
+    }
+    
 }