Fix memento/rebind of AbstractController - serverPoolTargets has entities as keys, so needed to fix MementoTransformer.transformIdsToEntities so it converted keys and values correctly. - Can revisit this in the future for instead using serialised form of an entity-ref, rather than just String perhaps.
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/e5d21855 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/e5d21855 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/e5d21855 Branch: refs/heads/0.5.0 Commit: e5d2185546a40281d7f5117c85a82f4568a4d210 Parents: 64da3fc Author: Aled Sage <[email protected]> Authored: Wed Apr 10 13:01:17 2013 +0100 Committer: Aled Sage <[email protected]> Committed: Wed Apr 10 14:34:36 2013 +0100 ---------------------------------------------------------------------- .../entity/rebind/MementoTransformer.java | 69 ++++++++---- .../entity/rebind/MementoTransformerTest.java | 107 +++++++++++++++++++ 2 files changed, 157 insertions(+), 19 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e5d21855/core/src/main/java/brooklyn/entity/rebind/MementoTransformer.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/rebind/MementoTransformer.java b/core/src/main/java/brooklyn/entity/rebind/MementoTransformer.java index 493b5c2..1706138 100644 --- a/core/src/main/java/brooklyn/entity/rebind/MementoTransformer.java +++ b/core/src/main/java/brooklyn/entity/rebind/MementoTransformer.java @@ -15,6 +15,7 @@ import brooklyn.location.Location; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import com.google.common.reflect.TypeToken; /** * Helpers for transforming references to locations into location ids, and vice versa. @@ -99,29 +100,35 @@ public class MementoTransformer { throw new IllegalStateException("Cannot transform ids to locations of type "+requiredType); } } - + public static <T> T transformIdsToEntities(RebindContext rebindContext, Object value, Class<T> requiredType, boolean removeDanglingRefs) { + return transformIdsToEntities(rebindContext, value, TypeToken.of(requiredType), removeDanglingRefs); + } + + public static <T> T transformIdsToEntities(RebindContext rebindContext, Object value, TypeToken<T> requiredType, boolean removeDanglingRefs) { + Class<? super T> requiredRawType = requiredType.getRawType(); + if (value == null) { return null; - } else if (Entity.class.isAssignableFrom(requiredType)) { + } else if (Entity.class.isAssignableFrom(requiredRawType)) { Entity entity = rebindContext.getEntity((String)value); if (entity == null) { if (removeDanglingRefs) { - LOG.warn("No entity found for "+value+"; return null for "+requiredType.getSimpleName()); + LOG.warn("No entity found for "+value+"; return null for "+requiredRawType.getSimpleName()); } else { throw new IllegalStateException("No entity found for "+value); } } return (T) entity; - } else if (Iterable.class.isAssignableFrom(requiredType)) { + } else if (Iterable.class.isAssignableFrom(requiredRawType)) { Collection<Entity> result = Lists.newArrayList(); for (String id : (Iterable<String>)value) { Entity entity = rebindContext.getEntity(id); if (entity == null) { if (removeDanglingRefs) { - LOG.warn("No entity found for "+id+"; discarding reference from "+requiredType.getSimpleName()); + LOG.warn("No entity found for "+id+"; discarding reference from "+requiredRawType.getSimpleName()); } else { throw new IllegalStateException("No entity found for "+id); } @@ -130,7 +137,7 @@ public class MementoTransformer { } } - if (Set.class.isAssignableFrom(requiredType)) { + if (Set.class.isAssignableFrom(requiredRawType)) { result = Sets.newLinkedHashSet(result); } if (!requiredType.isAssignableFrom(result.getClass())) { @@ -139,19 +146,41 @@ public class MementoTransformer { return (T) result; } else if (value instanceof Map) { - Map<Object,Entity> result = Maps.newLinkedHashMap(); - for (Map.Entry<?, String> entry : ((Map<?,String>)value).entrySet()) { - String id = entry.getValue(); - Entity entity = rebindContext.getEntity(id); - if (entity == null) { - if (removeDanglingRefs) { - LOG.warn("No entity found for "+id+"; discarding reference from "+requiredType.getSimpleName()); - } else { - throw new IllegalStateException("No entity found for "+id); + // If told explicitly the generics, then use that; but otherwise default to the value being of type Entity + TypeToken<?> keyType = requiredType.resolveType(Map.class.getTypeParameters()[0]); + TypeToken<?> valueType = requiredType.resolveType(Map.class.getTypeParameters()[1]); + boolean keyIsEntity = Entity.class.isAssignableFrom(keyType.getRawType()); + boolean valueIsEntity = Entity.class.isAssignableFrom(valueType.getRawType()) || !keyIsEntity; + + Map<Object,Object> result = Maps.newLinkedHashMap(); + for (Map.Entry<?, ?> entry : ((Map<?,?>)value).entrySet()) { + Object key = entry.getKey(); + Object val = entry.getValue(); + + if (keyIsEntity) { + key = rebindContext.getEntity((String)key); + if (key == null) { + if (removeDanglingRefs) { + LOG.warn("No entity found for "+entry.getKey()+"; discarding reference from "+requiredRawType.getSimpleName()); + continue; + } else { + throw new IllegalStateException("No entity found for "+entry.getKey()); + } } - } else { - result.put(entry.getKey(), entity); } + if (valueIsEntity) { + val = rebindContext.getEntity((String)val); + if (val == null) { + if (removeDanglingRefs) { + LOG.warn("No entity found for "+entry.getValue()+"; discarding reference from "+requiredRawType.getSimpleName()); + continue; + } else { + throw new IllegalStateException("No entity found for "+entry.getValue()); + } + } + } + + result.put(key, val); } if (!requiredType.isAssignableFrom(LinkedHashMap.class)) { @@ -232,13 +261,15 @@ public class MementoTransformer { } private static Map<?,?> transformEntitiesToIds(Map<?,?> vs) { - if (containsType(vs.values(), Entity.class)) { + if (containsType(vs.values(), Entity.class) || containsType(vs.keySet(), Entity.class)) { // requires transforming for serialization Map<Object,Object> result = Maps.newLinkedHashMap(); for (Map.Entry<?,?> entry : vs.entrySet()) { Object k = entry.getKey(); Object v = entry.getValue(); - result.put(k, ((Entity)v).getId()); + Object k2 = (k instanceof Entity) ? ((Entity)k).getId() : k; + Object v2 = (v instanceof Entity) ? ((Entity)v).getId() : v; + result.put(k2, v2); } return result; } else { http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e5d21855/core/src/test/java/brooklyn/entity/rebind/MementoTransformerTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/rebind/MementoTransformerTest.java b/core/src/test/java/brooklyn/entity/rebind/MementoTransformerTest.java new file mode 100644 index 0000000..3a2237b --- /dev/null +++ b/core/src/test/java/brooklyn/entity/rebind/MementoTransformerTest.java @@ -0,0 +1,107 @@ +package brooklyn.entity.rebind; + +import static org.testng.Assert.assertEquals; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.entity.Entity; +import brooklyn.location.Location; +import brooklyn.location.basic.SshMachineLocation; +import brooklyn.test.entity.TestEntityImpl; +import brooklyn.util.MutableMap; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.reflect.TypeToken; + +public class MementoTransformerTest { + + private Entity entity; + private RebindContextImpl rebindContext; + private SshMachineLocation location; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + location = new SshMachineLocation(MutableMap.of("address", "localhost")); + entity = new TestEntityImpl(); + rebindContext = new RebindContextImpl(MementoTransformerTest.class.getClassLoader()); + rebindContext.registerLocation(location.getId(), location); + rebindContext.registerEntity(entity.getId(), entity); + } + + @Test + public void testTransformLocation() throws Exception { + assertTransformsLocationIds(location, Location.class); + } + + @Test + public void testTransformLocationSet() throws Exception { + assertTransformsLocationIds(ImmutableSet.of(location), Set.class); + } + + @Test + public void testTransformLocationList() throws Exception { + assertTransformsLocationIds(ImmutableList.of(location), List.class); + } + + @Test + public void testTransformLocationMaop() throws Exception { + assertTransformsLocationIds(ImmutableMap.of("a", location), Map.class); + } + + @Test + public void testTransformEntity() throws Exception { + assertTransformsEntityIds(entity, Entity.class); + } + + @Test + public void testTransformEntitySet() throws Exception { + assertTransformsEntityIds(ImmutableSet.of(entity), Set.class); + } + + @Test + public void testTransformEntityList() throws Exception { + assertTransformsEntityIds(ImmutableList.of(entity), List.class); + } + + @Test + public void testTransformMapWithEntityValueUsingClazz() throws Exception { + assertTransformsEntityIds(ImmutableMap.of("a", entity), Map.class); + } + + @SuppressWarnings("serial") + @Test + public void testTransformMapWithEntityValueUsingTypeToken() throws Exception { + assertTransformsEntityIds(ImmutableMap.of("a", entity), new TypeToken<Map<String,Entity>>() {}); + } + + @SuppressWarnings("serial") + @Test + public void testTransformMapWithEntityKey() throws Exception { + assertTransformsEntityIds(ImmutableMap.of(entity, "a"), new TypeToken<Map<Entity,String>>() {}); + } + + private void assertTransformsLocationIds(Object orig, Class<?> type) throws Exception { + Object transformed = MementoTransformer.transformLocationsToIds(orig); + Object result = MementoTransformer.transformIdsToLocations(rebindContext, transformed, type, true); + assertEquals(result, orig, "transformed="+transformed); + } + + private void assertTransformsEntityIds(Object orig, Class<?> type) throws Exception { + Object transformed = MementoTransformer.transformEntitiesToIds(orig); + Object result = MementoTransformer.transformIdsToEntities(rebindContext, transformed, type, true); + assertEquals(result, orig, "transformed="+transformed); + } + + private void assertTransformsEntityIds(Object orig, TypeToken<?> type) throws Exception { + Object transformed = MementoTransformer.transformEntitiesToIds(orig); + Object result = MementoTransformer.transformIdsToEntities(rebindContext, transformed, type, true); + assertEquals(result, orig, "transformed="+transformed); + } +}
