XML serialisation: avoid error if try to serialize entity - If entity/location/policy sneaks through to be serialized (e.g. through config key of LocationConfigKeys.CALLER_CONTEXT pointing at Entity)... - Don't blow up: instead let it deserialize as null, which is better than failing to serialize+deserialize everything about the entity. - Logs at WARN for first time encountered, and debug subsequently.
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/c7ff5922 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/c7ff5922 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/c7ff5922 Branch: refs/heads/0.6.0 Commit: c7ff592232e1e3f6812e946304da03e03b017d83 Parents: 224fca6 Author: Aled Sage <[email protected]> Authored: Tue Nov 12 11:13:59 2013 +0000 Committer: Aled Sage <[email protected]> Committed: Tue Nov 12 21:52:59 2013 +0000 ---------------------------------------------------------------------- .../rebind/persister/XmlMementoSerializer.java | 74 +++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c7ff5922/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java b/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java index c8c8d36..68d8ae1 100644 --- a/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java +++ b/core/src/main/java/brooklyn/entity/rebind/persister/XmlMementoSerializer.java @@ -4,24 +4,41 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.io.IOException; import java.io.Writer; +import java.util.concurrent.atomic.AtomicBoolean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.entity.Entity; import brooklyn.entity.rebind.dto.BasicEntityMemento; import brooklyn.entity.rebind.dto.BasicLocationMemento; import brooklyn.entity.rebind.dto.MutableBrooklynMemento; import brooklyn.event.basic.BasicAttributeSensor; import brooklyn.event.basic.BasicConfigKey; +import brooklyn.location.Location; +import brooklyn.policy.EntityAdjunct; import brooklyn.util.exceptions.Exceptions; import brooklyn.util.xstream.XmlSerializer; +import com.google.common.base.Function; +import com.thoughtworks.xstream.converters.Converter; +import com.thoughtworks.xstream.converters.MarshallingContext; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; + /* uses xml, cleaned up a bit * * there is an early attempt at doing this with JSON in pull request #344 but * it is not nicely deserializable, see comments at http://xstream.codehaus.org/json-tutorial.html */ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements MementoSerializer<T> { - + + private static final Logger LOG = LoggerFactory.getLogger(XmlMementoSerializer.class); + @SuppressWarnings("unused") private final ClassLoader classLoader; + @SuppressWarnings({ "unchecked", "rawtypes" }) public XmlMementoSerializer(ClassLoader classLoader) { this.classLoader = checkNotNull(classLoader, "classLoader"); xstream.alias("brooklyn", MutableBrooklynMemento.class); @@ -29,6 +46,18 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento xstream.alias("location", BasicLocationMemento.class); xstream.alias("configKey", BasicConfigKey.class); xstream.alias("attributeSensor", BasicAttributeSensor.class); + xstream.registerConverter(new ConverterImpl(Location.class, new Function<Location,String>() { + @Override public String apply(Location input) { + return (input != null) ? input.getId() : null; + }})); + xstream.registerConverter(new ConverterImpl(Entity.class, new Function<Entity,String>() { + @Override public String apply(Entity input) { + return (input != null) ? input.getId() : null; + }})); + xstream.registerConverter(new ConverterImpl(EntityAdjunct.class, new Function<EntityAdjunct,String>() { + @Override public String apply(EntityAdjunct input) { + return (input != null) ? input.getId() : null; + }})); } @Override @@ -41,4 +70,45 @@ public class XmlMementoSerializer<T> extends XmlSerializer<T> implements Memento } } -} \ No newline at end of file + public static class ConverterImpl<T> implements Converter { + private final AtomicBoolean hasWarned = new AtomicBoolean(false); + private final Class<?> converatable; + private final Function<T,String> idExtractor; + + ConverterImpl(Class<T> converatable, Function<T,String> idExtractor) { + this.converatable = checkNotNull(converatable, "converatable"); + this.idExtractor = checkNotNull(idExtractor, "idExtractor"); + } + + @SuppressWarnings({ "rawtypes" }) + @Override + public boolean canConvert(Class type) { + return converatable.isAssignableFrom(type); + } + + @SuppressWarnings("unchecked") + @Override + public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { + if (source != null) { + if (hasWarned.compareAndSet(false, true)) { + LOG.warn("Cannot marshall to xml (for persistence) {} {}; should have been intercepted; unmarshalling will give null!", converatable.getSimpleName(), source); + } else { + LOG.debug("Cannot marshall to xml (for persistence) {} {}; should have been intercepted; unmarshalling will give null!", converatable.getSimpleName(), source); + } + } + // no-op; can't marshall this; deserializing will give null! + writer.startNode("unserializableLocation"); + writer.setValue(idExtractor.apply((T)source)); + writer.endNode(); + } + + @Override + public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + reader.moveDown(); + String id = reader.getValue(); + reader.moveUp(); + LOG.warn("Cannot unmarshall from persisted xml {} {}; should have been intercepted; returning null!", converatable.getSimpleName(), id); + return null; + } + } +}
