You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by an...@apache.org on 2015/02/03 12:41:34 UTC
[03/17] incubator-brooklyn git commit: add some low-level xslt
compound transformer rules
add some low-level xslt compound transformer rules
this will allow refactoring and simplifying the existing mechanisms, and supporting new things like replacing 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/7cd66834
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/7cd66834
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/7cd66834
Branch: refs/heads/master
Commit: 7cd66834d331e9f3e7bf908c207f7f0caf215755
Parents: 55b5f65
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed Jan 28 12:25:00 2015 +0000
Committer: Andrea Turli <an...@gmail.com>
Committed: Tue Feb 3 11:25:06 2015 +0100
----------------------------------------------------------------------
.../rebind/transformer/CompoundTransformer.java | 106 ++++++++++++++-----
.../transformer/impl/XsltTransformer.java | 6 +-
.../recursiveCopyWithExtraRules.xslt | 32 ++++++
.../rebind/transformer/xmlReplaceItem.xslt | 32 ++++++
.../transformer/CompoundTransformerTest.java | 81 +++++++++++++-
5 files changed, 225 insertions(+), 32 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7cd66834/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 80cc1ca..7d1c7c8 100644
--- a/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java
+++ b/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java
@@ -30,6 +30,7 @@ import brooklyn.entity.rebind.transformer.impl.XsltTransformer;
import brooklyn.mementos.BrooklynMementoRawData;
import brooklyn.util.ResourceUtils;
import brooklyn.util.collections.MutableMap;
+import brooklyn.util.text.Strings;
import brooklyn.util.text.TemplateProcessor;
import com.google.common.annotations.Beta;
@@ -64,34 +65,7 @@ public class CompoundTransformer {
rawDataTransformers.put(checkNotNull(type, "type"), checkNotNull(val, "val"));
return this;
}
- public Builder renameType(String oldVal, String newVal) {
- // xstream format for inner classes is like <brooklyn.entity.rebind.transformer.CompoundTransformerTest_-OrigType>
- oldVal = toXstreamClassnameFormat(oldVal);
- newVal = toXstreamClassnameFormat(newVal);
-
- String xsltTemplate = ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/entity/rebind/transformer/renameType.xslt");
- String xslt = TemplateProcessor.processTemplateContents(xsltTemplate, ImmutableMap.of("old_val", oldVal, "new_val", newVal));
- return xsltTransformer(xslt);
- }
- public Builder renameClass(String oldVal, String newVal) {
- // xstream format for inner classes is like <brooklyn.entity.rebind.transformer.CompoundTransformerTest_-OrigType>
- oldVal = toXstreamClassnameFormat(oldVal);
- newVal = toXstreamClassnameFormat(newVal);
-
- String xsltTemplate = ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/entity/rebind/transformer/renameClass.xslt");
- String xslt = TemplateProcessor.processTemplateContents(xsltTemplate, ImmutableMap.of("old_val", oldVal, "new_val", newVal));
- return xsltTransformer(xslt);
- }
- public Builder renameField(String clazz, String oldVal, String newVal) {
- // xstream format for inner classes is like <brooklyn.entity.rebind.transformer.CompoundTransformerTest_-OrigType>
- clazz = toXstreamClassnameFormat(clazz);
- oldVal = toXstreamClassnameFormat(oldVal);
- newVal = toXstreamClassnameFormat(newVal);
-
- String xsltTemplate = ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/entity/rebind/transformer/renameField.xslt");
- String xslt = TemplateProcessor.processTemplateContents(xsltTemplate, ImmutableMap.of("class_name", clazz, "old_val", oldVal, "new_val", newVal));
- return xsltTransformer(xslt);
- }
+
/** registers the given XSLT code to be applied to all persisted {@link BrooklynObjectType}s */
public Builder xsltTransformer(String xslt) {
XsltTransformer xsltTransformer = new XsltTransformer(xslt);
@@ -106,7 +80,83 @@ public class CompoundTransformer {
rawDataTransformer(type, xsltTransformer);
return this;
}
+ protected Builder xsltTransformerFromXsltFreemarkerTemplateUrl(String templateUrl, Map<String,String> vars) {
+ String xsltTemplate = ResourceUtils.create(this).getResourceAsString(templateUrl);
+ String xslt = TemplateProcessor.processTemplateContents(xsltTemplate, vars);
+ return xsltTransformer(xslt);
+ }
+ protected Builder xsltTransformerRecursiveCopyWithExtraRules(String ...rules) {
+ String xsltTemplate = ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/entity/rebind/transformer/recursiveCopyWithExtraRules.xslt");
+ String xslt = TemplateProcessor.processTemplateContents(xsltTemplate, ImmutableMap.of("extra_rules", Strings.join(rules, "\n")));
+ return xsltTransformer(xslt);
+ }
+
+ /** Discards and replaces the item at the given XPath.
+ * <p>
+ * For example to replace all occurrences
+ * of text "foo" inside a tag "Tag1", you can use <code>TagName/text()[.='foo']</code>;
+ * passing <code>bar</code> as the second argument would cause
+ * <code><Tag1>foo</Tag1></code> to become <code><Tag1>bar</Tag1></code>.
+ * <p>
+ * Note that java class names may require conversion prior to invoking this;
+ * see {@link #toXstreamClassnameFormat(String)}.
+ */
+ // ie TagName/text()[.='foo'] with 'bar' causes <Tag1>foo</Tag1> to <Tag1>bar</Tag1>
+ public Builder xmlReplaceItem(String xpathToMatch, String newValue) {
+ return xsltTransformerFromXsltFreemarkerTemplateUrl("classpath://brooklyn/entity/rebind/transformer/xmlReplaceItem.xslt",
+ ImmutableMap.of("xpath_to_match", xpathToMatch, "new_val", newValue));
+ // alternatively:
+// return xsltTransformerRecursiveCopyWithExtraRules(
+// "<xsl:template match=\""+xpathToMatch+"\">"
+// + newValue
+// + "</xsl:template>");
+ }
+
+ /**
+ * Replaces a tag, but while continuing to recurse.
+ */
+ public Builder xmlRenameTag(String xpathToMatch, String newValue) {
+ return xsltTransformerRecursiveCopyWithExtraRules(
+ "<xsl:template match=\""+xpathToMatch+"\">"
+ + "<"+newValue+">"
+ + "<xsl:apply-templates select=\"@*|node()\" />"
+ + "</"+newValue+">"
+ + "</xsl:template>");
+ // alternatively:
+// xmlReplaceItem(xpathToMatch, "<"+newValue+">"+"<xsl:apply-templates select=\"@*|node()\" />"+"</"+newValue+">")
+ }
+
+ /** Changes the contents inside a "type" tag:
+ * where the contents match the old value, they are changed to the new value.
+ * <p>
+ * In brooklyn/xstream, a "type" node typically gives the name of a java or catalog type to be used
+ * when creating an instance. */
+ public Builder renameType(String oldVal, String newVal) {
+ return xsltTransformerFromXsltFreemarkerTemplateUrl("classpath://brooklyn/entity/rebind/transformer/renameType.xslt",
+ ImmutableMap.of("old_val", toXstreamClassnameFormat(oldVal), "new_val", toXstreamClassnameFormat(newVal)));
+ }
+ /** Changes an 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). */
+ public Builder renameClass(String oldVal, String newVal) {
+ return xsltTransformerFromXsltFreemarkerTemplateUrl("classpath://brooklyn/entity/rebind/transformer/renameClass.xslt",
+ ImmutableMap.of("old_val", toXstreamClassnameFormat(oldVal), "new_val", 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.
+ * <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. */
+ public Builder renameField(String clazz, String oldVal, String newVal) {
+ return xsltTransformerFromXsltFreemarkerTemplateUrl("classpath://brooklyn/entity/rebind/transformer/renameField.xslt",
+ ImmutableMap.of("class_name", toXstreamClassnameFormat(clazz), "old_val", toXstreamClassnameFormat(oldVal), "new_val", toXstreamClassnameFormat(newVal)));
+ }
+
private String toXstreamClassnameFormat(String val) {
+ // xstream format for inner classes is like <brooklyn.entity.rebind.transformer.CompoundTransformerTest_-OrigType>
return (val.contains("$")) ? val.replace("$", "_-") : val;
}
public CompoundTransformer build() {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7cd66834/core/src/main/java/brooklyn/entity/rebind/transformer/impl/XsltTransformer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/transformer/impl/XsltTransformer.java b/core/src/main/java/brooklyn/entity/rebind/transformer/impl/XsltTransformer.java
index feed847..00dfc7f 100644
--- a/core/src/main/java/brooklyn/entity/rebind/transformer/impl/XsltTransformer.java
+++ b/core/src/main/java/brooklyn/entity/rebind/transformer/impl/XsltTransformer.java
@@ -38,14 +38,16 @@ import com.google.common.annotations.Beta;
public class XsltTransformer implements RawDataTransformer {
private final TransformerFactory factory;
- private final StreamSource xslt;
+ private final String xsltContent;
public XsltTransformer(String xsltContent) {
factory = TransformerFactory.newInstance();
- xslt = new StreamSource(new ByteArrayInputStream(xsltContent.getBytes()));
+ this.xsltContent = xsltContent;
}
public String transform(String input) throws IOException, URISyntaxException, TransformerException {
+ // stream source is single-use
+ StreamSource xslt = new StreamSource(new ByteArrayInputStream(xsltContent.getBytes()));
Transformer transformer = factory.newTransformer(xslt);
Source text = new StreamSource(new ByteArrayInputStream(input.getBytes()));
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7cd66834/core/src/main/resources/brooklyn/entity/rebind/transformer/recursiveCopyWithExtraRules.xslt
----------------------------------------------------------------------
diff --git a/core/src/main/resources/brooklyn/entity/rebind/transformer/recursiveCopyWithExtraRules.xslt b/core/src/main/resources/brooklyn/entity/rebind/transformer/recursiveCopyWithExtraRules.xslt
new file mode 100644
index 0000000..b0f1eb2
--- /dev/null
+++ b/core/src/main/resources/brooklyn/entity/rebind/transformer/recursiveCopyWithExtraRules.xslt
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+ <xsl:output omit-xml-declaration="yes"/>
+
+ <xsl:template match="node()|@*">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ </xsl:copy>
+ </xsl:template>
+
+${extra_rules}
+
+</xsl:stylesheet>
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7cd66834/core/src/main/resources/brooklyn/entity/rebind/transformer/xmlReplaceItem.xslt
----------------------------------------------------------------------
diff --git a/core/src/main/resources/brooklyn/entity/rebind/transformer/xmlReplaceItem.xslt b/core/src/main/resources/brooklyn/entity/rebind/transformer/xmlReplaceItem.xslt
new file mode 100644
index 0000000..8d4acc0
--- /dev/null
+++ b/core/src/main/resources/brooklyn/entity/rebind/transformer/xmlReplaceItem.xslt
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+ <xsl:output omit-xml-declaration="yes"/>
+
+ <xsl:template match="node()|@*">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="${xpath_to_match}">${new_val}</xsl:template>
+
+</xsl:stylesheet>
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7cd66834/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 c8cc39d..6e7ab36 100644
--- a/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerTest.java
@@ -27,16 +27,18 @@ import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
import brooklyn.config.ConfigKey;
import brooklyn.entity.Entity;
+import brooklyn.entity.rebind.BrooklynObjectType;
import brooklyn.entity.rebind.PersistenceExceptionHandler;
import brooklyn.entity.rebind.PersistenceExceptionHandlerImpl;
import brooklyn.entity.rebind.RebindExceptionHandler;
-import brooklyn.entity.rebind.RebindOptions;
import brooklyn.entity.rebind.RebindManager.RebindFailureMode;
+import brooklyn.entity.rebind.RebindOptions;
import brooklyn.entity.rebind.RebindTestFixtureWithApp;
import brooklyn.entity.rebind.RebindTestUtils;
import brooklyn.entity.rebind.RecordingRebindExceptionHandler;
@@ -46,7 +48,6 @@ import brooklyn.entity.rebind.persister.PersistMode;
import brooklyn.event.basic.BasicConfigKey;
import brooklyn.management.ManagementContext;
import brooklyn.management.ha.HighAvailabilityMode;
-import brooklyn.management.internal.LocalManagementContext;
import brooklyn.management.internal.ManagementContextInternal;
import brooklyn.mementos.BrooklynMementoRawData;
import brooklyn.test.entity.TestApplication;
@@ -55,6 +56,7 @@ import brooklyn.util.os.Os;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
@SuppressWarnings("serial")
public class CompoundTransformerTest extends RebindTestFixtureWithApp {
@@ -71,6 +73,81 @@ public class CompoundTransformerTest extends RebindTestFixtureWithApp {
}
@Test
+ public void testXmlReplaceItemText() throws Exception {
+ CompoundTransformer transformer = CompoundTransformer.builder()
+ .xmlReplaceItem("Tag1/text()[.='foo']", "bar")
+ .build();
+ assertSingleXmlTransformation(transformer, "<Tag1>foo</Tag1>", "<Tag1>bar</Tag1>");
+ assertSingleXmlTransformation(transformer, "<Tag1>baz</Tag1>", "<Tag1>baz</Tag1>");
+ assertSingleXmlTransformation(transformer, "<Tag2>foo</Tag2>", "<Tag2>foo</Tag2>");
+ // works when nested
+ assertSingleXmlTransformation(transformer, "<Tag0><Tag1>foo</Tag1><Tag2/></Tag0>", "<Tag0><Tag1>bar</Tag1><Tag2/></Tag0>");
+ // keeps attributes and other children
+ assertSingleXmlTransformation(transformer, "<Tag1 attr=\"value\">foo</Tag1>", "<Tag1 attr=\"value\">bar</Tag1>");
+ assertSingleXmlTransformation(transformer, "<Tag1>foo<Tag2/></Tag1>", "<Tag1>bar<Tag2/></Tag1>");
+ }
+
+ @Test
+ public void testXmlReplaceItemTree() throws Exception {
+ CompoundTransformer transformer = CompoundTransformer.builder()
+ .xmlReplaceItem("Tag1[text()='foo']", "<Tag1>bar</Tag1>")
+ .build();
+ assertSingleXmlTransformation(transformer, "<Tag1>foo</Tag1>", "<Tag1>bar</Tag1>");
+ assertSingleXmlTransformation(transformer, "<Tag1>baz</Tag1>", "<Tag1>baz</Tag1>");
+ assertSingleXmlTransformation(transformer, "<Tag2>foo</Tag2>", "<Tag2>foo</Tag2>");
+ // works when nested
+ assertSingleXmlTransformation(transformer, "<Tag0><Tag1>foo</Tag1><Tag2/></Tag0>", "<Tag0><Tag1>bar</Tag1><Tag2/></Tag0>");
+ // this deletes attributes and other children
+ assertSingleXmlTransformation(transformer, "<Tag1 attr=\"value\">foo</Tag1>", "<Tag1>bar</Tag1>");
+ assertSingleXmlTransformation(transformer, "<Tag1>foo<Tag2/></Tag1>", "<Tag1>bar</Tag1>");
+ }
+
+ @Test
+ public void testXmlReplaceItemAttribute() throws Exception {
+ // note, the syntax for changing an attribute value is obscure, especially the RHS
+ CompoundTransformer transformer = CompoundTransformer.builder()
+ .xmlReplaceItem("Tag1/@attr[.='foo']", "<xsl:attribute name=\"attr\">bar</xsl:attribute>")
+ .build();
+ assertSingleXmlTransformation(transformer, "<Tag1 attr=\"foo\">foo</Tag1>", "<Tag1 attr=\"bar\">foo</Tag1>");
+ assertSingleXmlTransformation(transformer, "<Tag1 attr=\"baz\">foo</Tag1>", "<Tag1 attr=\"baz\">foo</Tag1>");
+ }
+
+ @Test
+ public void testXmlRenameTag() throws Exception {
+ CompoundTransformer transformer = CompoundTransformer.builder()
+ .xmlRenameTag("Tag1[text()='foo']", "Tag2")
+ .build();
+ assertSingleXmlTransformation(transformer, "<Tag1>foo</Tag1>", "<Tag2>foo</Tag2>");
+ assertSingleXmlTransformation(transformer, "<Tag1>baz</Tag1>", "<Tag1>baz</Tag1>");
+ assertSingleXmlTransformation(transformer, "<Tag2>foo</Tag2>", "<Tag2>foo</Tag2>");
+ // works when nested
+ assertSingleXmlTransformation(transformer, "<Tag0><Tag1>foo</Tag1><Tag2/></Tag0>", "<Tag0><Tag2>foo</Tag2><Tag2/></Tag0>");
+ // keeps attributes and other children
+ assertSingleXmlTransformation(transformer, "<Tag1 attr=\"value\">foo</Tag1>", "<Tag2 attr=\"value\">foo</Tag2>");
+ assertSingleXmlTransformation(transformer, "<Tag1>foo<Tag2/></Tag1>", "<Tag2>foo<Tag2/></Tag2>");
+ }
+
+ @Test
+ public void testXmlReplaceItemActuallyAlsoRenamingTag() throws Exception {
+ CompoundTransformer transformer = CompoundTransformer.builder()
+ .xmlReplaceItem("Tag1[text()='foo']", "<Tag2><xsl:apply-templates select=\"@*|node()\" /></Tag2>")
+ .build();
+ assertSingleXmlTransformation(transformer, "<Tag1>foo</Tag1>", "<Tag2>foo</Tag2>");
+ assertSingleXmlTransformation(transformer, "<Tag1>baz</Tag1>", "<Tag1>baz</Tag1>");
+ assertSingleXmlTransformation(transformer, "<Tag2>foo</Tag2>", "<Tag2>foo</Tag2>");
+ // works when nested
+ assertSingleXmlTransformation(transformer, "<Tag0><Tag1>foo</Tag1><Tag2/></Tag0>", "<Tag0><Tag2>foo</Tag2><Tag2/></Tag0>");
+ // keeps attributes and other children
+ assertSingleXmlTransformation(transformer, "<Tag1 attr=\"value\">foo</Tag1>", "<Tag2 attr=\"value\">foo</Tag2>");
+ assertSingleXmlTransformation(transformer, "<Tag1>foo<Tag2/></Tag1>", "<Tag2>foo<Tag2/></Tag2>");
+ }
+
+ protected void assertSingleXmlTransformation(CompoundTransformer transformer, String xmlIn, String xmlOutExpected) throws Exception {
+ String xmlOutActual = Iterables.getOnlyElement( transformer.getRawDataTransformers().get(BrooklynObjectType.ENTITY) ).transform(xmlIn);
+ Assert.assertEquals(xmlOutActual, xmlOutExpected);
+ }
+
+ @Test
public void testNoopTransformation() throws Exception {
CompoundTransformer transformer = CompoundTransformer.builder()
.build();