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 <[email protected]> Authored: Mon Aug 31 15:38:56 2015 +0100 Committer: Alex Heneveld <[email protected]> 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; + } + }
