diff --git a/controller/src/main/java/org/jboss/as/controller/ModelControllerImpl.java b/controller/src/main/java/org/jboss/as/controller/ModelControllerImpl.java index c9f2e5f4557..837c43dfdba 100644 --- a/controller/src/main/java/org/jboss/as/controller/ModelControllerImpl.java +++ b/controller/src/main/java/org/jboss/as/controller/ModelControllerImpl.java @@ -533,8 +533,7 @@ boolean boot(final List bootList, final OperationMessageHandler handl // stop break; } else { - if(parsedOp.handler instanceof ParallelBootOperationStepHandler && - ((ParallelBootOperationStepHandler)parsedOp.handler).getParsedBootOp().getChildOperations().size() != parsedOp.getChildOperations().size()) { + if(parsedOp.isBootHandlerUpdateNeeded()) { ParallelBootOperationStepHandler updatedHandler = new ParallelBootOperationStepHandler(executorService, managementModel.get().getRootResourceRegistration(), processState, this, operationID, extraValidationStepHandler); for(ModelNode childOp : parsedOp.getChildOperations()) { updatedHandler.addSubsystemOperation(new ParsedBootOp(childOp)); diff --git a/controller/src/main/java/org/jboss/as/controller/ParsedBootOp.java b/controller/src/main/java/org/jboss/as/controller/ParsedBootOp.java index 53d0c4c8407..06587d04a2a 100644 --- a/controller/src/main/java/org/jboss/as/controller/ParsedBootOp.java +++ b/controller/src/main/java/org/jboss/as/controller/ParsedBootOp.java @@ -30,6 +30,7 @@ public class ParsedBootOp { public final OperationStepHandler handler; public final ModelNode response; private List childOperations; + private boolean bootHandlerUpdateNeeded = false; ParsedBootOp(final ModelNode operation) { this(operation, null, new ModelNode()); @@ -84,6 +85,21 @@ public PathAddress getAddress() { return address; } + public boolean isBootHandlerUpdateNeeded() { + return this.handler instanceof ParallelBootOperationStepHandler + && ( + bootHandlerUpdateNeeded + || ((ParallelBootOperationStepHandler) this.handler).getParsedBootOp().getChildOperations().size() != getChildOperations().size()); + } + + /** + * Setting this will force the ParallelBootOperationStepHandler handdler to be updated on boot. + */ + public void bootHandlerUpdateNeeded() { + this.bootHandlerUpdateNeeded = true; + } + + @Override public String toString() { return "ParsedBootOp{" + "operation=" + operation + ", operationName=" + operationName + ", address=" + address + ", handler=" + handler + ", response=" + response + ", childOperations=" + childOperations + '}'; diff --git a/controller/src/main/java/org/jboss/as/controller/logging/ControllerLogger.java b/controller/src/main/java/org/jboss/as/controller/logging/ControllerLogger.java index 49565759173..3738d8b45dc 100644 --- a/controller/src/main/java/org/jboss/as/controller/logging/ControllerLogger.java +++ b/controller/src/main/java/org/jboss/as/controller/logging/ControllerLogger.java @@ -3805,4 +3805,8 @@ OperationFailedRuntimeException capabilityAlreadyRegisteredInContext(String capa @Message(id = 516, value = "Parameter %s specifies an invalid module name: %s") OperationFailedException invalidModuleNameParameter(String parameterName, String moduleName); + + @LogMessage(level = WARN) + @Message(id = 517, value = "There are multiple Parallel Boot Operations.") + void multipleParallelBootOperation(); } diff --git a/controller/src/main/java/org/jboss/as/controller/persistence/yaml/YamlConfigurationExtension.java b/controller/src/main/java/org/jboss/as/controller/persistence/yaml/YamlConfigurationExtension.java index b0b90c4764d..df8233713e0 100644 --- a/controller/src/main/java/org/jboss/as/controller/persistence/yaml/YamlConfigurationExtension.java +++ b/controller/src/main/java/org/jboss/as/controller/persistence/yaml/YamlConfigurationExtension.java @@ -15,6 +15,7 @@ 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.REMOVE; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.UNDEFINE_ATTRIBUTE_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.URL; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE; @@ -168,23 +169,22 @@ public void processOperations(ImmutableManagementResourceRegistration rootRegist xmlOperations.put(op.getAddress(), op); } else { if (op.handler instanceof ParallelBootOperationStepHandler) { - /* We need to createa new ParsedBootOp so that the handler number of childOperations is different from the number of childOperation of the handler and thus the handler will be properly updated. - * @see ModelCOntrollerImpl.boot(final List bootList, final OperationMessageHandler handler, final OperationTransactionControl control, + /* We need to force the ParallelBootOperationStepHandler to be updated during the boot ( around line 536) + * @see ModelControllerImpl.boot(final List bootList, final OperationMessageHandler handler, final OperationTransactionControl control, * final boolean rollbackOnRuntimeFailure, MutableRootResourceRegistrationProvider parallelBootRootResourceRegistrationProvider, * final boolean skipModelValidation, final boolean partialModel, final ConfigurationExtension configExtension) */ - parallelBootOp = new ParsedBootOp(op, op.handler); - for (ModelNode childOp : childOperations) { - ParsedBootOp subOp = new ParsedBootOp(childOp, null); - xmlOperations.put(subOp.getAddress(), subOp); - parallelBootOp.addChildOperation(subOp); - } - } else { - for (ModelNode childOp : childOperations) { - ParsedBootOp subOp = new ParsedBootOp(childOp, null); - xmlOperations.put(subOp.getAddress(), subOp); + if (parallelBootOp == null) { + parallelBootOp = op; + parallelBootOp.bootHandlerUpdateNeeded(); + } else { + MGMT_OP_LOGGER.multipleParallelBootOperation(); } } + for (ModelNode childOp : childOperations) { + ParsedBootOp subOp = new ParsedBootOp(childOp, null); + xmlOperations.put(subOp.getAddress(), subOp); + } } } for (Map config : configs) { @@ -195,7 +195,7 @@ public void processOperations(ImmutableManagementResourceRegistration rootRegist } List reorderedList = new ArrayList<>(postExtensionOps.size()); for (ParsedBootOp op : postExtensionOps) { - if (parallelBootOp != null && op.getAddress().size() > 0 && "subsystem".equals(op.getAddress().getElement(0).getKey())) { + if (parallelBootOp != null && isSubsystemOperation(op)) { //The new operations created from the YAML are added to the parallel boot operation enclosing all the subsystem operations parallelBootOp.addChildOperation(op); } else if (op.handler instanceof ParallelBootOperationStepHandler) { @@ -212,6 +212,9 @@ public void processOperations(ImmutableManagementResourceRegistration rootRegist needReload = true; } + private boolean isSubsystemOperation(ParsedBootOp op) { + return op.getAddress().size() > 0 && SUBSYSTEM.equals(op.getAddress().getElement(0).getKey()); + } @SuppressWarnings("unchecked") private void processResource(PathAddress parentAddress, Map yaml, ImmutableManagementResourceRegistration rootRegistration, ImmutableManagementResourceRegistration resourceRegistration, Map xmlOperations, List postExtensionOps, boolean placeHolder) { for (String name : yaml.keySet()) { diff --git a/controller/src/test/java/org/jboss/as/controller/YamlConfigurationParallelBootTest.java b/controller/src/test/java/org/jboss/as/controller/YamlConfigurationParallelBootTest.java new file mode 100644 index 00000000000..d66a788cd05 --- /dev/null +++ b/controller/src/test/java/org/jboss/as/controller/YamlConfigurationParallelBootTest.java @@ -0,0 +1,418 @@ +/* + * Copyright 2024 JBoss by Red Hat. + * + * Licensed 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. + */ +package org.jboss.as.controller; + +import static org.jboss.as.controller.PathAddress.pathAddress; +import static org.jboss.as.controller.PathElement.pathElement; +import static org.jboss.as.controller.SimpleAttributeDefinitionBuilder.create; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION; +import static org.jboss.dmr.ModelType.STRING; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executors; +import org.jboss.as.controller.client.helpers.Operations; +import org.jboss.as.controller.descriptions.NonResolvingResourceDescriptionResolver; +import org.jboss.as.controller.descriptions.StandardResourceDescriptionResolver; +import org.jboss.as.controller.operations.global.GlobalOperationHandlers; +import org.jboss.as.controller.operations.validation.StringLengthValidator; +import org.jboss.as.controller.persistence.ConfigurationExtension; +import org.jboss.as.controller.persistence.ConfigurationExtensionFactory; +import org.jboss.as.controller.persistence.yaml.YamlConfigurationExtension; +import org.jboss.as.controller.persistence.yaml.YamlConfigurationExtensionTest; +import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration; +import org.jboss.as.controller.registry.ManagementResourceRegistration; +import org.jboss.dmr.ModelNode; +import org.jboss.dmr.ModelType; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + * @author Emmanuel Hugonnet (c) 2024 Red Hat, Inc. + */ +public class YamlConfigurationParallelBootTest { + + private static final String MY_RESOURCE = "my-resource"; + private static ImmutableManagementResourceRegistration rootRegistration; + private static final PathElement SUBSYSTEM_SYSTEM_PROPERTIES_PATH = pathElement(SUBSYSTEM, "system-properties"); + private static final PathElement SUBSYSTEM_PROPERTIES_PATH = pathElement(SUBSYSTEM, "properties"); + private static final PathElement SUBSYSTEM_CLASSPATH_PATH = pathElement(SUBSYSTEM, "classpaths"); + private static final PathElement SUBSYSTEM_BASIC_PATH = pathElement(SUBSYSTEM, "basics"); + + @BeforeClass + public static void setUp() { + StandardResourceDescriptionResolver descriptionResolver = new StandardResourceDescriptionResolver(MY_RESOURCE, YamlConfigurationExtensionTest.class.getName(), Thread.currentThread().getContextClassLoader()); + SimpleResourceDefinition rootResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(ResourceRegistration.root(), descriptionResolver)); + AttributeDefinition valueAtt = SimpleAttributeDefinitionBuilder.create(VALUE, STRING, true) + .setAllowExpression(true) + .setValidator(new StringLengthValidator(0, true, true)) + .build(); + SimpleResourceDefinition systemPropertyResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(PathElement.pathElement("system-property"), descriptionResolver) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + })) { + @Override + public void registerAttributes(ManagementResourceRegistration resourceRegistration) { + super.registerAttributes(resourceRegistration); + resourceRegistration.registerReadWriteAttribute(valueAtt, null, ModelOnlyWriteAttributeHandler.INSTANCE); + } + + @Override + public void registerOperations(ManagementResourceRegistration resourceRegistration) { + super.registerOperations(resourceRegistration); + resourceRegistration.registerOperationHandler(SimpleOperationDefinitionBuilder.of(WRITE_ATTRIBUTE_OPERATION, descriptionResolver).build(), ModelOnlyWriteAttributeHandler.INSTANCE); + } + }; + SimpleResourceDefinition subsystemSystemPropertyResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(SUBSYSTEM_SYSTEM_PROPERTIES_PATH, descriptionResolver) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + })) { + @Override + public void registerChildren(ManagementResourceRegistration resourceRegistration) { + super.registerChildren(resourceRegistration); + resourceRegistration.registerSubModel(systemPropertyResource); + } + }; + SimpleResourceDefinition extensionResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(pathElement("extension"), descriptionResolver) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + })); + SimpleResourceDefinition subsystemExtensionResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(pathElement(SUBSYSTEM, "extensions"), descriptionResolver) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + })) { + @Override + public void registerChildren(ManagementResourceRegistration resourceRegistration) { + super.registerChildren(resourceRegistration); + resourceRegistration.registerSubModel(extensionResource); + } + }; + AttributeDefinition listAtt = PrimitiveListAttributeDefinition.Builder.of("strings", STRING).build(); + SimpleResourceDefinition listResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(pathElement("list"), descriptionResolver) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + })) { + @Override + public void registerAttributes(ManagementResourceRegistration resourceRegistration) { + super.registerAttributes(resourceRegistration); + resourceRegistration.registerReadWriteAttribute(listAtt, null, ModelOnlyWriteAttributeHandler.INSTANCE); + } + }; + SimpleResourceDefinition subsystemListResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(pathElement(SUBSYSTEM, "lists"), descriptionResolver) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + })) { + @Override + public void registerChildren(ManagementResourceRegistration resourceRegistration) { + super.registerChildren(resourceRegistration); + resourceRegistration.registerSubModel(listResource); + } + }; + SimpleResourceDefinition basicResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(pathElement("basic"), descriptionResolver) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + })) { + @Override + public void registerAttributes(ManagementResourceRegistration resourceRegistration) { + super.registerAttributes(resourceRegistration); + resourceRegistration.registerReadWriteAttribute(valueAtt, null, ModelOnlyWriteAttributeHandler.INSTANCE); + } + }; + SimpleResourceDefinition subsystemBasicResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(SUBSYSTEM_BASIC_PATH, descriptionResolver) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + })) { + @Override + public void registerChildren(ManagementResourceRegistration resourceRegistration) { + super.registerChildren(resourceRegistration); + resourceRegistration.registerSubModel(basicResource); + } + }; + PropertiesAttributeDefinition propertiesAtt = new PropertiesAttributeDefinition.Builder("props", false).build(); + SimpleResourceDefinition propertyResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(pathElement("property"), descriptionResolver) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + })) { + @Override + public void registerAttributes(ManagementResourceRegistration resourceRegistration) { + super.registerAttributes(resourceRegistration); + resourceRegistration.registerReadWriteAttribute(propertiesAtt, null, ModelOnlyWriteAttributeHandler.INSTANCE); + } + + @Override + public void registerOperations(ManagementResourceRegistration resourceRegistration) { + super.registerOperations(resourceRegistration); + resourceRegistration.registerOperationHandler(SimpleOperationDefinitionBuilder.of(WRITE_ATTRIBUTE_OPERATION, descriptionResolver).build(), ModelOnlyWriteAttributeHandler.INSTANCE); + } + }; + SimpleResourceDefinition subsystemPropertyResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(SUBSYSTEM_PROPERTIES_PATH, descriptionResolver) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + })) { + @Override + public void registerChildren(ManagementResourceRegistration resourceRegistration) { + super.registerChildren(resourceRegistration); + resourceRegistration.registerSubModel(propertyResource); + } + }; + ObjectTypeAttributeDefinition CLASS = ObjectTypeAttributeDefinition.Builder.of("class", + create("class-name", ModelType.STRING, false) + .setAllowExpression(false) + .build(), + create("module", ModelType.STRING, false) + .setAllowExpression(false) + .build()) + .build(); + ObjectMapAttributeDefinition COMPLEX_MAP = ObjectMapAttributeDefinition.create("complex-map", CLASS) + .setRequired(false) + .build(); + SimpleResourceDefinition classpathResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(PathElement.pathElement("classpath"), descriptionResolver) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + })) { + @Override + public void registerAttributes(ManagementResourceRegistration resourceRegistration) { + super.registerAttributes(resourceRegistration); + resourceRegistration.registerReadWriteAttribute(COMPLEX_MAP, null, ModelOnlyWriteAttributeHandler.INSTANCE); + } + }; + SimpleResourceDefinition subsystemClasspathResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(SUBSYSTEM_CLASSPATH_PATH, descriptionResolver) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + })) { + @Override + public void registerChildren(ManagementResourceRegistration resourceRegistration) { + super.registerChildren(resourceRegistration); + resourceRegistration.registerSubModel(classpathResource); + } + }; + SimpleResourceDefinition childResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(PathElement.pathElement("child"), descriptionResolver) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + })) { + @Override + public void registerAttributes(ManagementResourceRegistration resourceRegistration) { + super.registerAttributes(resourceRegistration); + resourceRegistration.registerReadWriteAttribute(valueAtt, null, ModelOnlyWriteAttributeHandler.INSTANCE); + } + }; + SimpleResourceDefinition parentResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(PathElement.pathElement("parent"), descriptionResolver) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + })) { + @Override + public void registerAttributes(ManagementResourceRegistration resourceRegistration) { + super.registerAttributes(resourceRegistration); + resourceRegistration.registerReadWriteAttribute(valueAtt, null, ModelOnlyWriteAttributeHandler.INSTANCE); + } + + @Override + public void registerChildren(ManagementResourceRegistration resourceRegistration) { + super.registerChildren(resourceRegistration); + resourceRegistration.registerSubModel(childResource); + } + }; + SimpleResourceDefinition subsystemParentResource = new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters(PathElement.pathElement(SUBSYSTEM, "parents"), descriptionResolver) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + })) { + @Override + public void registerChildren(ManagementResourceRegistration resourceRegistration) { + super.registerChildren(resourceRegistration); + resourceRegistration.registerSubModel(parentResource); + } + }; + ManagementResourceRegistration root = ManagementResourceRegistration.Factory.forProcessType(ProcessType.EMBEDDED_SERVER).createRegistration(rootResource); + GlobalOperationHandlers.registerGlobalOperations(root, ProcessType.EMBEDDED_SERVER); + root.registerSubModel(subsystemSystemPropertyResource); + root.registerSubModel(subsystemExtensionResource); + root.registerSubModel(subsystemListResource); + root.registerSubModel(subsystemBasicResource); + root.registerSubModel(subsystemPropertyResource); + root.registerSubModel(subsystemClasspathResource); + root.registerSubModel(subsystemParentResource); + + AttributeDefinition connectorType = SimpleAttributeDefinitionBuilder.create("type", STRING, true) + .setAllowExpression(true) + .setValidator(new StringLengthValidator(0, true, true)) + .build(); + ManagementResourceRegistration connectorRegistration = root.registerSubModel(new SimpleResourceDefinition(new SimpleResourceDefinition.Parameters( + PathElement.pathElement("connector"), NonResolvingResourceDescriptionResolver.INSTANCE) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + }) + .setRemoveHandler(new AbstractRemoveStepHandler() { + })) + ); + connectorRegistration.registerReadWriteAttribute(connectorType, null, ModelOnlyWriteAttributeHandler.INSTANCE); + ManagementResourceRegistration acceptorRegistration = connectorRegistration.registerSubModel( + new SimpleResourceDefinition( + new SimpleResourceDefinition.Parameters(PathElement.pathElement("acceptor"), NonResolvingResourceDescriptionResolver.INSTANCE) + .setAddHandler(new AbstractBoottimeAddStepHandler() { + }) + .setRemoveHandler(new AbstractRemoveStepHandler() { + })) + ); + acceptorRegistration.registerReadWriteAttribute(connectorType, null, ModelOnlyWriteAttributeHandler.INSTANCE); + rootRegistration = root; + } + + /** + * Verify adding new resources. + * + * @throws java.net.URISyntaxException + */ + @Test + public void testAddingNewResources() throws URISyntaxException { + ParallelBootOperationStepHandler handler = new ParallelBootOperationStepHandler(Executors.newSingleThreadExecutor(), rootRegistration, new ControlledProcessState(true), null, 0, null); + handler.addSubsystemOperation(new ParsedBootOp(Operations.createAddOperation(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH).toModelNode()), null)); + handler.addSubsystemOperation(new ParsedBootOp(Operations.createAddOperation(pathAddress(SUBSYSTEM_PROPERTIES_PATH).toModelNode()), null)); + handler.addSubsystemOperation(new ParsedBootOp(Operations.createAddOperation(pathAddress(SUBSYSTEM_CLASSPATH_PATH).toModelNode()), null)); + List postExtensionOps = new ArrayList<>(); + postExtensionOps.add(handler.getParsedBootOp()); + ConfigurationExtension instance = ConfigurationExtensionFactory.createConfigurationExtension(Paths.get(this.getClass().getResource("simple_subsystem.yml").toURI())); + instance.processOperations(rootRegistration, postExtensionOps); + assertFalse(postExtensionOps.isEmpty()); + assertEquals(1, postExtensionOps.size()); + List childOperations = postExtensionOps.get(0).getChildOperations(); + assertEquals(10, childOperations.size()); + postExtensionOps = new ArrayList<>(); + for (ModelNode op : childOperations) { + postExtensionOps.add(new ParsedBootOp(op, null)); + } + assertEquals(ADD, postExtensionOps.get(0).operationName); + assertEquals(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH), postExtensionOps.get(0).address); + assertEquals(ADD, postExtensionOps.get(1).operationName); + assertEquals(pathAddress(SUBSYSTEM_PROPERTIES_PATH), postExtensionOps.get(1).address); + assertEquals(ADD, postExtensionOps.get(2).operationName); + assertEquals(pathAddress(SUBSYSTEM_CLASSPATH_PATH), postExtensionOps.get(2).address); + assertEquals(ADD, postExtensionOps.get(3).operationName); + assertEquals(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH, pathElement("system-property", "aaa")), postExtensionOps.get(3).address); + assertTrue(postExtensionOps.get(3).operation.hasDefined("value")); + assertEquals(ADD, postExtensionOps.get(4).operationName); + assertEquals(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH, pathElement("system-property", "bbb")), postExtensionOps.get(4).address); + assertTrue(postExtensionOps.get(4).operation.hasDefined("value")); + assertEquals(ADD, postExtensionOps.get(5).operationName); + assertEquals(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH, pathElement("system-property", "ccc")), postExtensionOps.get(5).address); + assertTrue(postExtensionOps.get(5).operation.hasDefined("value")); + assertEquals("test", postExtensionOps.get(5).operation.get("value").asString()); + assertEquals(ADD, postExtensionOps.get(6).operationName); + assertEquals(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH, pathElement("system-property", "value")), postExtensionOps.get(6).address); + assertTrue(postExtensionOps.get(6).operation.hasDefined("value")); + assertEquals("test", postExtensionOps.get(6).operation.get("value").asString()); + assertEquals(ADD, postExtensionOps.get(7).operationName); + assertEquals(pathAddress(SUBSYSTEM_BASIC_PATH, pathElement("basic", "test")), postExtensionOps.get(7).address); + assertFalse(postExtensionOps.get(7).operation.hasDefined("value")); + assertEquals(ADD, postExtensionOps.get(8).operationName); + assertEquals(pathAddress(SUBSYSTEM_PROPERTIES_PATH, pathElement("property", "test-property")), postExtensionOps.get(8).address); + assertTrue(postExtensionOps.get(8).operation.hasDefined("props")); + ModelNode properties = postExtensionOps.get(8).operation.get("props").asObject(); + assertEquals("0", properties.get("ip_ttl").asString()); + assertEquals("5", properties.get("tcp_ttl").asString()); + assertEquals(ADD, postExtensionOps.get(9).operationName); + assertEquals(pathAddress(SUBSYSTEM_CLASSPATH_PATH, pathElement("classpath", "runtime")), postExtensionOps.get(9).address); + assertTrue(postExtensionOps.get(9).operation.hasDefined("complex-map")); + ModelNode objectMap = postExtensionOps.get(9).operation.get("complex-map").asObject(); + assertEquals("org.widlfly.test.Main", objectMap.get("main-class").asObject().get("class-name").asString()); + assertEquals("org.wildfly.test:main", objectMap.get("main-class").asObject().get("module").asString()); + assertEquals("org.widlfly.test.MyTest", objectMap.get("test-class").asObject().get("class-name").asString()); + assertEquals("org.wildfly.test:main", objectMap.get("test-class").asObject().get("module").asString()); + + } + + /** + * Verify setting attributes. + * + * @throws java.net.URISyntaxException + */ + @Test + public void testWriteAttribute() throws URISyntaxException { + ParallelBootOperationStepHandler handler = new ParallelBootOperationStepHandler(Executors.newSingleThreadExecutor(), rootRegistration, new ControlledProcessState(true), null, 0, null); + handler.addSubsystemOperation(new ParsedBootOp(Operations.createAddOperation(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH).toModelNode()), null)); + handler.addSubsystemOperation(new ParsedBootOp(Operations.createAddOperation(pathAddress(SUBSYSTEM_PROPERTIES_PATH).toModelNode()), null)); + handler.addSubsystemOperation(new ParsedBootOp(Operations.createAddOperation(pathAddress(SUBSYSTEM_CLASSPATH_PATH).toModelNode()), null)); + handler.addSubsystemOperation(new ParsedBootOp(Operations.createAddOperation(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH, pathElement("system-property", "aaa")).toModelNode()), null)); + handler.addSubsystemOperation(new ParsedBootOp(Operations.createAddOperation(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH, pathElement("system-property", "bbb")).toModelNode()), null)); + handler.addSubsystemOperation(new ParsedBootOp(Operations.createAddOperation(pathAddress(SUBSYSTEM_PROPERTIES_PATH, pathElement("property", "test-property")).toModelNode()), null)); + handler.addSubsystemOperation(new ParsedBootOp(Operations.createAddOperation(pathAddress(SUBSYSTEM_CLASSPATH_PATH, pathElement("classpath", "runtime")).toModelNode()), null)); + List postExtensionOps = new ArrayList<>(); + postExtensionOps.add(handler.getParsedBootOp()); + ConfigurationExtension instance = new YamlConfigurationExtension(); + instance.load(Paths.get(this.getClass().getResource("simple_subsystem.yml").toURI())); + instance.processOperations(rootRegistration, postExtensionOps); + assertFalse(postExtensionOps.isEmpty()); + assertEquals(1, postExtensionOps.size()); + List childOperations = postExtensionOps.get(0).getChildOperations(); + assertEquals(14, childOperations.size()); + postExtensionOps = new ArrayList<>(); + for (ModelNode op : childOperations) { + postExtensionOps.add(new ParsedBootOp(op, null)); + } + assertEquals(ADD, postExtensionOps.get(0).operationName); + assertEquals(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH), postExtensionOps.get(0).address); + assertEquals(ADD, postExtensionOps.get(1).operationName); + assertEquals(pathAddress(SUBSYSTEM_PROPERTIES_PATH), postExtensionOps.get(1).address); + assertEquals(ADD, postExtensionOps.get(2).operationName); + assertEquals(pathAddress(SUBSYSTEM_CLASSPATH_PATH), postExtensionOps.get(2).address); + assertEquals(ADD, postExtensionOps.get(3).operationName); + assertEquals(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH, pathElement("system-property", "aaa")), postExtensionOps.get(3).address); + assertFalse(postExtensionOps.get(3).operation.hasDefined("value")); + assertEquals(ADD, postExtensionOps.get(4).operationName); + assertEquals(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH, pathElement("system-property", "bbb")), postExtensionOps.get(4).address); + assertFalse(postExtensionOps.get(4).operation.hasDefined("value")); + assertEquals(ADD, postExtensionOps.get(5).operationName); + assertEquals(pathAddress(SUBSYSTEM_PROPERTIES_PATH, pathElement("property", "test-property")), postExtensionOps.get(5).address); + assertFalse(postExtensionOps.get(5).operation.hasDefined("value")); + assertEquals(ADD, postExtensionOps.get(6).operationName); + assertEquals(pathAddress(SUBSYSTEM_CLASSPATH_PATH, pathElement("classpath", "runtime")), postExtensionOps.get(6).address); + assertFalse(postExtensionOps.get(6).operation.hasDefined("value")); + assertEquals(WRITE_ATTRIBUTE_OPERATION, postExtensionOps.get(7).operationName); + assertTrue(postExtensionOps.get(7).operation.hasDefined("value")); + assertEquals("foo", postExtensionOps.get(7).operation.get("value").asString()); + assertEquals(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH, pathElement("system-property", "aaa")), postExtensionOps.get(7).address); + assertEquals(WRITE_ATTRIBUTE_OPERATION, postExtensionOps.get(8).operationName); + assertTrue(postExtensionOps.get(8).operation.hasDefined("value")); + assertEquals("bar", postExtensionOps.get(8).operation.get("value").asString()); + assertEquals(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH, pathElement("system-property", "bbb")), postExtensionOps.get(8).address); + assertEquals(WRITE_ATTRIBUTE_OPERATION, postExtensionOps.get(8).operationName); + assertTrue(postExtensionOps.get(8).operation.hasDefined("value")); + assertEquals("bar", postExtensionOps.get(8).operation.get("value").asString()); + assertEquals(ADD, postExtensionOps.get(9).operationName); + assertEquals(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH, pathElement("system-property", "ccc")), postExtensionOps.get(9).address); + assertTrue(postExtensionOps.get(9).operation.hasDefined("value")); + assertEquals("test", postExtensionOps.get(9).operation.get("value").asString()); + assertEquals(ADD, postExtensionOps.get(10).operationName); + assertEquals(pathAddress(SUBSYSTEM_SYSTEM_PROPERTIES_PATH, pathElement("system-property", "value")), postExtensionOps.get(10).address); + assertTrue(postExtensionOps.get(10).operation.hasDefined("value")); + assertEquals("test", postExtensionOps.get(10).operation.get("value").asString()); + assertEquals(ADD, postExtensionOps.get(11).operationName); + assertEquals(pathAddress(SUBSYSTEM_BASIC_PATH, pathElement("basic", "test")), postExtensionOps.get(11).address); + assertFalse(postExtensionOps.get(11).operation.hasDefined("value")); + + assertEquals(WRITE_ATTRIBUTE_OPERATION, postExtensionOps.get(12).operationName); + assertEquals(pathAddress(SUBSYSTEM_PROPERTIES_PATH, pathElement("property", "test-property")), postExtensionOps.get(12).address); + assertTrue(postExtensionOps.get(12).operation.hasDefined("name")); + assertEquals("props", postExtensionOps.get(12).operation.get("name").asString()); + assertTrue(postExtensionOps.get(12).operation.hasDefined("value")); + ModelNode properties = postExtensionOps.get(12).operation.get("value").asObject(); + assertEquals("0", properties.get("ip_ttl").asString()); + assertEquals("5", properties.get("tcp_ttl").asString()); + + assertEquals(WRITE_ATTRIBUTE_OPERATION, postExtensionOps.get(13).operationName); + assertEquals(pathAddress(SUBSYSTEM_CLASSPATH_PATH, pathElement("classpath", "runtime")), postExtensionOps.get(13).address); + assertTrue(postExtensionOps.get(13).operation.hasDefined("value")); + assertEquals("complex-map", postExtensionOps.get(13).operation.get("name").asString()); + assertTrue(postExtensionOps.get(13).operation.hasDefined("value")); + ModelNode objectMap = postExtensionOps.get(13).operation.get("value").asObject(); + assertEquals("org.widlfly.test.Main", objectMap.get("main-class").get("class-name").asString()); + assertEquals("org.wildfly.test:main", objectMap.get("main-class").get("module").asString()); + assertEquals("org.widlfly.test.MyTest", objectMap.get("test-class").get("class-name").asString()); + assertEquals("org.wildfly.test:main", objectMap.get("test-class").get("module").asString()); + } +} diff --git a/controller/src/test/resources/org/jboss/as/controller/simple_subsystem.yml b/controller/src/test/resources/org/jboss/as/controller/simple_subsystem.yml new file mode 100644 index 00000000000..baa86d8a921 --- /dev/null +++ b/controller/src/test/resources/org/jboss/as/controller/simple_subsystem.yml @@ -0,0 +1,39 @@ +wildfly-configuration: + extension: + org.jboss.as.failure: + module: org.jboss.as.failure + subsystem: + system-properties: + system-property: + aaa: + value: foo + bbb: + value: bar + system-property: + ccc: + value: test + value: + value: test + basics: + basic: + test: + properties: + property: + test-property: + props: + ip_ttl: '0' + tcp_ttl: '5' + classpaths: + classpath: + runtime: + complex-map: + main-class: + class-name: org.widlfly.test.Main + module: org.wildfly.test:main + test-class: + class-name: org.widlfly.test.MyTest + module: org.wildfly.test:main +#User defined elements, must be ignored. +org: + foo: + bar: true \ No newline at end of file