diff --git a/subsystem-test/framework/src/main/java/org/jboss/as/subsystem/test/TransformationUtils.java b/subsystem-test/framework/src/main/java/org/jboss/as/subsystem/test/TransformationUtils.java index 583fa78c50e..2e8e391deee 100644 --- a/subsystem-test/framework/src/main/java/org/jboss/as/subsystem/test/TransformationUtils.java +++ b/subsystem-test/framework/src/main/java/org/jboss/as/subsystem/test/TransformationUtils.java @@ -96,10 +96,6 @@ private static Resource modelToResource(final PathAddress startAddress, final Im } allFields.remove(name); } - if (!value.isDefined() && model.isDefined() && reg.getChildAddresses(PathAddress.EMPTY_ADDRESS).isEmpty()) { - value.setEmptyObject(); - } - res.writeModel(value); for (String childType : reg.getChildNames(PathAddress.EMPTY_ADDRESS)) { if (model.hasDefined(childType)) { @@ -113,6 +109,12 @@ private static Resource modelToResource(final PathAddress startAddress, final Im allFields.remove(childType); } + if (!value.isDefined() && model.isDefined() && (reg.getChildAddresses(PathAddress.EMPTY_ADDRESS).isEmpty() || res.getChildTypes().isEmpty())) { + value.setEmptyObject(); + } + res.writeModel(value); + + if (!allFields.isEmpty()){ throw ControllerLogger.ROOT_LOGGER.modelFieldsNotKnown(allFields, startAddress.append(fullPath)); } diff --git a/subsystem-test/tests/src/test/java/org/jboss/as/subsystem/test/InternalPackageProtectedAccess.java b/subsystem-test/tests/src/test/java/org/jboss/as/subsystem/test/InternalPackageProtectedAccess.java new file mode 100644 index 00000000000..99b80c4d3d5 --- /dev/null +++ b/subsystem-test/tests/src/test/java/org/jboss/as/subsystem/test/InternalPackageProtectedAccess.java @@ -0,0 +1,20 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.jboss.as.subsystem.test; + +import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration; +import org.jboss.as.controller.registry.Resource; +import org.jboss.dmr.ModelNode; + +/** + * For tests to call package protected methods in this package + */ +public class InternalPackageProtectedAccess { + public static Resource modelToResource(final ImmutableManagementResourceRegistration reg, final ModelNode model, boolean includeUndefined) { + return TransformationUtils.modelToResource(reg, model, includeUndefined); + } +} diff --git a/subsystem-test/tests/src/test/java/org/jboss/as/subsystem/test/transformationutils/TransformationUtilsTestCase.java b/subsystem-test/tests/src/test/java/org/jboss/as/subsystem/test/transformationutils/TransformationUtilsTestCase.java new file mode 100644 index 00000000000..3728a84632a --- /dev/null +++ b/subsystem-test/tests/src/test/java/org/jboss/as/subsystem/test/transformationutils/TransformationUtilsTestCase.java @@ -0,0 +1,469 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.jboss.as.subsystem.test.transformationutils; + +import static org.jboss.as.controller.PathAddress.EMPTY_ADDRESS; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INCLUDE_DEFAULTS; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_RESOURCE_OPERATION; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RECURSIVE; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; + +import org.jboss.as.controller.Extension; +import org.jboss.as.controller.ExtensionContext; +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.as.controller.OperationStepHandler; +import org.jboss.as.controller.PathAddress; +import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.SimpleOperationDefinitionBuilder; +import org.jboss.as.controller.descriptions.NonResolvingResourceDescriptionResolver; +import org.jboss.as.controller.operations.common.Util; +import org.jboss.as.controller.operations.global.ReadResourceHandler; +import org.jboss.as.controller.parsing.ExtensionParsingContext; +import org.jboss.as.controller.parsing.ParseUtils; +import org.jboss.as.controller.persistence.SubsystemMarshallingContext; +import org.jboss.as.controller.registry.ManagementResourceRegistration; +import org.jboss.as.controller.registry.Resource; +import org.jboss.as.model.test.ModelTestUtils; +import org.jboss.as.subsystem.test.AbstractSubsystemTest; +import org.jboss.as.subsystem.test.AdditionalInitialization; +import org.jboss.as.subsystem.test.ControllerInitializer; +import org.jboss.as.subsystem.test.InternalPackageProtectedAccess; +import org.jboss.as.subsystem.test.KernelServices; +import org.jboss.as.subsystem.test.simple.subsystem.SimpleSubsystemExtension; +import org.jboss.as.subsystem.test.transformationutils.subsystem.TransformationUtilsExtension; +import org.jboss.dmr.ModelNode; +import org.jboss.staxmapper.XMLElementReader; +import org.jboss.staxmapper.XMLElementWriter; +import org.jboss.staxmapper.XMLExtendedStreamReader; +import org.jboss.staxmapper.XMLExtendedStreamWriter; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +public class TransformationUtilsTestCase extends AbstractSubsystemTest { + + private static final DelegatingExtension DELEGATING_EXTENSION = new DelegatingExtension(); + private KernelServices kernelServices; + + private static final PathAddress SUBSYSTEM_ADDR = PathAddress.pathAddress(SUBSYSTEM, TransformationUtilsExtension.SUBSYSTEM_NAME); + + private static Boolean includeUndefined = null; + + + public TransformationUtilsTestCase() { + super(TransformationUtilsExtension.SUBSYSTEM_NAME, DELEGATING_EXTENSION); + } + + @After + public void after() { + kernelServices.shutdown(); + kernelServices = null; + DELEGATING_EXTENSION.current = null; + includeUndefined = null; + } + + @Test + public void testNoChildNoAttrReg_EmptySubsystemIsDefined_inclUndefined() throws Exception { + List operations = new ArrayList<>(); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR)); + + ModelNode model = runAndReadSubsystemModel(TransformationUtilsExtension.createBuilder(), operations, true); + + ModelNode expected = new ModelNode(); + expected.setEmptyObject(); + + ModelTestUtils.compare(expected, model); + } + + @Test + public void testNoChildWithAttrReg_EmptySubsystemIsDefined_inclUndefined() throws Exception { + TransformationUtilsExtension.Builder builder = TransformationUtilsExtension.createBuilder() + .addAttribute("test"); + + List operations = new ArrayList<>(); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR)); + + ModelNode model = runAndReadSubsystemModel(builder, operations, true); + + ModelNode expected = new ModelNode(); + expected.get("test").set(new ModelNode()); + + ModelTestUtils.compare(expected, model); + } + + @Test + public void testNoChildWithAttrReg_SubsystemWithAttrIsDefined_inclUndefined() throws Exception { + TransformationUtilsExtension.Builder builder = TransformationUtilsExtension.createBuilder() + .addAttribute("test"); + + List operations = new ArrayList<>(); + ModelNode add = Util.createAddOperation(SUBSYSTEM_ADDR); + add.get("test").set("one"); + operations.add(add); + + ModelNode model = runAndReadSubsystemModel(builder, operations, true); + + ModelNode expected = new ModelNode(); + expected.get("test").set("one"); + + ModelTestUtils.compare(expected, model); + } + + @Test + public void testHasChildNoAttrReg_EmptySubsystemIsDefined_inclUndefined() throws Exception { + // This is one of the cases that https://issues.redhat.com/browse/WFCORE-7036 fixes + TransformationUtilsExtension.Builder builder = TransformationUtilsExtension.createBuilder(); + PathElement pe = PathElement.pathElement("child", "test"); + builder.createChildBuilder(pe); + + List operations = new ArrayList<>(); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR)); + + + ModelNode model = runAndReadSubsystemModel(builder, operations, true); + + ModelNode expected = new ModelNode(); + expected.setEmptyObject(); + + ModelTestUtils.compare(expected, model); + } + + @Test + public void testHasChildNoAttrReg_SubsystemWithChildIsDefined_inclUndefined() throws Exception { + TransformationUtilsExtension.Builder builder = TransformationUtilsExtension.createBuilder(); + PathElement pe = PathElement.pathElement("child", "test"); + builder.createChildBuilder(pe); + + List operations = new ArrayList<>(); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR)); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR.append(pe))); + + + ModelNode model = runAndReadSubsystemModel(builder, operations, true); + + ModelNode expected = new ModelNode(); + expected.get(pe.getKey(), pe.getValue()).setEmptyObject(); + + ModelTestUtils.compare(expected, model); + } + + + @Test + public void testHasChildWithAttrReg_EmptySubsystemIsDefined_inclUndefined() throws Exception { + TransformationUtilsExtension.Builder builder = TransformationUtilsExtension.createBuilder() + .addAttribute("test"); + builder.createChildBuilder(PathElement.pathElement("child", "test")); + + List operations = new ArrayList<>(); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR)); + + ModelNode model = runAndReadSubsystemModel(builder, operations, true); + + ModelNode expected = new ModelNode(); + expected.get("test").set(new ModelNode()); + + ModelTestUtils.compare(expected, model); + } + + @Test + public void testHasChildWithAttrReg_SubsystemWithAttrIsDefined_inclUndefined() throws Exception { + TransformationUtilsExtension.Builder builder = TransformationUtilsExtension.createBuilder() + .addAttribute("test"); + builder.createChildBuilder(PathElement.pathElement("child", "test")); + + List operations = new ArrayList<>(); + ModelNode add = Util.createAddOperation(SUBSYSTEM_ADDR); + add.get("test").set("one"); + operations.add(add); + + ModelNode model = runAndReadSubsystemModel(builder, operations, true); + + ModelNode expected = new ModelNode(); + expected.get("test").set("one"); + + ModelTestUtils.compare(expected, model); + } + + @Test + public void testHasChildWithAttrReg_SubsystemWithChildIsDefined_inclUndefined() throws Exception { + TransformationUtilsExtension.Builder builder = TransformationUtilsExtension.createBuilder() + .addAttribute("test"); + PathElement pe = PathElement.pathElement("child", "test"); + builder.createChildBuilder(pe); + + List operations = new ArrayList<>(); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR)); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR.append(pe))); + + + ModelNode model = runAndReadSubsystemModel(builder, operations, true); + + ModelNode expected = new ModelNode(); + // Attribute is undefined + expected.get("test").set(new ModelNode()); //Undefined + expected.get(pe.getKey(), pe.getValue()).setEmptyObject(); + + ModelTestUtils.compare(expected, model); + } + + @Test + public void testNoChildNoAttrReg_EmptySubsystemIsDefined_exclUndefined() throws Exception { + List operations = new ArrayList<>(); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR)); + + ModelNode model = runAndReadSubsystemModel(TransformationUtilsExtension.createBuilder(), operations, false); + + ModelNode expected = new ModelNode(); + expected.setEmptyObject(); + + ModelTestUtils.compare(expected, model); + } + + @Test + public void testNoChildWithAttrReg_EmptySubsystemIsDefined_exclUndefined() throws Exception { + TransformationUtilsExtension.Builder builder = TransformationUtilsExtension.createBuilder() + .addAttribute("test"); + + List operations = new ArrayList<>(); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR)); + + ModelNode model = runAndReadSubsystemModel(builder, operations, false); + + ModelNode expected = new ModelNode(); + expected.setEmptyObject(); + + ModelTestUtils.compare(expected, model); + } + + @Test + public void testNoChildWithAttrReg_SubsystemWithAttrIsDefined_exclUndefined() throws Exception { + TransformationUtilsExtension.Builder builder = TransformationUtilsExtension.createBuilder() + .addAttribute("test"); + + List operations = new ArrayList<>(); + ModelNode add = Util.createAddOperation(SUBSYSTEM_ADDR); + add.get("test").set("one"); + operations.add(add); + + ModelNode model = runAndReadSubsystemModel(builder, operations, false); + + ModelNode expected = new ModelNode(); + expected.get("test").set("one"); + + ModelTestUtils.compare(expected, model); + } + + @Test + public void testHasChildNoAttrReg_EmptySubsystemIsDefined_exclUndefined() throws Exception { + // This is one of the cases that https://issues.redhat.com/browse/WFCORE-7036 fixes + TransformationUtilsExtension.Builder builder = TransformationUtilsExtension.createBuilder(); + PathElement pe = PathElement.pathElement("child", "test"); + builder.createChildBuilder(pe); + + List operations = new ArrayList<>(); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR)); + + + ModelNode model = runAndReadSubsystemModel(builder, operations, false); + + ModelNode expected = new ModelNode(); + expected.setEmptyObject(); + + ModelTestUtils.compare(expected, model); + } + + @Test + public void testHasChildNoAttrReg_SubsystemWithChildIsDefined_exclUndefined() throws Exception { + TransformationUtilsExtension.Builder builder = TransformationUtilsExtension.createBuilder(); + PathElement pe = PathElement.pathElement("child", "test"); + builder.createChildBuilder(pe); + + List operations = new ArrayList<>(); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR)); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR.append(pe))); + + + ModelNode model = runAndReadSubsystemModel(builder, operations, false); + + ModelNode expected = new ModelNode(); + expected.get(pe.getKey(), pe.getValue()).setEmptyObject(); + + ModelTestUtils.compare(expected, model); + } + + + @Test + public void testHasChildWithAttrReg_EmptySubsystemIsDefined_exclUndefined() throws Exception { + // This is one of the cases that https://issues.redhat.com/browse/WFCORE-7036 fixes + TransformationUtilsExtension.Builder builder = TransformationUtilsExtension.createBuilder() + .addAttribute("test"); + builder.createChildBuilder(PathElement.pathElement("child", "test")); + + List operations = new ArrayList<>(); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR)); + + ModelNode model = runAndReadSubsystemModel(builder, operations, false); + + ModelNode expected = new ModelNode(); + expected.setEmptyObject(); + + ModelTestUtils.compare(expected, model); + } + + @Test + public void testHasChildWithAttrReg_SubsystemWithAttrIsDefined_exclUndefined() throws Exception { + TransformationUtilsExtension.Builder builder = TransformationUtilsExtension.createBuilder() + .addAttribute("test"); + builder.createChildBuilder(PathElement.pathElement("child", "test")); + + List operations = new ArrayList<>(); + ModelNode add = Util.createAddOperation(SUBSYSTEM_ADDR); + add.get("test").set("one"); + operations.add(add); + + ModelNode model = runAndReadSubsystemModel(builder, operations, false); + + ModelNode expected = new ModelNode(); + expected.get("test").set("one"); + + ModelTestUtils.compare(expected, model); + } + + @Test + public void testHasChildWithAttrReg_SubsystemWithChildIsDefined_exclUndefined() throws Exception { + TransformationUtilsExtension.Builder builder = TransformationUtilsExtension.createBuilder() + .addAttribute("test"); + PathElement pe = PathElement.pathElement("child", "test"); + builder.createChildBuilder(pe); + + List operations = new ArrayList<>(); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR)); + operations.add(Util.createAddOperation(SUBSYSTEM_ADDR.append(pe))); + + + ModelNode model = runAndReadSubsystemModel(builder, operations, false); + + ModelNode expected = new ModelNode(); + expected.get(pe.getKey(), pe.getValue()).setEmptyObject(); + + ModelTestUtils.compare(expected, model); + } + + private ModelNode runAndReadSubsystemModel(TransformationUtilsExtension.Builder builder, List bootOps, boolean includeUndefined) throws Exception { + TransformationUtilsTestCase.includeUndefined = includeUndefined; + Assert.assertNull(kernelServices); + DELEGATING_EXTENSION.current = builder.build(); + kernelServices = createKernelServicesBuilder(new TestInitialization()) + .setBootOperations(bootOps) + .build(); + + ModelNode model = kernelServices.executeForResult(Util.createOperation("invoke-transformation-utils", EMPTY_ADDRESS)); + model = model.get(SUBSYSTEM).get(TransformationUtilsExtension.SUBSYSTEM_NAME); + return model; + } + + + private static class DelegatingExtension implements Extension { + + volatile Extension current; + + DelegatingExtension() { + } + + @Override + public void initialize(ExtensionContext context) { + current.initialize(context); + } + + @Override + public void initializeParsers(ExtensionParsingContext context) { + context.setSubsystemXmlMapping(TransformationUtilsExtension.SUBSYSTEM_NAME, DummyParser.NAMESPACE, new DummyParser()); + } + } + + + private static class DummyParser implements XMLStreamConstants, XMLElementReader>, XMLElementWriter { + public static final String NAMESPACE = "urn:mycompany:dummy:1.0"; + + /** {@inheritDoc} */ + @Override + public void writeContent(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException { + // We don't actually invoke this parser, so what is does doesn't matter. + // It is just here since the testing framework requires a parser + context.startSubsystemElement(SimpleSubsystemExtension.NAMESPACE, false); + writer.writeEndElement(); + } + + /** {@inheritDoc} */ + @Override + public void readElement(XMLExtendedStreamReader reader, List list) throws XMLStreamException { + // We don't actually invoke this parser, so the contents don't matter. + // It is just here since the testing framework requires a parser + ParseUtils.requireNoContent(reader); + list.add(Util.createAddOperation(SUBSYSTEM_ADDR)); + } + } + + + private static class TestInitialization extends AdditionalInitialization.ManagementAdditionalInitialization { + @Override + protected ControllerInitializer createControllerInitializer() { + return new ControllerInitializer() { + @Override + protected void initializeModel(Resource rootResource, ManagementResourceRegistration rootRegistration) { + super.initializeModel(rootResource, rootRegistration); + rootRegistration.registerOperationHandler( + new SimpleOperationDefinitionBuilder("invoke-transformation-utils", NonResolvingResourceDescriptionResolver.INSTANCE) + .build(), + new OperationStepHandler() { + @Override + public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { + if (includeUndefined == null) { + throw new IllegalStateException(); + } + final boolean includeDefaults = operation.get(INCLUDE_DEFAULTS).asBoolean(true); + // Add a step to transform the result of a READ_RESOURCE. + // Do this first, Stage.IMMEDIATE + final ModelNode readResourceResult = new ModelNode(); + context.addStep(new OperationStepHandler() { + @Override + public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { + // This mimics what TransformationUtils + + Resource root = InternalPackageProtectedAccess.modelToResource( + context.getRootResourceRegistration(), readResourceResult.get(RESULT), includeUndefined); + context.getResult().set(Resource.Tools.readModel(root)); + } + }, OperationContext.Stage.MODEL, true); + + // Now add a step to do the READ_RESOURCE, also IMMEDIATE. This will execute *before* the one ^^^ + final ModelNode op = new ModelNode(); + op.get(OP).set(READ_RESOURCE_OPERATION); + op.get(OP_ADDR).set(PathAddress.EMPTY_ADDRESS.toModelNode()); + op.get(RECURSIVE).set(true); + op.get(INCLUDE_DEFAULTS).set(includeDefaults); + context.addStep(readResourceResult, op, ReadResourceHandler.INSTANCE, OperationContext.Stage.MODEL, true); + } + } + ); + } + }; + } + } + + +} diff --git a/subsystem-test/tests/src/test/java/org/jboss/as/subsystem/test/transformationutils/subsystem/TransformationUtilsExtension.java b/subsystem-test/tests/src/test/java/org/jboss/as/subsystem/test/transformationutils/subsystem/TransformationUtilsExtension.java new file mode 100644 index 00000000000..db30d70af7a --- /dev/null +++ b/subsystem-test/tests/src/test/java/org/jboss/as/subsystem/test/transformationutils/subsystem/TransformationUtilsExtension.java @@ -0,0 +1,173 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + * + */ + +package org.jboss.as.subsystem.test.transformationutils.subsystem; + +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; + +import org.jboss.as.controller.AttributeDefinition; +import org.jboss.as.controller.Extension; +import org.jboss.as.controller.ExtensionContext; +import org.jboss.as.controller.ModelOnlyAddStepHandler; +import org.jboss.as.controller.ModelOnlyRemoveStepHandler; +import org.jboss.as.controller.ModelOnlyWriteAttributeHandler; +import org.jboss.as.controller.ModelVersion; +import org.jboss.as.controller.PathAddress; +import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.ResourceDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.as.controller.SimpleResourceDefinition; +import org.jboss.as.controller.SubsystemRegistration; +import org.jboss.as.controller.descriptions.NonResolvingResourceDescriptionResolver; +import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler; +import org.jboss.as.controller.operations.common.Util; +import org.jboss.as.controller.parsing.ExtensionParsingContext; +import org.jboss.as.controller.parsing.ParseUtils; +import org.jboss.as.controller.persistence.SubsystemMarshallingContext; +import org.jboss.as.controller.registry.ManagementResourceRegistration; +import org.jboss.as.subsystem.test.simple.subsystem.SimpleSubsystemExtension; +import org.jboss.dmr.ModelNode; +import org.jboss.dmr.ModelType; +import org.jboss.staxmapper.XMLElementReader; +import org.jboss.staxmapper.XMLElementWriter; +import org.jboss.staxmapper.XMLExtendedStreamReader; +import org.jboss.staxmapper.XMLExtendedStreamWriter; + +public class TransformationUtilsExtension implements Extension { + /** The name space used for the {@code substystem} element */ + public static final String NAMESPACE = "urn:mycompany:test-subsystem:1.0"; + + /** The name of our subsystem within the model. */ + public static final String SUBSYSTEM_NAME = "test-subsystem"; + + /** The parser used for parsing our subsystem */ + private final SubsystemParser parser = new SubsystemParser(); + + private final ResourceDefinition subsystemResourceDefinition; + + private TransformationUtilsExtension(ResourceDefinition subsystemResourceDefinition) { + this.subsystemResourceDefinition = subsystemResourceDefinition; + } + + @Override + public void initializeParsers(ExtensionParsingContext context) { + context.setSubsystemXmlMapping(SUBSYSTEM_NAME, NAMESPACE, parser); + } + + + @Override + public void initialize(ExtensionContext context) { + final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, ModelVersion.create(1)); + final ManagementResourceRegistration registration = subsystem.registerSubsystemModel(subsystemResourceDefinition); + //We always need to add a 'describe' operation + registration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE); + subsystem.registerXMLElementWriter(parser); + } + + public static Builder createBuilder() { + return new Builder(true, PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME)); + } + + /** + * The subsystem parser, which uses stax to read and write to and from xml + */ + private static class SubsystemParser implements XMLStreamConstants, XMLElementReader>, XMLElementWriter { + + /** {@inheritDoc} */ + @Override + public void writeContent(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException { + context.startSubsystemElement(SimpleSubsystemExtension.NAMESPACE, false); + writer.writeEndElement(); + } + + /** {@inheritDoc} */ + @Override + public void readElement(XMLExtendedStreamReader reader, List list) throws XMLStreamException { + // Require no content + ParseUtils.requireNoContent(reader); + list.add(Util.createAddOperation(PathAddress.pathAddress(PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME)))); + } + } + + public static class Builder { + private final PathElement pathElement; + private Set attributes = new HashSet<>(); + private Set children = new HashSet<>(); + + private final boolean top; + + + private Builder(boolean top, PathElement pathElement) { + this.top = top; + this.pathElement = pathElement; + } + + public Builder addAttribute(String name) { + AttributeDefinition attr = SimpleAttributeDefinitionBuilder.create(name, ModelType.STRING) + .setRequired(false) + .build(); + attributes.add(attr); + return this; + } + + public Builder createChildBuilder(PathElement pathElement) { + Builder childBuilder = new Builder(false, pathElement); + children.add(childBuilder); + return childBuilder; + } + + public TransformationUtilsExtension build() { + if (!top) { + throw new IllegalArgumentException(); + } + return new TransformationUtilsExtension(buildResourceDefinition()); + } + + ResourceDefinition buildResourceDefinition() { + Set children = new HashSet<>(); + for (Builder child : this.children) { + children.add(child.buildResourceDefinition()); + } + return new TestResourceDefinition(pathElement, attributes, children); + } + + } + + private static class TestResourceDefinition extends SimpleResourceDefinition { + private final Set attributes; + private final Set children; + + public TestResourceDefinition(PathElement pathElement, Set attributes, Set children) { + super(pathElement, + NonResolvingResourceDescriptionResolver.INSTANCE, + ModelOnlyAddStepHandler.INSTANCE, + ModelOnlyRemoveStepHandler.INSTANCE); + this.attributes = attributes; + this.children = children; + } + + @Override + public void registerAttributes(ManagementResourceRegistration resourceRegistration) { + for (AttributeDefinition attr : attributes) { + resourceRegistration.registerReadWriteAttribute(attr, null, ModelOnlyWriteAttributeHandler.INSTANCE); + } + } + + @Override + public void registerChildren(ManagementResourceRegistration resourceRegistration) { + for (ResourceDefinition child : children) { + resourceRegistration.registerSubModel(child); + } + } + } +}