/* * JBoss, Home of Professional Open Source. * Copyright 2014, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.controller.transform.description; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; import java.util.Collections; import java.util.Map; import org.jboss.as.controller.ExpressionResolver; import org.jboss.as.controller.ModelVersion; import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.ProcessType; import org.jboss.as.controller.ResourceDefinition; import org.jboss.as.controller.RunningMode; import org.jboss.as.controller.SimpleResourceDefinition; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.controller.descriptions.NonResolvingResourceDescriptionResolver; import org.jboss.as.controller.operations.common.Util; import org.jboss.as.controller.registry.ManagementResourceRegistration; import org.jboss.as.controller.registry.Resource; import org.jboss.as.controller.transform.OperationTransformer; import org.jboss.as.controller.transform.OperationTransformer.TransformedOperation; import org.jboss.as.controller.transform.ResourceTransformationContext; import org.jboss.as.controller.transform.TransformationContext; import org.jboss.as.controller.transform.TransformationTarget; import org.jboss.as.controller.transform.TransformationTargetImpl; import org.jboss.as.controller.transform.TransformerRegistry; import org.jboss.as.controller.transform.Transformers; import org.jboss.as.controller.transform.TransformersSubRegistration; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ValueExpression; import org.junit.Assert; import org.junit.Before; import org.junit.Test; /** * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> */ public class ChainedOperationBuilderTestCase { private static PathElement PATH = PathElement.pathElement("toto", "testSubsystem"); private static final ResourceDefinition ROOT = new SimpleResourceDefinition(null, new NonResolvingResourceDescriptionResolver()); private static final ModelVersion V1_0_0 = ModelVersion.create(1, 0, 0); private static final ModelVersion V2_0_0 = ModelVersion.create(2, 0, 0); private static final ModelVersion V3_0_0 = ModelVersion.create(3, 0, 0); private static final ModelVersion V4_0_0 = ModelVersion.create(4, 0, 0); private static final ModelVersion UNKNOWN_VERSION = ModelVersion.create(1, 1, 0); private static final ModelVersion[] ALL_TESTED_VERSIONS = {V1_0_0, ModelVersion.create(1, 0, 1), ModelVersion.create(1, 0, 5), UNKNOWN_VERSION}; private static final ModelVersion[] VALID_TESTED_VERSIONS = {V1_0_0, ModelVersion.create(1, 0, 1), ModelVersion.create(1, 0, 5)}; private Resource resourceRoot = Resource.Factory.create(); private Resource toto; private TransformerRegistry registry = TransformerRegistry.Factory.create(); private ManagementResourceRegistration resourceRegistration = ManagementResourceRegistration.Factory.forProcessType(ProcessType.EMBEDDED_SERVER).createRegistration(ROOT); private TransformersSubRegistration transformersSubRegistration; private ModelNode resourceModel; @Before public void setUp() { // Cleanup resourceRoot = Resource.Factory.create(); registry = TransformerRegistry.Factory.create(); resourceRegistration = ManagementResourceRegistration.Factory.forProcessType(ProcessType.EMBEDDED_SERVER).createRegistration(ROOT); // test toto = Resource.Factory.create(); resourceRoot.registerChild(PATH, toto); resourceModel = toto.getModel(); // Register the description transformersSubRegistration = registry.getServerRegistration(ModelVersion.create(1)); } @Test public void testOperationNoTransformation() throws Exception { ChainedTransformationDescriptionBuilder chainedBuilder = TransformationDescriptionBuilder.Factory.createChainedInstance(PATH, V2_0_0); chainedBuilder.createBuilder(V2_0_0, V1_0_0); TransformationDescription.Tools.register(chainedBuilder.build(V1_0_0).get(V1_0_0), transformersSubRegistration); final PathAddress address = PathAddress.EMPTY_ADDRESS.append(PATH); ModelNode node = new ModelNode(); node.get(OP_ADDR).set(address.toModelNode()); node.get(OP).set(ADD); OperationTransformer.TransformedOperation op; for(ModelVersion version : ALL_TESTED_VERSIONS) { op = transformOperation(node, version); Assert.assertFalse(op.rejectOperation(success())); Assert.assertEquals(node, op.getTransformedOperation()); } node.get(OP_ADDR).set(address.append("one", "two").toModelNode()); for(ModelVersion version : ALL_TESTED_VERSIONS) { op = transformOperation(node, version); Assert.assertFalse(op.rejectOperation(success())); Assert.assertEquals(node, op.getTransformedOperation()); } } @Test public void testOperationChainedNoTransformation() throws Exception { ChainedTransformationDescriptionBuilder chainedBuilder = TransformationDescriptionBuilder.Factory.createChainedInstance(PATH, V3_0_0); chainedBuilder.createBuilder(V3_0_0, V2_0_0); chainedBuilder.createBuilder(V2_0_0, V1_0_0); TransformationDescription.Tools.register(chainedBuilder.build(V1_0_0, V2_0_0).get(V1_0_0), transformersSubRegistration); final PathAddress address = PathAddress.EMPTY_ADDRESS.append(PATH); ModelNode node = new ModelNode(); node.get(OP_ADDR).set(address.toModelNode()); node.get(OP).set(ADD); assertNotRejectedOperation(node, node); node.get(OP_ADDR).set(address.append("one", "two").toModelNode()); assertNotRejectedOperation(node, node); } @Test public void testDefaultOperationOneInChain() throws Exception { ChainedTransformationDescriptionBuilder chainedBuilder = TransformationDescriptionBuilder.Factory.createChainedInstance(PATH, V2_0_0); chainedBuilder.createBuilder(V2_0_0, V1_0_0) .getAttributeBuilder() .addRejectCheck(RejectAttributeChecker.SIMPLE_EXPRESSIONS, "attr1") .setValueConverter(new SimpleAttributeConverter("old", "new"), "attr2"); TransformationDescription.Tools.register(chainedBuilder.build(V1_0_0).get(V1_0_0), transformersSubRegistration); final PathAddress address = PathAddress.EMPTY_ADDRESS.append(PATH); ModelNode original = new ModelNode(); original.get(OP_ADDR).set(address.toModelNode()); original.get(OP).set(ADD); original.get("attr1").set(new ValueExpression("${no.no}")); original.get("attr2").set("old"); original.get("attr3").set("whatever"); OperationTransformer.TransformedOperation op = transformOperation(original, ModelVersion.create(1)); Assert.assertTrue(op.rejectOperation(success())); ModelNode expected = original.clone(); expected.get("attr2").set("new"); assertRejectedOperation(original, expected); ModelNode originalNotRejected = original.clone(); originalNotRejected.get("attr1").set("accepted"); expected = originalNotRejected.clone(); expected.get("attr2").set("new"); assertNotRejectedOperation(originalNotRejected, expected); } @Test public void testDefaultOperationChain() throws Exception { ChainedTransformationDescriptionBuilder chainedBuilder = TransformationDescriptionBuilder.Factory.createChainedInstance(PATH, V4_0_0); chainedBuilder.createBuilder(V4_0_0, V3_0_0) .getAttributeBuilder() .addRejectCheck(RejectAttributeChecker.SIMPLE_EXPRESSIONS, "attr1") .setValueConverter(new SimpleAttributeConverter("old", "one"), "attr"); chainedBuilder.createBuilder(V3_0_0, V2_0_0) .getAttributeBuilder() .addRejectCheck(RejectAttributeChecker.SIMPLE_EXPRESSIONS, "attr2") .setValueConverter(new SimpleAttributeConverter("one", "two"), "attr"); chainedBuilder.createBuilder(V2_0_0, V1_0_0) .getAttributeBuilder() .addRejectCheck(RejectAttributeChecker.SIMPLE_EXPRESSIONS, "attr3") .setValueConverter(new SimpleAttributeConverter("two", "three"), "attr"); TransformationDescription.Tools.register(chainedBuilder.build(V1_0_0, V2_0_0, V3_0_0).get(V1_0_0), transformersSubRegistration); final PathAddress address = PathAddress.EMPTY_ADDRESS.append(PATH); ModelNode original = new ModelNode(); original.get(OP_ADDR).set(address.toModelNode()); original.get(OP).set(ADD); original.get("attr1").set("a"); original.get("attr2").set("b"); original.get("attr3").set("c"); original.get("attr").set("old"); ModelNode expected = original.clone(); expected.get("attr").set("three"); assertNotRejectedOperation(original, expected); ModelNode reject = original.clone(); reject.get("attr1").set(new ValueExpression("${reject}")); expected = reject.clone(); expected.get("attr").set("three"); assertRejectedOperation(reject, expected); reject = original.clone(); reject.get("attr2").set(new ValueExpression("${reject}")); expected = reject.clone(); expected.get("attr").set("three"); assertRejectedOperation(reject, expected); reject = original.clone(); reject.get("attr3").set(new ValueExpression("${reject}")); expected = reject.clone(); expected.get("attr").set("three"); assertRejectedOperation(reject, expected); } private void assertRejectedOperation(ModelNode reject, ModelNode expected) throws OperationFailedException { OperationTransformer.TransformedOperation op; for (ModelVersion version : VALID_TESTED_VERSIONS) { op = transformOperation(reject, version); Assert.assertTrue(op.rejectOperation(success())); Assert.assertEquals(expected, op.getTransformedOperation()); } op = transformOperation(reject, UNKNOWN_VERSION); Assert.assertFalse(op.rejectOperation(success())); Assert.assertEquals(reject, op.getTransformedOperation()); } private void assertNotRejectedOperation(ModelNode original, ModelNode expected) throws OperationFailedException { OperationTransformer.TransformedOperation op; for (ModelVersion version : VALID_TESTED_VERSIONS) { op = transformOperation(original, version); Assert.assertFalse(op.rejectOperation(success())); Assert.assertEquals(expected, op.getTransformedOperation()); } op = transformOperation(original, UNKNOWN_VERSION); Assert.assertFalse(op.rejectOperation(success())); Assert.assertEquals(original, op.getTransformedOperation()); } @Test public void testDefaultOperationChildWithNoTransformer() throws Exception { ChainedTransformationDescriptionBuilder chainedBuilder = TransformationDescriptionBuilder.Factory.createChainedInstance(PATH, V4_0_0); chainedBuilder.createBuilder(V4_0_0, V3_0_0) .getAttributeBuilder() .addRejectCheck(RejectAttributeChecker.SIMPLE_EXPRESSIONS, "attr1") .setValueConverter(new SimpleAttributeConverter("old", "new"), "attr2"); chainedBuilder.createBuilder(V3_0_0, V2_0_0) .getAttributeBuilder() .addRejectCheck(RejectAttributeChecker.SIMPLE_EXPRESSIONS, "attr1") .setValueConverter(new SimpleAttributeConverter("old", "new"), "attr2"); chainedBuilder.createBuilder(V2_0_0, V1_0_0) .getAttributeBuilder() .addRejectCheck(RejectAttributeChecker.SIMPLE_EXPRESSIONS, "attr1") .setValueConverter(new SimpleAttributeConverter("old", "new"), "attr2"); TransformationDescription.Tools.register(chainedBuilder.build(V1_0_0, V2_0_0, V3_0_0).get(V1_0_0), transformersSubRegistration); final PathAddress address = PathAddress.EMPTY_ADDRESS.append(PATH).append("one", "two"); ModelNode original = new ModelNode(); original.get(OP_ADDR).set(address.toModelNode()); original.get(OP).set(ADD); original.get("attr1").set(new ValueExpression("${no.no}")); original.get("attr2").set("old"); original.get("attr3").set("whatever"); //Since this operation is on child resource with no transformers registered we should //just get the original back and there should be no rejections assertNotRejectedOperation(original, original); } @Test public void testOperationAgainstDiscardedAndRejectedResources() throws Exception { ChainedTransformationDescriptionBuilder chainedBuilder = TransformationDescriptionBuilder.Factory.createChainedInstance(PATH, V4_0_0); ResourceTransformationDescriptionBuilder builder = chainedBuilder.createBuilder(V4_0_0, V3_0_0) .getAttributeBuilder() .setValueConverter(new SimpleAttributeConverter("old", "one"), "attr") .end(); builder.discardChildResource(PathElement.pathElement("child", "discard1")); builder.rejectChildResource(PathElement.pathElement("child", "reject1")); builder = chainedBuilder.createBuilder(V3_0_0, V2_0_0) .getAttributeBuilder() .setValueConverter(new SimpleAttributeConverter("one", "two"), "attr") .end(); builder.discardChildResource(PathElement.pathElement("child", "discard2")); builder.rejectChildResource(PathElement.pathElement("child", "reject2")); builder = chainedBuilder.createBuilder(V2_0_0, V1_0_0) .getAttributeBuilder() .setValueConverter(new SimpleAttributeConverter("two", "three"), "attr") .end(); builder.discardChildResource(PathElement.pathElement("child", "discard3")); builder.rejectChildResource(PathElement.pathElement("child", "reject3")); TransformationDescription.Tools.register(chainedBuilder.build(V1_0_0, V2_0_0, V3_0_0).get(V1_0_0), transformersSubRegistration); //Although tested elsewhere test that the root operations get transformed as expected final PathAddress address = PathAddress.EMPTY_ADDRESS.append(PATH); ModelNode original = Util.createAddOperation(address); original.get(OP).set(ADD); original.get("attr1").set("a"); original.get("attr").set("old"); OperationTransformer.TransformedOperation op = transformOperation(original, ModelVersion.create(1)); ModelNode expected = original.clone(); expected.get("attr").set("three"); assertNotRejectedOperation(original, expected); //Test ops against children with nothing special registered, and its children original = Util.createAddOperation(address.append(PathElement.pathElement("child", "normal"))); original.get("attr1").set("a"); assertNotRejectedOperation(original, original); original = Util.createAddOperation(address.append(PathElement.pathElement("child", "normal")).append(PathElement.pathElement("one", "more"))); original.get("attr1").set("a"); assertNotRejectedOperation(original, original); //Test ops against the discarded resources original = Util.createAddOperation(address.append(PathElement.pathElement("child", "discard1"))); original.get("attr1").set("a"); assertDiscardedOperation(original); original = Util.createAddOperation(address.append(PathElement.pathElement("child", "discard2"))); original.get("attr1").set("a"); assertDiscardedOperation(original); original = Util.createAddOperation(address.append(PathElement.pathElement("child", "discard3"))); original.get("attr1").set("a"); assertDiscardedOperation(original); //Test ops against children of the discarded resources original = Util.createAddOperation(address.append(PathElement.pathElement("child", "discard1")).append(PathElement.pathElement("one", "more"))); original.get("attr1").set("a"); assertDiscardedOperation(original); original = Util.createAddOperation(address.append(PathElement.pathElement("child", "discard2")).append(PathElement.pathElement("one", "more"))); original.get("attr1").set("a"); assertDiscardedOperation(original); original = Util.createAddOperation(address.append(PathElement.pathElement("child", "discard3")).append(PathElement.pathElement("one", "more"))); original.get("attr1").set("a"); assertDiscardedOperation(original); //Test ops against the rejected resources original = Util.createAddOperation(address.append(PathElement.pathElement("child", "reject1"))); original.get("attr1").set("a"); assertRejectedOperation(original, original); original = Util.createAddOperation(address.append(PathElement.pathElement("child", "reject2"))); original.get("attr1").set("a"); assertRejectedOperation(original, original); original = Util.createAddOperation(address.append(PathElement.pathElement("child", "reject3"))); original.get("attr1").set("a"); assertRejectedOperation(original, original); //Test ops against children of the rejected resources original = Util.createAddOperation(address.append(PathElement.pathElement("child", "reject1")).append(PathElement.pathElement("one", "more"))); original.get("attr1").set("a"); assertRejectedOperation(original, original); original = Util.createAddOperation(address.append(PathElement.pathElement("child", "reject2")).append(PathElement.pathElement("one", "more"))); original.get("attr1").set("a"); assertRejectedOperation(original, original); original = Util.createAddOperation(address.append(PathElement.pathElement("child", "reject3")).append(PathElement.pathElement("one", "more"))); original.get("attr1").set("a"); assertRejectedOperation(original, original); } @Test public void testRawOperationOverride() throws Exception { ChainedTransformationDescriptionBuilder chainedBuilder = TransformationDescriptionBuilder.Factory.createChainedInstance(PATH, V4_0_0); ResourceTransformationDescriptionBuilder builder = chainedBuilder.createBuilder(V4_0_0, V3_0_0) .addRawOperationTransformationOverride("test", new OperationTransformer() { @Override public TransformedOperation transformOperation(TransformationContext context, PathAddress address, ModelNode operation) throws OperationFailedException { Assert.assertEquals("test", operation.get(OP).asString()); return new TransformedOperation(Util.createOperation("test1", address), TransformedOperation.ORIGINAL_RESULT); } }); builder.getAttributeBuilder() .setValueConverter(new SimpleAttributeConverter("old", "one"), "attr") .end(); builder = chainedBuilder.createBuilder(V3_0_0, V2_0_0) .addRawOperationTransformationOverride("test1", new OperationTransformer() { @Override public TransformedOperation transformOperation(TransformationContext context, PathAddress address, ModelNode operation) throws OperationFailedException { Assert.assertEquals("test1", operation.get(OP).asString()); return new TransformedOperation(Util.createOperation("test2", address), TransformedOperation.ORIGINAL_RESULT); } }); builder.getAttributeBuilder() .setValueConverter(new SimpleAttributeConverter("one", "two"), "attr") .end(); builder = chainedBuilder.createBuilder(V2_0_0, V1_0_0) .addRawOperationTransformationOverride("test2", new OperationTransformer() { @Override public TransformedOperation transformOperation(TransformationContext context, PathAddress address, ModelNode operation) throws OperationFailedException { Assert.assertEquals("test2", operation.get(OP).asString()); return new TransformedOperation(Util.createOperation("test3", address), TransformedOperation.ORIGINAL_RESULT); } }); builder.getAttributeBuilder() .setValueConverter(new SimpleAttributeConverter("two", "three"), "attr") .end(); TransformationDescription.Tools.register(chainedBuilder.build(V1_0_0, V2_0_0, V3_0_0).get(V1_0_0), transformersSubRegistration); //Although tested elsewhere test that not overridded operations get transformed as expected final PathAddress address = PathAddress.EMPTY_ADDRESS.append(PATH); ModelNode original = Util.createAddOperation(address); original.get(OP).set(ADD); original.get("attr1").set("a"); original.get("attr").set("old"); ModelNode expected = original.clone(); expected.get("attr").set("three"); assertNotRejectedOperation(original, expected); //Test that the raw operation transformer kicks in original = Util.createEmptyOperation("test", address); for(ModelVersion version : VALID_TESTED_VERSIONS) { transformOperation(original, version); } } @Test public void testDiscardOperations() throws Exception { ChainedTransformationDescriptionBuilder chainedBuilder = TransformationDescriptionBuilder.Factory.createChainedInstance(PATH, V4_0_0); chainedBuilder.createBuilder(V4_0_0, V3_0_0) .discardOperations("discard1A", "discard1B"); chainedBuilder.createBuilder(V3_0_0, V2_0_0) .discardOperations("discard2A", "discard2B"); chainedBuilder.createBuilder(V2_0_0, V1_0_0) .discardOperations("discard3A", "discard3B"); TransformationDescription.Tools.register(chainedBuilder.build(V1_0_0, V2_0_0, V3_0_0).get(V1_0_0), transformersSubRegistration); //Although tested elsewhere test that not discarded operations get transformed as expected final PathAddress address = PathAddress.EMPTY_ADDRESS.append(PATH); ModelNode original = Util.createAddOperation(address); original.get(OP).set(ADD); original.get("attr1").set("a"); original.get("attr").set("old"); assertNotRejectedOperation(original, original); //Now test that the discarded operations get discarded original = Util.createEmptyOperation("discard1A", address); assertDiscardedOperation(original); original = Util.createEmptyOperation("discard1B", address); assertDiscardedOperation(original); original = Util.createEmptyOperation("discard2A", address); assertDiscardedOperation(original); original = Util.createEmptyOperation("discard2B", address); assertDiscardedOperation(original); original = Util.createEmptyOperation("discard3A", address); assertDiscardedOperation(original); original = Util.createEmptyOperation("discard3B", address); assertDiscardedOperation(original); } private void assertDiscardedOperation(ModelNode original) throws OperationFailedException { OperationTransformer.TransformedOperation op; for (ModelVersion version : VALID_TESTED_VERSIONS) { op = transformOperation(original, version); Assert.assertFalse(op.rejectOperation(success())); Assert.assertNull(op.getTransformedOperation()); } op = transformOperation(original, UNKNOWN_VERSION); Assert.assertFalse(op.rejectOperation(success())); Assert.assertEquals(original, op.getTransformedOperation()); } @Test public void testOperationTransformationOverride() throws Exception { ChainedTransformationDescriptionBuilder chainedBuilder = TransformationDescriptionBuilder.Factory.createChainedInstance(PATH, V4_0_0); ResourceTransformationDescriptionBuilder builder = chainedBuilder.createBuilder(V4_0_0, V3_0_0) .getAttributeBuilder() .setValueConverter(new SimpleAttributeConverter("old", "one"), "attr") .end(); builder.addOperationTransformationOverride("test") .inheritResourceAttributeDefinitions() .addRename("attr1", "attr-one") .rename("testA"); builder = chainedBuilder.createBuilder(V3_0_0, V2_0_0) .getAttributeBuilder() .setValueConverter(new SimpleAttributeConverter("one", "two"), "attr") .end(); builder.addOperationTransformationOverride("testA") .setValueConverter(new NewAttributeConverter("new-one"), "attr-new") .setValueConverter(new SimpleAttributeConverter("a", "No1"), "attr-one"); builder = chainedBuilder.createBuilder(V2_0_0, V1_0_0) .getAttributeBuilder() .setValueConverter(new NewAttributeConverter("newA"), "new-main") .end(); builder.addOperationTransformationOverride("testA") .inheritResourceAttributeDefinitions() .setValueConverter(new SimpleAttributeConverter("new-one", "new1"), "attr-new") .addRename("attr-new", "new-attr") .rename("testB"); TransformationDescription.Tools.register(chainedBuilder.build(V1_0_0, V2_0_0, V3_0_0).get(V1_0_0), transformersSubRegistration); //Although tested elsewhere test that the main operation gets transformed as expected final PathAddress address = PathAddress.EMPTY_ADDRESS.append(PATH); ModelNode original = Util.createAddOperation(address); original.get(OP).set(ADD); original.get("attr1").set("a"); original.get("attr").set("old"); ModelNode expected = original.clone(); expected.get("attr").set("two"); expected.get("new-main").set("newA"); assertNotRejectedOperation(original, expected); //Test the overridden operation original = Util.createAddOperation(address); original.get(OP).set("test"); original.get("attr").set("old"); original.get("attr1").set("a"); expected = Util.createOperation("testB", address); expected.get("attr-one").set("No1"); expected.get("attr").set("one"); expected.get("new-main").set("newA"); expected.get("new-attr").set("new1"); assertNotRejectedOperation(original, expected); } @Test public void testCustomOperationTransformationOverride() throws Exception { ChainedTransformationDescriptionBuilder chainedBuilder = TransformationDescriptionBuilder.Factory.createChainedInstance(PATH, V4_0_0); ResourceTransformationDescriptionBuilder builder = chainedBuilder.createBuilder(V4_0_0, V3_0_0) .getAttributeBuilder() .setValueConverter(new SimpleAttributeConverter("old", "one"), "attr") .end(); builder.addOperationTransformationOverride("test") .inheritResourceAttributeDefinitions() .addRename("attr1", "attr-one") .rename("testA") .setCustomOperationTransformer(new OperationTransformer() { @Override public TransformedOperation transformOperation(TransformationContext context, PathAddress address, ModelNode operation) throws OperationFailedException { Assert.assertEquals("testA", operation.get(OP).asString()); Assert.assertEquals("one", operation.get("attr").asString()); Assert.assertEquals("a", operation.get("attr-one").asString()); ModelNode copy = operation.clone(); copy.get("attr").set("one_"); copy.get("attr-one").set("a_"); return new TransformedOperation(copy, TransformedOperation.ORIGINAL_RESULT); } }); builder = chainedBuilder.createBuilder(V3_0_0, V2_0_0) .getAttributeBuilder() .setValueConverter(new SimpleAttributeConverter("one", "two"), "attr") .end(); builder.addOperationTransformationOverride("testA") .setValueConverter(new SimpleAttributeConverter("a_", "No1"), "attr-one") .setCustomOperationTransformer(new OperationTransformer() { @Override public TransformedOperation transformOperation(TransformationContext context, PathAddress address, ModelNode operation) throws OperationFailedException { ModelNode copy = operation.clone(); copy.get("attr-new").set("new-one"); return new TransformedOperation(copy, TransformedOperation.ORIGINAL_RESULT); } }); builder = chainedBuilder.createBuilder(V2_0_0, V1_0_0) .getAttributeBuilder() .setValueConverter(new NewAttributeConverter("newA"), "new-main") .end(); builder.addOperationTransformationOverride("testA") .inheritResourceAttributeDefinitions() .setValueConverter(new SimpleAttributeConverter("new-one", "new1"), "attr-new") .addRename("attr-new", "new-attr") .rename("testB"); TransformationDescription.Tools.register(chainedBuilder.build(V1_0_0, V2_0_0, V3_0_0).get(V1_0_0), transformersSubRegistration); //Although tested elsewhere test that the main operation gets transformed as expected final PathAddress address = PathAddress.EMPTY_ADDRESS.append(PATH); ModelNode original = Util.createAddOperation(address); original.get(OP).set(ADD); original.get("attr1").set("a"); original.get("attr").set("old"); ModelNode expected = original.clone(); expected.get("attr").set("two"); expected.get("new-main").set("newA"); assertNotRejectedOperation(original, expected); //Test the overridden operation original = Util.createAddOperation(address); original.get(OP).set("test"); original.get("attr").set("old"); original.get("attr1").set("a"); expected = Util.createOperation("testB", address); expected.get("attr-one").set("No1"); expected.get("attr").set("one_"); expected.get("new-main").set("newA"); expected.get("new-attr").set("new1"); assertNotRejectedOperation(original, expected); } @Test public void testOperationsTransformationHierarchy() throws Exception { final PathElement common = PathElement.pathElement("child", "common"); final PathElement one = PathElement.pathElement("child", "one"); final PathElement two = PathElement.pathElement("child", "two"); final PathElement three = PathElement.pathElement("child", "three"); ChainedTransformationDescriptionBuilder chainedBuilder = TransformationDescriptionBuilder.Factory.createChainedInstance(PATH, V4_0_0); ResourceTransformationDescriptionBuilder builder = chainedBuilder.createBuilder(V4_0_0, V3_0_0); builder.addChildResource(common).getAttributeBuilder() .setValueConverter(new SimpleAttributeConverter("old", "one"), "attr") .end(); builder.addChildResource(one).addOperationTransformationOverride("test") .setValueConverter(new NewAttributeConverter("a"), "attr-1"); builder = chainedBuilder.createBuilder(V3_0_0, V2_0_0); builder.addChildResource(common).getAttributeBuilder() .setValueConverter(new SimpleAttributeConverter("one", "two"), "attr"); builder.addChildResource(two).addOperationTransformationOverride("test") .setValueConverter(new NewAttributeConverter("b"), "attr-2"); builder = chainedBuilder.createBuilder(V2_0_0, V1_0_0); builder.addChildResource(common).getAttributeBuilder() .setValueConverter(new SimpleAttributeConverter("two", "three"), "attr"); builder.addChildResource(three).addOperationTransformationOverride("test") .setValueConverter(new NewAttributeConverter("c"), "attr-3"); TransformationDescription.Tools.register(chainedBuilder.build(V1_0_0, V2_0_0, V3_0_0).get(V1_0_0), transformersSubRegistration); //Although tested elsewhere test that the root operation gets (not) transformed as expected final PathAddress address = PathAddress.EMPTY_ADDRESS.append(PATH); ModelNode original = Util.createAddOperation(address); original.get("attr").set("old"); assertNotRejectedOperation(original, original); //Add ops on the common child should have the 'common' transformation original = Util.createAddOperation(address.append(common)); original.get("attr").set("old"); ModelNode expected = original.clone(); expected.get("attr").set("three"); assertNotRejectedOperation(original, expected); //Add ops on the one, two and three children should not be transformed original = Util.createAddOperation(address.append(one)); assertNotRejectedOperation(original, original); original = Util.createAddOperation(address.append(two)); assertNotRejectedOperation(original, original); original = Util.createAddOperation(address.append(three)); assertNotRejectedOperation(original, original); //The overridden ops on the children should have basic transformation original = Util.createEmptyOperation("test", address.append(one)); expected = original.clone(); expected.get("attr-1").set("a"); assertNotRejectedOperation(original, expected); original = Util.createEmptyOperation("test", address.append(two)); expected = original.clone(); expected.get("attr-2").set("b"); assertNotRejectedOperation(original, expected); original = Util.createEmptyOperation("test", address.append(three)); expected = original.clone(); expected.get("attr-3").set("c"); assertNotRejectedOperation(original, expected); } @Test public void testOperationsTransformationHierarchyWithPathChange() throws Exception { ChainedTransformationDescriptionBuilder chainedBuilder = TransformationDescriptionBuilder.Factory.createChainedInstance(PATH, V4_0_0); ResourceTransformationDescriptionBuilder builder = chainedBuilder.createBuilder(V4_0_0, V3_0_0); builder.getAttributeBuilder().setValueConverter(new SimpleAttributeConverter("old", "one"), "attr"); ResourceTransformationDescriptionBuilder childBuilder = builder.addChildRedirection(PathElement.pathElement("a", "one"), PathElement.pathElement("a1", "one1")); childBuilder.getAttributeBuilder().setValueConverter(new SimpleAttributeConverter("original", "ONE"), "ch"); ResourceTransformationDescriptionBuilder grandChildBuilder = childBuilder.addChildRedirection(PathElement.pathElement("x", "a"), PathElement.pathElement("x1", "a1")); grandChildBuilder.getAttributeBuilder().setValueConverter(new SimpleAttributeConverter("start", "A"), "gc"); grandChildBuilder = builder.addChildResource(PathElement.pathElement("b", "two")).addChildRedirection(PathElement.pathElement("y", "b"), PathElement.pathElement("y1", "b1")); grandChildBuilder.getAttributeBuilder().setValueConverter(new SimpleAttributeConverter("first", "second"), "gc"); builder = chainedBuilder.createBuilder(V3_0_0, V2_0_0); builder.getAttributeBuilder().setValueConverter(new SimpleAttributeConverter("one", "two"), "attr"); childBuilder = builder.addChildRedirection(PathElement.pathElement("a1", "one1"), PathElement.pathElement("a2", "one2")); childBuilder.getAttributeBuilder().setValueConverter(new SimpleAttributeConverter("ONE", "TWO"), "ch"); grandChildBuilder = builder.addChildResource(PathElement.pathElement("b", "two")).addChildRedirection(PathElement.pathElement("y1", "b1"), PathElement.pathElement("y2", "b2")); grandChildBuilder.getAttributeBuilder().setValueConverter(new SimpleAttributeConverter("second", "third"), "gc"); builder = chainedBuilder.createBuilder(V2_0_0, V1_0_0); builder.getAttributeBuilder().setValueConverter(new SimpleAttributeConverter("two", "three"), "attr"); childBuilder = builder.addChildRedirection(PathElement.pathElement("a2", "one2"), PathElement.pathElement("a3", "one3")); childBuilder.getAttributeBuilder().setValueConverter(new SimpleAttributeConverter("TWO", "THREE"), "ch"); grandChildBuilder = childBuilder.addChildRedirection(PathElement.pathElement("x1", "a1"), PathElement.pathElement("x2", "a2")); grandChildBuilder.getAttributeBuilder().setValueConverter(new SimpleAttributeConverter("A", "B"), "gc"); grandChildBuilder = builder.addChildResource(PathElement.pathElement("b", "two")).addChildRedirection(PathElement.pathElement("y2", "b2"), PathElement.pathElement("y3", "b3")); grandChildBuilder.getAttributeBuilder().setValueConverter(new SimpleAttributeConverter("third", "fourth"), "gc"); TransformationDescription.Tools.register(chainedBuilder.build(V1_0_0, V2_0_0, V3_0_0).get(V1_0_0), transformersSubRegistration); //Although tested elsewhere test that the root operation gets transformed as expected final PathAddress address = PathAddress.EMPTY_ADDRESS.append(PATH); ModelNode original = Util.createAddOperation(address); original.get(OP).set(ADD); original.get("attr1").set("a"); original.get("attr").set("old"); ModelNode expected = original.clone(); expected.get("attr").set("three"); assertNotRejectedOperation(original, expected); //Test child redirection original = Util.createAddOperation(address.append(PathElement.pathElement("a", "one"))); original.get("ch").set("original"); expected = Util.createAddOperation(address.append(PathElement.pathElement("a3", "one3"))); expected.get("ch").set("THREE"); assertNotRejectedOperation(original, expected); //Test grandchild redirection when the intermediate child changes original = Util.createAddOperation(address.append(PathElement.pathElement("a", "one"), PathElement.pathElement("x", "a"))); original.get("gc").set("start"); expected = Util.createAddOperation(address.append(PathElement.pathElement("a3", "one3"), PathElement.pathElement("x2", "a2"))); expected.get("gc").set("B"); assertNotRejectedOperation(original, expected); //Test grandchild redirection when the intermediate child stays the same original = Util.createAddOperation(address.append(PathElement.pathElement("b", "two"), PathElement.pathElement("y", "b"))); original.get("gc").set("first"); expected = Util.createAddOperation(address.append(PathElement.pathElement("b", "two"), PathElement.pathElement("y3", "b3"))); expected.get("gc").set("fourth"); assertNotRejectedOperation(original, expected); /* This does not work, but should not be an issue since in the real world operations must be executed against the current model, so a1=one1 does not exist //Test that ops for addresses further down the chain don't get transformed original = Util.createAddOperation(address.append(PathElement.pathElement("a1", "one1"))); op = transformOperation(original, ModelVersion.create(1)); Assert.assertFalse(op.rejectOperation(success())); Assert.assertEquals(original, op.getTransformedOperation()); */ } @Test public void testReadResourceFromTransformer() throws Exception { resourceModel.get("attr1").set("reject1"); //Note that when reading the resource from the chained transformers, the original model is used. ChainedTransformationDescriptionBuilder chainedBuilder = TransformationDescriptionBuilder.Factory.createChainedInstance(PATH, V4_0_0); chainedBuilder.createBuilder(V4_0_0, V3_0_0) .getAttributeBuilder() .addRejectCheck(new SimpleRejectAttributeChecker("reject1"), "attr1"); chainedBuilder.createBuilder(V3_0_0, V2_0_0) .getAttributeBuilder() .addRejectCheck(new SimpleRejectAttributeChecker("reject2"), "attr1"); chainedBuilder.createBuilder(V2_0_0, V1_0_0) .getAttributeBuilder() .addRejectCheck(new SimpleRejectAttributeChecker("reject3"), "attr1"); TransformationDescription.Tools.register(chainedBuilder.build(V1_0_0, V2_0_0, V3_0_0).get(V1_0_0), transformersSubRegistration); final PathAddress address = PathAddress.pathAddress(PATH); ModelNode original = new ModelNode(); original.get(OP_ADDR).set(address.toModelNode()); original.get(OP).set(ADD); TransformedOperation op = transformOperation(original, ModelVersion.create(1)); Assert.assertTrue(op.rejectOperation(success())); resourceModel.get("attr1").set("reject2"); op = transformOperation(original, ModelVersion.create(1)); Assert.assertTrue(op.rejectOperation(success())); resourceModel.get("attr1").set("reject3"); op = transformOperation(original, ModelVersion.create(1)); Assert.assertTrue(op.rejectOperation(success())); resourceModel.get("attr1").set("ok"); assertNotRejectedOperation(original, original); } private OperationTransformer.TransformedOperation transformOperation(final ModelNode operation, final ModelVersion version) throws OperationFailedException { final TransformationTarget target = create(registry, version); final TransformationContext context = createContext(target); return getTransfomers(target).transformOperation(context, operation.clone()); } private ResourceTransformationContext createContext(final TransformationTarget target) { return Transformers.Factory.create(target, resourceRoot, resourceRegistration, ExpressionResolver.TEST_RESOLVER, RunningMode.NORMAL, ProcessType.STANDALONE_SERVER, null); } private Transformers getTransfomers(final TransformationTarget target) { return Transformers.Factory.create(target); } protected TransformationTarget create(final TransformerRegistry registry, ModelVersion version) { return create(registry, version, TransformationTarget.TransformationTargetType.SERVER); } protected TransformationTarget create(final TransformerRegistry registry, ModelVersion version, TransformationTarget.TransformationTargetType type) { return TransformationTargetImpl.create(null, registry, version, Collections.<PathAddress, ModelVersion>emptyMap(), type); } private static ModelNode success() { final ModelNode result = new ModelNode(); result.get(ModelDescriptionConstants.OUTCOME).set(ModelDescriptionConstants.SUCCESS); result.get(ModelDescriptionConstants.RESULT); return result; } private static class SimpleAttributeConverter extends AttributeConverter.DefaultAttributeConverter { private final ModelNode expectedValue; private final ModelNode newValue; SimpleAttributeConverter(String expectedValue, String newValue) { this.expectedValue = new ModelNode(expectedValue); this.newValue = new ModelNode(newValue); } SimpleAttributeConverter(ModelNode expectedValue, ModelNode newValue) { this.expectedValue = expectedValue; this.newValue = newValue; } @Override protected void convertAttribute(PathAddress address, String attributeName, ModelNode attributeValue, TransformationContext context) { //System.out.println("-----> transform " + address + " " + attributeValue.asString() + " expected:" + expectedValue); Assert.assertEquals(expectedValue, attributeValue); attributeValue.set(newValue); } } private static class NewAttributeConverter extends SimpleAttributeConverter { NewAttributeConverter(String newValue) { super(new ModelNode(), new ModelNode(newValue)); } } private static class SimpleRejectAttributeChecker implements RejectAttributeChecker { private final ModelNode value; public SimpleRejectAttributeChecker(String value) { this.value = new ModelNode(value); } @Override public boolean rejectOperationParameter(PathAddress address, String attributeName, ModelNode attributeValue, ModelNode operation, TransformationContext context) { boolean relative = context.readResource(PathAddress.EMPTY_ADDRESS).getModel().get(attributeName).equals(value); boolean absolute = context.readResourceFromRoot(address).getModel().get(attributeName).equals(value); Assert.assertEquals(relative, absolute); return relative; } @Override public boolean rejectResourceAttribute(PathAddress address, String attributeName, ModelNode attributeValue, TransformationContext context) { return false; } @Override public String getRejectionLogMessageId() { return "Test123"; } @Override public String getRejectionLogMessage(Map<String, ModelNode> attributes) { return "Failed!!!!"; } } }