add low-level transformer support for changing catalogItemId
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/e82c9a67 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/e82c9a67 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/e82c9a67 Branch: refs/heads/master Commit: e82c9a672ee7cdc2ae4f80d2ffc32a1152f92a63 Parents: 5e1d94e Author: Alex Heneveld <[email protected]> Authored: Wed Jan 28 17:01:55 2015 +0000 Committer: Alex Heneveld <[email protected]> Committed: Thu Jan 29 11:40:43 2015 +0000 ---------------------------------------------------------------------- .../rebind/transformer/CompoundTransformer.java | 59 ++++++++++++++----- .../transformer/CompoundTransformerLoader.java | 13 ++++- .../CompoundTransformerLoaderTest.java | 9 ++- .../transformer/CompoundTransformerTest.java | 60 +++++++++++++++++++- 4 files changed, 125 insertions(+), 16 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e82c9a67/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java b/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java index 26f6af5..bb87783 100644 --- a/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java +++ b/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java @@ -34,10 +34,11 @@ import brooklyn.util.text.Strings; import brooklyn.util.text.TemplateProcessor; import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Multimap; -import com.google.common.annotations.VisibleForTesting; @Beta public class CompoundTransformer { @@ -119,34 +120,66 @@ public class CompoundTransformer { + "</"+newValue+">"); } - /** Changes the contents inside a "type" tag: - * where the contents match the old value, they are changed to the new value. + /** + * Renames an explicit type name reference in brooklyn-xstream serialization. + * <p> + * Really this changes the contents inside any tag named "type", + * where the contents match the oldVal, they are changed to the newVal. * <p> - * In brooklyn/xstream, a "type" node typically gives the name of a java or catalog type to be used - * when creating an instance. */ + * In brooklyn-xstream, the "type" node typically gives the name of a java or catalog type to be used + * when creating an instance; that's how this works. + */ public Builder renameType(String oldVal, String newVal) { return xmlReplaceItem("type/text()[.='"+toXstreamClassnameFormat(oldVal)+"']", toXstreamClassnameFormat(newVal)); // previously this did a more complex looping, essentially // <when .=oldVal>newVal</when><otherwise><apply-templates/></otherwise> // but i think these are equivalent } - /** Changes an XML tag matching a given old value: + /** + * Renames an implicit class name reference (a tag). + * <p> + * Really this changes any XML tag matching a given old value; * the tag is changed to the new value. * <p> - * In xstream many tags correspond to the java class of an object so this is a way to change - * the java class (or xstream alias) of a persisted instance (or instance inside them). */ + * In brooklyn-xstream many tags correspond to the java class of an object; + * that's how this works to to change the java class (or xstream alias) + * of a persisted instance, included nested instances. + */ public Builder renameClass(String oldVal, String newVal) { return xmlRenameTag(toXstreamClassnameFormat(oldVal), toXstreamClassnameFormat(newVal)); } - /** Changes an XML tag inside another tag: - * where the outer tag and inner tag match the values given here, - * the inner tag is changed to the new value. + /** + * Renames a field in xstream serialization. * <p> - * In stream tags corresponding to fields are contained in the tag corresponding to the class name, - * so this gives a way to change the name of a field which will be deserialized. */ + * Really this changes an XML tag inside another tag, + * where the outer tag and inner tag match the clazz and oldVal values given here, + * the inner tag is changed to the newVal. + * <p> + * In brooklyn-xstream, tags corresponding to fields are contained in the tag + * corresponding to the class name; that's how this works. + */ public Builder renameField(String clazz, String oldVal, String newVal) { return xmlRenameTag(toXstreamClassnameFormat(clazz)+"/"+toXstreamClassnameFormat(oldVal), toXstreamClassnameFormat(newVal)); } + /** Changes the contents of an XML tag 'catalogItemId' where the + * old text matches oldSymbolicName and optionally oldVersion + * to have newSymbolicName and newVersion. + * <p> + * This provides a programmatic way to change the catalogItemID. */ + public Builder changeCatalogItemId(String oldSymbolicName, String oldVersion, + String newSymbolicName, String newVersion) { + if (oldVersion==null) + return changeCatalogItemId(oldSymbolicName, newSymbolicName, newVersion); + // warnings use underscore notation because that's what CompoundTransformerLoader uses + return xmlReplaceItem("catalogItemId/text()[.='"+ + Preconditions.checkNotNull(oldSymbolicName, "old_symbolic_name")+":"+Preconditions.checkNotNull(oldVersion, "old_version")+"']", + Preconditions.checkNotNull(newSymbolicName, "new_symbolic_name")+":"+Preconditions.checkNotNull(newVersion, "new_version")); + } + /** As {@link #changeCatalogItemId(String, String, String, String)} matching any old version. */ + public Builder changeCatalogItemId(String oldSymbolicName, String newSymbolicName, String newVersion) { + return xmlReplaceItem("catalogItemId/text()[starts-with(.,'"+Preconditions.checkNotNull(oldSymbolicName, "old_symbolic_name")+":')]", + Preconditions.checkNotNull(newSymbolicName, "new_symbolic_name")+":"+Preconditions.checkNotNull(newVersion, "new_version")); + } private String toXstreamClassnameFormat(String val) { // xstream format for inner classes is like <brooklyn.entity.rebind.transformer.CompoundTransformerTest_-OrigType> http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e82c9a67/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoader.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoader.java b/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoader.java index 9d3cac1..3465709 100644 --- a/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoader.java +++ b/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoader.java @@ -62,13 +62,17 @@ public class CompoundTransformerLoader { String oldVal = (String) args.get("old_val"); String newVal = (String) args.get("new_val"); builder.renameField(clazz, oldVal, newVal); + } else if (name.equals("catalogItemId")) { + builder.changeCatalogItemId( + (String) args.get("old_symbolic_name"), checkString(args.get("old_version"), "old_version"), + (String) args.get("new_symbolic_name"), checkString(args.get("new_version"), "new_version")); } else if (name.equals("xslt")) { String url = (String) args.get("url"); @SuppressWarnings("unchecked") Map<String,?> substitutions = (Map<String, ?>) args.get("substitutions"); String xsltTemplate = ResourceUtils.create(CompoundTransformer.class).getResourceAsString(url); String xslt = TemplateProcessor.processTemplateContents(xsltTemplate, substitutions == null ? ImmutableMap.<String, String>of() : substitutions); - // TODO pass XSLT-style parameters instead, maybe? that's more normal, + // we could pass XSLT-style parameters instead, maybe? that's more normal, // but OTOH freemarker is maybe more powerful, given our other support there builder.xsltTransformer(xslt); } else if (name.equals("rawDataTransformer")) { @@ -83,4 +87,11 @@ public class CompoundTransformerLoader { throw new IllegalStateException("Unsupported transform '"+name+"' ("+args+")"); } } + + private static String checkString(Object object, String name) { + if (object!=null && !(object instanceof String)) { + throw new IllegalArgumentException("Argument '"+name+"' must be a string; numbers may need explicit quoting in YAML."); + } + return (String) object; + } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e82c9a67/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoaderTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoaderTest.java b/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoaderTest.java index 454e860..d55e073 100644 --- a/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoaderTest.java +++ b/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoaderTest.java @@ -44,6 +44,12 @@ public class CompoundTransformerLoaderTest { " class_name: myclassname\n"+ " old_val: myoldname\n"+ " new_val: mynewname\n"+ + // low-level mechanism to change catalogItemId; used (and tested) by higher-level methods + // which use symbolic_name:version notation to avoid the unpleasant need for yaml quotes + "catalogItemId:\n"+ + " old_symbolic_name: myclassname\n"+ + " new_symbolic_name: myclassname\n"+ + " new_version: '2.0'\n"+ "xslt:\n"+ " url: classpath://brooklyn/entity/rebind/transformer/impl/renameType.xslt\n"+ " substitutions:\n"+ @@ -58,7 +64,8 @@ public class CompoundTransformerLoaderTest { assertTrue(Iterables.get(rawDataTransformers, 1) instanceof XsltTransformer); assertTrue(Iterables.get(rawDataTransformers, 2) instanceof XsltTransformer); assertTrue(Iterables.get(rawDataTransformers, 3) instanceof XsltTransformer); - assertTrue(Iterables.get(rawDataTransformers, 4) instanceof MyRawDataTransformer); + assertTrue(Iterables.get(rawDataTransformers, 4) instanceof XsltTransformer); + assertTrue(Iterables.get(rawDataTransformers, 5) instanceof MyRawDataTransformer); } public static class MyRawDataTransformer implements RawDataTransformer { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e82c9a67/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerTest.java b/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerTest.java index 49c1005..db3cbc2 100644 --- a/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerTest.java +++ b/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerTest.java @@ -294,7 +294,65 @@ public class CompoundTransformerTest extends RebindTestFixtureWithApp { assertSingleXmlTransformation(transformer, input, expected); } - + + @Test + public void testChangeCatalogItemIdExplicitVersionInXml() throws Exception { + CompoundTransformer transformer = CompoundTransformer.builder() + .changeCatalogItemId("foo", "1.0", "bar", "2.0") + .build(); + + String input = + "<entity myattrib=\"myval\">"+NEWLINE+ + " <catalogItemId>foo:1.0</catalogItemId>"+NEWLINE+ + " <config>ignore</config>"+NEWLINE+ + "</entity>"; + String expected = + "<entity myattrib=\"myval\">"+NEWLINE+ + " <catalogItemId>bar:2.0</catalogItemId>"+NEWLINE+ + " <config>ignore</config>"+NEWLINE+ + "</entity>"; + + assertSingleXmlTransformation(transformer, input, expected); + } + @Test + public void testChangeCatalogItemIdExplicitVersionNonMatchInXml() throws Exception { + CompoundTransformer transformer = CompoundTransformer.builder() + .changeCatalogItemId("foo", "1.0", "bar", "2.0") + .build(); + + String input = + "<entity myattrib=\"myval\">"+NEWLINE+ + " <catalogItemId>foo:1.1</catalogItemId>"+NEWLINE+ + " <config>ignore</config>"+NEWLINE+ + "</entity>"; + String expected = + "<entity myattrib=\"myval\">"+NEWLINE+ + " <catalogItemId>foo:1.1</catalogItemId>"+NEWLINE+ + " <config>ignore</config>"+NEWLINE+ + "</entity>"; + + assertSingleXmlTransformation(transformer, input, expected); + } + @Test + public void testChangeCatalogItemIdAnyVersionInXml() throws Exception { + CompoundTransformer transformer = CompoundTransformer.builder() + .changeCatalogItemId("foo", "bar", "2.0") + .build(); + + String input = + "<entity myattrib=\"myval\">"+NEWLINE+ + " <catalogItemId>foo:1.2</catalogItemId>"+NEWLINE+ + " <config>ignore</config>"+NEWLINE+ + "</entity>"; + String expected = + "<entity myattrib=\"myval\">"+NEWLINE+ + " <catalogItemId>bar:2.0</catalogItemId>"+NEWLINE+ + " <config>ignore</config>"+NEWLINE+ + "</entity>"; + + assertSingleXmlTransformation(transformer, input, expected); + } + protected TestApplication transformAndRebind(CompoundTransformer transformer) throws Exception { RebindTestUtils.waitForPersisted(origApp); BrooklynMementoRawData newRawData = transform(origManagementContext, transformer);
